3

mnesia は、MySQL や他の RDBMS のように自動インクリメント機能をサポートしていないことに気付きました。mnesia のドキュメントで説明されているカウンターは、あまりよく説明されていません。たとえば、ドキュメント全体でカウンターを操作する関数が1つ見つかりました

mnesia:dirty_update_counter({Tab::atom(),Key::any()}, Val::positive_integer())

したがって、これはしばらくの間、タイプのレコードで機能するため、私を悩ませていました

{タブ名、キー、整数}
これも不明確であり、おそらく Erlang の本や mnesia のドキュメントで説明する例が提供されていないためです.これにより、私は独自のカウンター操作 API を実装する必要がありました.私のレコードに「カウンター」と呼ばれるフィールドを含めてから、カウンターを持つことを目的としたテーブルにレコードを追加する時点で、私は次のようにします:

#recordname{field1 = Val1,...,counter = auto_increment(?THIS_TABLE)}

カウンタ フィールドの位置は重要ではありません。API は次のように実装されます。

%% @doc この関数は、テーブル
%% に新しいレコードを書き込んでいるときに、その結​​果をレコードのカウンタ フィールドに与えることによって呼び出されます。
%% @end
%%
%% @spec auto_increment(TableName::atom()) -> 整数() | 退出(理由)
auto_increment(テーブル名)-> ケースリスト:member(counter,table_info(TableName,attributes)) of false -> erlang:exit({counter,field,not_found,in_table,TableName}); true -> table_info(テーブル名,サイズ) + 1 終わり。
table_info(タブ,項目)-> F = fun({X,Y}) -> mnesia:table_info(X,Y) end, mnesia:activity(transaction,F,[{Tab,Item}],mnesia_frag).

これを説明するために、カウンターフィールドがテーブルの属性でない場合、このコードを実行しようとしているプロセスを強制的に終了させます。 catch ...) of body 、彼らは何が悪いのか簡単にわかります。または、このコード フラグメントがトランザクション内で実行されているかどうかを確認することもできます。mnesia:is_transaction()これが true を返す場合は を呼び出しmnesia:abort/1、false の場合は理由を付けて終了できます。また、mnesia アクティビティ関数で mnesia_frag を使用します。テーブルの断片化プロパティに関係なく動作します。 を使用するmnesia:transaction(Fun)と、この呼び出しは最初のフラグメント (ベース テーブル) にのみアクセスするため、断片化されたテーブルの一貫性が失われます。
ここで、カウンターを含むテーブルからレコードが削除されると、テーブル内の順序を再配置する必要があります。この操作は、テーブル全体での反復が必要なため、コストがかかります。カウンター = 5 のレコードを削除すると、カウンター = 6 のレコードをカウンター = 5 にする必要があるため、パターンに従います。カウンターが削除されたものより大きかったすべてのレコードは、デクリメントする必要があります。したがって、削除されたカウンター値と TableName を渡すことで、
mnesia:foldl/3 or mnesia:foldr/3 , the difference between these two comes in only with ordered table types
これを処理する関数を使用してテーブルを反復処理できます。


auto_decrement(Counter_deleted,TableName)->
    Attrs = table_info(TableName,attributes),
    ケースリスト:メンバー(カウンター、属性)
        false -> erlang:exit({counter,field,not_found,in_table,TableName});
        真 ->
            Counter_position = 位置 (カウンター、属性) + 1、         
            反復子 = fun(Rec,_) when element(Counter_position,Rec) > Counter_deleted ->
                            カウント = 要素(Counter_position,Rec),
                            New_rec = erlang:setelement(Counter_position,Rec,Count - 1),
                            mnesia:write(TableName,New_rec,read),
                            [];
                        (_,_) -> []
                        終わり、
            Find = fun({Fun,Table}) -> mnesia:foldl(Fun, [],Table) end,
            mnesia:activity(transaction,Find,[{Iterator,TableName}],mnesia_frag)
    終わり。


レコードからカウンター フィールドの位置を動的に見つけるのに役立つコードがあることに気付きました。これを行うのに役立つコードを以下に示します。


