ダーティな操作
より高速にデータにアクセスしなければならない場合のために、Mnesiaには、トランザクションのオーバーヘッドなしにテーブルを操作する「ダーティな」関数が用意されています。この関数を使用するとパフォーマンスが高まり、Mnesiaは一種のリアルタイムDBMSになります。ただし、トランザクションの原子性と独立性は著しく損なわれます。
サンプルのtest.erlファイルには、以下の関数が記述されています。
op
:レコード#1を削除し、レコード#5を挿入するトランザクション関数reverse_op
:レコード#5を削除し、レコード#1を挿入するトランザクション関数mop
:opとreverse_opを指定の回数実行するdirty_op
、dirty_reverse_op
、dirty_mop
:上記の関数のダーティバージョン
まず、Counter = 10000
にしてtest:mop(Counter)
を実行し、この関数をfirst@charlieノードとsecond@deltaノードで十分な時間動作させます。この関数は両ノードで実行され、一貫性の問題を生じることなく終了しました。同時にthird@nemoでtest:select()
を実行すると、3レコードから成るリストが常に返されます。これは、op
もreverse_op
も1回でmnesia:write
とmnesia:delete
を適用するからです。
同じtestのダーティバージョンであるtest:dirty_mop(Counter)
を両ノード上で同時に実行すると、多数のエラーメッセージが発生します。また、third@nemoでtest:select()
を実行すると、2レコードまたは4レコードから成るリストが返される場合もあります。
クエリーリスト内包表記
Mnesiaにはテーブル内のデータとやり取りするSQL風の言語はありませんが、ErlangのQuery List Comprehensionモジュール(qlc
)を使用することで、Erlangをデータベース言語として使用し、データベースに問い合わせることができます。qlc
モジュールは、リスト内包表記という強力なErlang構文を利用します。
数学的集合を定義したい場合は、集合ビルダーの表記法を用います。これは、集合の要素の特性を記述することによって、集合の要素を限定するものです。今度は、この表記法とErlangのリスト内包表記構文がどれくらい似ているかを見てみましょう。
Aは、10より小さく、かつ2乗しても自身と同じ数になる自然数(N)の集合を表しています。これを数式で表すと次のようになります。
Erlangでは、lists:seq(0,9)
という関数呼び出しで10未満のすべての整数のリストを返すことができるので、以下の構文を使用して上述の要素のリストを取得できます。
1> A = [X || X <- lists:seq(0,9), X == X * X]. [0,1]
2つの式の類似性は顕著です。リスト内包表記は、リストからリストを作成するツールと考えることもできます。以下に一般的な構文を示します。Expression
は、Qualifier1
などの修飾子によって生成およびフィルタされた要素に対して行う一連の演算です。
[Expression || Qualifier1, Qualifier2, ...]
Mnesiaデータベースに問い合わせるためのインターフェースをqlc
がどのように提供しているか理解できたでしょう。ただし、このモジュールを使用する前に、システムのどこにファイル「qlc.hrl」があるかを探し、コードに以下の行を含める必要があります。
-include_lib("/path_to/qlc.hrl" ).
私のtest.erlでは、関数test:select()
はtable1の全レコードからなるリストを返します。以下の行に注意してみると、
Handle = qlc:q([X || X <- mnesia:table(table1)])
関数qlc:q
に渡している引数がリスト内包表記であることに気付くでしょう。このリスト内包表記では、テーブルの内容を返す関数mnesia:table
をジェネレータとしています。関数qlc:q
が返したクエリーハンドルは関数qlc:e
で評価され、この関数によってすべてのテーブルデータが収集され、リストとして返されます。
QueryList = qlc:e(Handle).
先に説明した理由から、これらすべての関数がmnesia:transaction
の内部で動作することに注意してください。関数test:join()
には、テーブルtable1とtable2を結合する簡単な例が含まれています。この部分のクエリーリスト内包表記は次のようになります。
Handle = qlc:q([X#table1.number || X <- mnesia:table(table1), Y <- mnesia:table(table2), X#table1.number > 2000, X#table1.table1_id =:= Y#table2.table2_id ])
実際のところ、レコードとは、命令(先頭文字がマイナス記号の記述)で宣言された名前付きフィールドから成るタプルです。レコード内のフィールドにアクセスするには、ドット構文を使用します。例えば、X#table1.number
は、指定されたフィールドnumber
の値を返します。X
はtable1のレコードとして評価されます。
Mnesiaを探求する
Mnesiaの重要な機能をいくつか紹介してきましたが、もちろんこれですべてではありません。これまで学習してきたことが読者の好奇心を呼び起こし、Mnesiaについてもっと知りたいと思っていただければ幸いです。Mnesiaの可能性を最大限に引き出すため、ぜひ他の機能もより深く知ってもらいたいと思います。
参考資料
- Erlangオンラインマニュアル
- MNESIAデータベース管理システム(プレゼンテーション)