~ubuntu-branches/ubuntu/trusty/erlang/trusty

« back to all changes in this revision

Viewing changes to system/doc/efficiency_guide/tablesDatabases.xml

  • Committer: Bazaar Package Importer
  • Author(s): Clint Byrum
  • Date: 2011-05-05 15:48:43 UTC
  • mfrom: (3.5.13 sid)
  • Revision ID: james.westby@ubuntu.com-20110505154843-0om6ekzg6m7ugj27
Tags: 1:14.b.2-dfsg-3ubuntu1
* Merge from debian unstable.  Remaining changes:
  - Drop libwxgtk2.8-dev build dependency. Wx isn't in main, and not
    supposed to.
  - Drop erlang-wx binary.
  - Drop erlang-wx dependency from -megaco, -common-test, and -reltool, they
    do not really need wx. Also drop it from -debugger; the GUI needs wx,
    but it apparently has CLI bits as well, and is also needed by -megaco,
    so let's keep the package for now.
  - debian/patches/series: Do what I meant, and enable build-options.patch
    instead.
* Additional changes:
  - Drop erlang-wx from -et
* Dropped Changes:
  - patches/pcre-crash.patch: CVE-2008-2371: outer level option with
    alternatives caused crash. (Applied Upstream)
  - fix for ssl certificate verification in newSSL: 
    ssl_cacertfile_fix.patch (Applied Upstream)
  - debian/patches/series: Enable native.patch again, to get stripped beam
    files and reduce the package size again. (build-options is what
    actually accomplished this)
  - Remove build-options.patch on advice from upstream and because it caused
    odd build failures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?xml version="1.0" encoding="latin1" ?>
 
2
<!DOCTYPE chapter SYSTEM "chapter.dtd">
 
3
 
 
4
<chapter>
 
5
  <header>
 
6
    <copyright>
 
7
      <year>2001</year><year>2009</year>
 
8
      <holder>Ericsson AB. All Rights Reserved.</holder>
 
9
    </copyright>
 
10
    <legalnotice>
 
11
      The contents of this file are subject to the Erlang Public License,
 
12
      Version 1.1, (the "License"); you may not use this file except in
 
13
      compliance with the License. You should have received a copy of the
 
14
      Erlang Public License along with this software. If not, it can be
 
15
      retrieved online at http://www.erlang.org/.
 
16
    
 
17
      Software distributed under the License is distributed on an "AS IS"
 
18
      basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 
19
      the License for the specific language governing rights and limitations
 
20
      under the License.
 
21
    
 
22
    </legalnotice>
 
23
 
 
24
    <title>Tables and databases</title>
 
25
    <prepared>Ingela Anderton</prepared>
 
26
    <docno></docno>
 
27
    <date>2001-08-07</date>
 
28
    <rev></rev>
 
29
    <file>tablesDatabases.xml</file>
 
30
  </header>
 
31
 
 
32
  <section>
 
33
    <title>Ets, Dets and Mnesia</title>
 
34
    <p>Every example using Ets has a corresponding example in
 
35
      Mnesia. In general all Ets examples also apply to Dets tables.</p>
 
36
 
 
37
    <section>
 
38
      <title>Select/Match operations</title>
 
39
      <p>Select/Match operations on Ets and Mnesia tables can become
 
40
        very expensive operations. They usually need to scan the complete
 
41
        table. You should try to structure your
 
42
        data so that you minimize the need for select/match
 
43
        operations. However, if you really need a select/match operation,
 
44
        it will still be more efficient than using <c>tab2list</c>.
 
45
        Examples of this and also of ways to avoid select/match will be provided in
 
46
        some of the following sections. The functions
 
47
        <c>ets:select/2</c> and <c>mnesia:select/3</c> should be preferred over
 
48
        <c>ets:match/2</c>,<c>ets:match_object/2</c>, and <c>mnesia:match_object/3</c>.</p>
 
49
      <note>
 
50
        <p>There are exceptions when the complete table is not
 
51
          scanned, for instance if part of the key is bound when searching an
 
52
          <c>ordered_set</c> table, or if it is a Mnesia
 
53
          table and there is a secondary index on the field that is
 
54
          selected/matched. If the key is fully bound there will, of course, be
 
55
          no point in doing a select/match, unless you have a bag table and
 
56
          you are only interested in a sub-set of the elements with
 
57
          the specific key.</p>
 
58
      </note>
 
59
      <p>When creating a record to be used in a select/match operation you
 
60
        want most of the fields to have the value '_'. The easiest and fastest way
 
61
        to do that is as follows:</p>
 
62
      <pre>
 
63
#person{age = 42, _ = '_'}. </pre>
 
64
    </section>
 