位置(_,[]) -> -1;
位置(値,リスト)->
    検索 (リスト: メンバー (値、リスト)、値、リスト、1)。

検索 (false,_,_,_) -> -1;
find(true,V,[V|_],N)-> N;
find(真,V,[_|X],N)->
    (V、X、N + 1) を検索します。

find(V,[V|_],N)-> N;
find(V,[_|X],N) -> find(V,X,N + 1).


これは、このモジュールがプログラマーのカウンターを支援するために、プログラマーのレコードを認識してはならないためですelement(N::integer(),Tuple::tuple())。レコードの表現を動的に。

これらの 2 つの関数は私のために働いており、auto_increment が mnesia
に実装されるまでまだ機能しています。

たとえば、qlc (クエリ リスト内包表記) を使用して、動的な制約を持つテーブルをクエリする場合、次のコードを検討してください。


select(Q)->
    F = fun(QH) -> qlc:e(QH) 終わり、
    mnesia:activity(transaction,F,[Q],mnesia_frag).

read_by_custom_validation(Validation_fun,From_table)->
    select(qlc:q([X || X <- mnesia:table(From_table),Validation_fun(X) == true])))。

%% 2 つの関数を適用しています....
find_records_with_counter(From_this,To_that) when 
is_integer(From_this),is_integer(To_that),To_that >​​ From_this -> F = fun(#recordName{counter = N}) when N >= From_this,N =< To_That -> true; (_) -> 偽 終わり、 read_by_custom_validation(F,TableName)。

在庫管理システムでは、これは機能しています...



(stock_project@127.0.0.1)6> ストック:get_items_in_range(1,4)。
[#item{item_id = "D694",name = "セメント",
       time_stamp = {"2010 年 12 月 30 日","午前 11:29:10"},
       min_stock = 500,units = "bags",unit_cost = 20000,
       状態 = 利用可能、last_modified = 未定義、
       カテゴリ = "建材",カウンター = 1},
 #it​​em{item_id = "131B",name = "ネイル",
       time_stamp = {"2010 年 12 月 30 日","午前 11:29:10"},
       min_stock = 20000、units = "kg"、unit_cost = 1000、
       状態 = 利用可能、last_modified = 未定義、
       カテゴリ = "建材",カウンター = 2},
 #it​​em{item_id = "FDD9",name = "鉄板",
       time_stamp = {"2010 年 12 月 30 日","午前 11:29:10"},
       min_stock = 20,units = "バー",unit_cost = 50000,
       状態 = 利用可能、last_modified = 未定義、
       カテゴリ = "建材",カウンター = 3},
 #it​​em{item_id = "09D4",name = "ペイント",
       time_stamp = {"2010 年 12 月 30 日","午前 11:29:10"},
       min_stock = 30000,units = "tins",unit_cost = 5000,
       状態 = 利用可能、last_modified = 未定義、
       カテゴリ = "建材",カウンター = 4}]
(stock_project@127.0.0.1)7>

これは私のために働いています。他にどのようにカウンターを処理すればよいか教えてください。または、その側でそれらをどのように処理するか教えてください.

4

1 に答える 1

4

カウンターはあなたの問題に対する最良の解決策ですか? 高度に分散されたシステムでは、おそらくそれらを持たない方がよいと思います。そのため、それらは除外されています。

  1. テーブルに含まれる要素の数が必要な場合。別のレコード テーブル {counts, TableName, Count} を用意し、ストレージ テーブルの追加および削除トランザクションで dirty_update_counter 操作を使用します。
  2. 特定の順序 (たとえば、データが追加された順序) でデータを取得する場合は、ここに示す OrderedBy ソリューションの使用を検討してください。
  3. 高速な外部キー ルックアップが必要な場合は、キーの種類 (item_id のバイナリ値の方が速い可能性があります) をいじってみて、大幅な改善が得られるかどうかをテストします。

また、テーブル エントリが削除されたときにすべてのレコードのカウンターを更新する必要がある理由がわかりません。MySQL や他の RDBMS がこれを行うとは思いませんでした。

于 2011-05-13T02:56:49.657 に答える