65
 
 
66
    <section>
 
67
      <title>Deleting an element</title>
 
68
      <p>The delete operation is considered
 
69
        successful if the element was not present in the table. Hence
 
70
        all attempts to check that the element is present in the
 
71
        Ets/Mnesia table before deletion are unnecessary. Here follows
 
72
        an example for Ets tables.</p>
 
73
      <p><em>DO</em></p>
 
74
      <pre>
 
75
...
 
76
ets:delete(Tab, Key),
 
77
...</pre>
 
78
      <p><em>DO NOT</em></p>
 
79
      <pre>
 
80
...
 
81
case ets:lookup(Tab, Key) of
 
82
    [] ->
 
83
        ok;
 
84
    [_|_] ->
 
85
        ets:delete(Tab, Key)
 
86
end,
 
87
...</pre>
 
88
    </section>
 
89
 
 
90
    <section>
 
91
      <title>Data fetching</title>
 
92
      <p>Do not fetch data that you already have! Consider that you
 
93
        have a module that handles the abstract data type Person. You
 
94
        export the interface function <c>print_person/1</c> that uses the internal functions
 
95
        <c>print_name/1</c>, <c>print_age/1</c>, <c>print_occupation/1</c>.</p>
 
96
      <note>
 
97
        <p>If the functions <c>print_name/1</c> and so on, had been interface
 
98
          functions the matter comes in to a whole new light, as you
 
99
          do not want the user of the interface to know about the
 
100
          internal data representation. </p>
 
101
      </note>
 
102
      <p><em>DO</em></p>
 
103
      <code type="erl">
 
104
%%% Interface function
 
105
print_person(PersonId) ->
 
106
    %% Look up the person in the named table person,
 
107
    case ets:lookup(person, PersonId) of
 
108
        [Person] ->
 
109
            print_name(Person),
 
110
            print_age(Person),
 
111
            print_occupation(Person);
 
112
        [] ->
 
113
            io:format("No person with ID = ~p~n", [PersonID])
 
114
    end.
 
115
 
 
116
%%% Internal functions
 
117
print_name(Person) -> 
 
118
    io:format("No person ~p~n", [Person#person.name]).
 
119
                      
 
120
print_age(Person) -> 
 
121
    io:format("No person ~p~n", [Person#person.age]).
 
122
 
 
123
print_occupation(Person) -> 
 
124
    io:format("No person ~p~n", [Person#person.occupation]).</code>
 
125
      <p><em>DO NOT</em></p>
 
126
      <code type="erl">
 
127
%%% Interface function
 
128
print_person(PersonId) ->
 
129
    %% Look up the person in the named table person,
 
130
    case ets:lookup(person, PersonId) of
 
131
        [Person] ->
 
132
            print_name(PersonID),
 
133
            print_age(PersonID),
 
134
            print_occupation(PersonID);
 
135
        [] ->
 
136
            io:format("No person with ID = ~p~n", [PersonID])
 
137
    end.
 
138
 
 
139
%%% Internal functionss
 
140
print_name(PersonID) -> 
 
141
    [Person] = ets:lookup(person, PersonId),
 
142
    io:format("No person ~p~n", [Person#person.name]).
 
143
 
 
144
print_age(PersonID) -> 
 
145
    [Person] = ets:lookup(person, PersonId),
 
146
    io:format("No person ~p~n", [Person#person.age]).
 
147
 
 
148
print_occupation(PersonID) -> 
 
149
    [Person] = ets:lookup(person, PersonId),
 
150
    io:format("No person ~p~n", [Person#person.occupation]).</code>
 
151
    </section>
 
152
 
 
153
    <section>
 
154
      <title>Non-persistent data storage </title>
 
155
      <p>For non-persistent database storage, prefer Ets tables over
 
156
        Mnesia local_content tables. Even the Mnesia <c>dirty_write</c>
 
157
        operations carry a fixed overhead compared to Ets writes.
 
158
        Mnesia must check if the table is replicated or has indices,
 
159
        this involves at least one Ets lookup for each
 
160
        <c>dirty_write</c>. Thus, Ets writes will always be faster than
 
161
        Mnesia writes.</p>
 
162
    </section>
 
163
 
 
164
    <section>
 
165
      <title>tab2list</title>
 
166
      <p>Assume we have an Ets-table, which uses <c>idno</c> as key,
 
167
        and contains:</p>
 
168
      <pre>
 
169
[#person{idno = 1, name = "Adam",  age = 31, occupation = "mailman"},
 
170
 #person{idno = 2, name = "Bryan", age = 31, occupation = "cashier"},
 
171
 #person{idno = 3, name = "Bryan", age = 35, occupation = "banker"},
 
172
 #person{idno = 4, name = "Carl",  age = 25, occupation = "mailman"}]</pre>
 
173
      <p>If we <em>must</em> return all data stored in the Ets-table we
 
174
        can use <c>ets:tab2list/1</c>.  However, usually we are only
 
175
        interested in a subset of the information in which case
 
176
        <c>ets:tab2list/1</c> is expensive. If we only want to extract
 
177
        one field from each record, e.g., the age of every person, we
 
178
        should use:</p>
 
179
      <p><em>DO</em></p>
 
180
      <pre>
 
181
...
 
182
ets:select(Tab,[{ #person{idno='_', 
 
183
                          name='_', 
 
184
                          age='$1', 
 
185
                          occupation = '_'},
 
186
                [],
 
187
                ['$1']}]),
 
188
...</pre>
 
189
      <p><em>DO NOT</em></p>
 
190
      <pre>
 
191
...
 
192
TabList = ets:tab2list(Tab),
 
193
lists:map(fun(X) -> X#person.age end, TabList),
 
194
...</pre>
 
195
      <p>If we are only interested in the age of all persons named
 
196
        Bryan, we should:</p>
 
197
      <p><em>DO</em></p>
 
198
      <pre>
 
199
...
 
200
ets:select(Tab,[{ #person{idno='_', 
 
201
                          name="Bryan", 
 
202
                          age='$1', 
 
203
                          occupation = '_'},
 
204
                [],
 
205
                ['$1']}]),
 
206
...</pre>
 
207
      <p><em>DO NOT</em></p>
 
208
      <pre>
 
209
...
 
210
TabList = ets:tab2list(Tab),
 
211
lists:foldl(fun(X, Acc) -> case X#person.name of
 
212
                                "Bryan" ->
 
213
                                    [X#person.age|Acc];
 
214
                                 _ ->
 
215
                                     Acc
 
216
                           end
 
217
             end, [], TabList),
 
218
...</pre>
 
219
      <p><em>REALLY DO NOT</em></p>
 
220
      <pre>
 
221
...
 
222
TabList = ets:tab2list(Tab),
 
223
BryanList = lists:filter(fun(X) -> X#person.name == "Bryan" end,
 
224
                         TabList),
 
225
lists:map(fun(X) -> X#person.age end, BryanList),
 
226
...</pre>
 
227
      <p>If we need all information stored in the Ets table about
 
228
        persons named Bryan we should:</p>
 
229
      <p><em>DO</em></p>
 
230
      <pre>
 
231
...
 
232
ets:select(Tab, [{#person{idno='_', 
 
233
                          name="Bryan", 
 
234
                          age='_', 
 
235
                          occupation = '_'}, [], ['$_']}]),
 
236
...</pre>
 
237
      <p><em>DO NOT</em></p>
 
238
      <pre>
 
239
...
 
240
TabList = ets:tab2list(Tab),
 
241
lists:filter(fun(X) -> X#person.name == "Bryan" end, TabList),
 
242
...</pre>
 
243
    </section>
 
244
 
 
245
    <section>
 
246
      <title>Ordered_set tables</title>
 
247
      <p>If the data in the table should be accessed so that the order
 
248
        of the keys in the table is significant, the table type
 
249
        <c>ordered_set</c> could be used instead of the more usual
 
250
        <c>set</c> table type. An <c>ordered_set</c> is always
 
251
        traversed in Erlang term order with regard to the key field
 
252
        so that return values from functions such as <c>select</c>,
 
253
        <c>match_object</c>, and <c>foldl</c> are ordered by the key
 
254
        values. Traversing an <c>ordered_set</c> with the <c>first</c> and
 
255
        <c>next</c> operations also returns the keys ordered.</p>
 
256
      <note>
 
257
        <p>An <c>ordered_set</c> only guarantees that
 
258
          objects are processed in <em>key</em> order. Results from functions as 
 
259
          <c>ets:select/2</c> appear in the <em>key</em> order even if
 
260
          the key is not included in the result.</p>
 
261
      </note>
 
262
    </section>
 
263
  </section>
 
264
 
 
265
  <section>
 
266
    <title>Ets specific</title>
 
267
 
 
268
    <section>
 
269
      <title>Utilizing the keys of the Ets table</title>
 
270
      <p>An Ets table is a single key table (either a hash table or a
 
271
        tree ordered by the key) and should be used as one. In other
 
272
        words, use the key to look up things whenever possible. A
 
273
        lookup by a known key in a set Ets table is constant and for a
 
274
        ordered_set Ets table it is O(logN). A key lookup is always
 
275
        preferable to a call where the whole table has to be
 
276
        scanned. In the examples above, the field <c>idno</c> is the
 
277
        key of the table and all lookups where only the name is known
 
278
        will result in a complete scan of the (possibly large) table
 
279
        for a matching result.</p>
 
280
      <p>A simple solution would be to use the <c>name</c> field as
 
281
        the key instead of the <c>idno</c> field, but that would cause
 
282
        problems if the names were not unique. A more general solution
 
283
        would be create a second table with name as key and idno as
 
284
        data, i.e. to index (invert) the table with regards to the
 
285
        <c>name</c> field. The second table would of course have to be
 
286
        kept consistent with the master table. Mnesia could do this
 
287
        for you, but a home brew index table could be very efficient
 
288
        compared to the overhead involved in using Mnesia.</p>
 
289
      <p>An index table for the table in the previous examples would
 
290
        have to be a bag (as keys would appear more than once) and could
 
291
        have the following contents:</p>
 
292
      <pre>
 
293
 
 
294
[#index_entry{name="Adam", idno=1},
 
295
 #index_entry{name="Bryan", idno=2},
 
296
 #index_entry{name="Bryan", idno=3},
 
297
 #index_entry{name="Carl", idno=4}]</pre>
 
298
      <p>Given this index table a lookup of the <c>age</c> fields for
 
299
        all persons named "Bryan" could be done like this:</p>
 
300
      <pre>
 
301
...
 
302
MatchingIDs = ets:lookup(IndexTable,"Bryan"),
 
303
lists:map(fun(#index_entry{idno = ID}) ->
 
304
                 [#person{age = Age}] = ets:lookup(PersonTable, ID),
 
305
                 Age
 
306
          end,
 
307
          MatchingIDs),
 
308
...</pre>
 
309
      <p>Note that the code above never uses <c>ets:match/2</c> but
 
310
        instead utilizes the <c>ets:lookup/2</c> call. The
 
311
        <c>lists:map/2</c> call is only used to traverse the <c>idno</c>s
 
312
        matching the name "Bryan" in the table; therefore the number of lookups
 
313
        in the master table is minimized.</p>
 
314
      <p>Keeping an index table introduces some overhead when
 
315
        inserting records in the table, therefore the number of operations
 
316
        gained from the table has to be weighted against the number of
 
317
        operations inserting objects in the table. However, note that the gain when
 
318
        the key can be used to lookup elements is significant.</p>
 
319
    </section>
 
320
  </section>
 
321
 
 
322
  <section>
 
323
    <title>Mnesia specific</title>
 
324
 
 
325
    <section>
 
326
      <title>Secondary index</title>
 
327
      <p>If you frequently do a lookup on a field that is not the
 
328
        key of the table, you will lose performance using
 
329
        "mnesia:select/match_object" as this function will traverse the
 
330
        whole table. You may create a secondary index instead and
 
331
        use "mnesia:index_read" to get faster access, however this
 
332
        will require more memory. Example:</p>
 
333
      <pre>
 
334
-record(person, {idno, name, age, occupation}).
 
335
        ...
 
336
{atomic, ok} = 
 
337
mnesia:create_table(person, [{index,[#person.age]},
 
338
                              {attributes,
 
339
                                    record_info(fields, person)}]),
 
340
{atomic, ok} = mnesia:add_table_index(person, age), 
 
341
...
 
342
 
 
343
PersonsAge42 =
 
344
     mnesia:dirty_index_read(person, 42, #person.age),
 
345
...</pre>
 
346
    </section>
 
347
 
 
348
    <section>
 
349
      <title>Transactions </title>
 
350
      <p>Transactions is a way to guarantee that the distributed
 
351
        Mnesia database remains consistent, even when many different
 
352
        processes update it in parallel. However if you have
 
353
        real time requirements it is recommended to use dirty
 
354
        operations instead of transactions. When using the dirty
 
355
        operations you lose the consistency guarantee, this is usually
 
356
        solved by only letting one process update the table. Other
 
357
        processes have to send update requests to that process.</p>
 
358
      <pre>
 
359
...
 
360
% Using transaction
 
361
 
 
362
Fun = fun() ->
 
363
          [mnesia:read({Table, Key}),
 
364
           mnesia:read({Table2, Key2})]
 
365
      end, 
 
366
 
 
367
{atomic, [Result1, Result2]}  = mnesia:transaction(Fun),
 
368
...
 
369
 
 
370
% Same thing using dirty operations
 
371
...
 
372
 
 
373
Result1 = mnesia:dirty_read({Table, Key}),
 
374
Result2 = mnesia:dirty_read({Table2, Key2}),
 
375
...</pre>
 
376
    </section>
 
377
  </section>
 
378
</chapter>
 
379