在mnesia DBMS中管理增量计数器?

时间:2011-01-03 16:22:56

标签: erlang auto-increment mnesia

我已经意识到mnesia不像MySQL或其他RDBMS那样支持自动增量功能。在mnesia文档中谈到的计数器并没有得到很好的解释。例如,我在整个文档中找到了一个操作计数器的函数

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

所以,这已经打扰了我一段时间,因为它适用于类型记录

{TabName, Key, Integer}
这也不清楚,可能因为没有erlang书或mnesia文档提供了解释它的例子。这迫使我实现自己的反操纵API。因为我想能够访问和使用计数器管理我的记录,我必须在我的记录中包含一个名为“计数器”的字段,然后在表格中添加计数器以便有计数器,我这样做:

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

计数器字段的位置无关紧要。 API的实现方式如下:

%% @doc this function is called whenever u are writing a new record in the table
%% by giving its result to the counter field in your record.
%% @end
%%
%% @spec auto_increment(TableName::atom()) -> integer() | exit(Reason)
auto_increment(TableName)-> case lists:member(counter,table_info(TableName,attributes)) of false -> erlang:exit({counter,field,not_found,in_table,TableName}); true -> table_info(TableName,size) + 1 end.
table_info(Tab,Item)-> F = fun({X,Y}) -> mnesia:table_info(X,Y) end, mnesia:activity(transaction,F,[{Tab,Item}],mnesia_frag).

为了解释这一点,如果计数器字段不是表的属性,我强行让试图执行此代码的进程有理由退出,所以如果程序员在try ... catch中调用它,或者一个案例(抓住...)的身体,他们很容易看出什么是错的。或者,我可以通过使用mnesia:is_transaction()询问此代码片段是否在事务中执行,如果返回true,则调用mnesia:abort/1,如果为false,我可以直接退出原因。此外,我使用mnesia活动函数中的mnesia_frag,因为无论表的碎片属性如何,此实现都将起作用。如果我使用mnesia:transaction(Fun),碎片表将变得不一致,因为此调用只会访问初始片段(基表)。
现在,当从带有计数器的表中删除记录时,我们需要重新排列表中的顺序。这个操作很昂贵,因为它需要遍历整个表。因为如果他们删除了counter = 5的记录,那么counter = 6的记录必须变为counter = 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),
    case lists:member(counter,Attrs) of
        false -> erlang:exit({counter,field,not_found,in_table,TableName});
        true -> 
            Counter_position = position(counter,Attrs) + 1,         
            Iterator =  fun(Rec,_) when element(Counter_position,Rec) > Counter_deleted -> 
                            Count = element(Counter_position,Rec),
                            New_rec = erlang:setelement(Counter_position,Rec,Count - 1),
                            mnesia:write(TableName,New_rec,read),
                            [];
                        (_,_) -> []
                        end,
            Find = fun({Fun,Table}) -> mnesia:foldl(Fun, [],Table) end,
            mnesia:activity(transaction,Find,[{Iterator,TableName}],mnesia_frag)
    end.


您注意到我有代码可以帮助我从记录中动态地找到计数器字段的位置。帮助我这样做的代码如下所示:


position(_,[]) -> -1;
position(Value,List)->
    find(lists:member(Value,List),Value,List,1).

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

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

这是因为这个模块不能知道任何程序员的记录来帮助他使用counter.Hence,以便使用元组操作函数(如{{1)从记录中访问计数器的值我必须动态地计算它在记录的元组表示中的位置。

These two functions have worked for me and are still working till auto_increment
is implemented in mnesia.

例如,使用qlc(查询列表解析)查询具有动态约束的表,请考虑以下代码:


select(Q)->
    F = fun(QH) -> qlc:e(QH) end,
    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])).

%% Applying the two functions....
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; (_) -> false end, read_by_custom_validation(F,TableName).

在库存管理系统中,这是有效的......



(stock_project@127.0.0.1)6> stock:get_items_in_range(1,4).
[#item{item_id = "D694",name = "cement",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 500,units = "bags",unit_cost = 20000,
       state = available,last_modified = undefined,
       category = "building material",counter = 1},
 #item{item_id = "131B",name = "nails",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 20000,units = "kgs",unit_cost = 1000,
       state = available,last_modified = undefined,
       category = "building material",counter = 2},
 #item{item_id = "FDD9",name = "iron sheets",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 20,units = "bars",unit_cost = 50000,
       state = available,last_modified = undefined,
       category = "building material",counter = 3},
 #item{item_id = "09D4",name = "paint",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 30000,units = "tins",unit_cost = 5000,
       state = available,last_modified = undefined,
       category = "building material",counter = 4}]
(stock_project@127.0.0.1)7>

这对我有用。请告诉我如何处理柜台。或者你可以告诉我你是如何处理它们的。

1 个答案:

答案 0 :(得分:4)

计数器是您问题的最佳解决方案吗?我希望高度分散的系统可能最好没有它们,因此它们被排除在外。

  1. 如果您想要计算表中有多少元素。有一个单独的记录表{counts,TableName,Count},并在添加和删除存储表的事务中使用dirty_update_counter操作。
  2. 如果您想按特定顺序检索数据(例如,添加它们的顺序),请使用显示here的OrderedBy解决方案。
  3. 如果你想要快速的外键查找,我会考虑使用你的键的类型(item_id的二进制值可能更快),并测试你是否有任何显着的改进。
  4. 另外,我不明白为什么在删除表条目时必须更新每条记录上的计数器。我不认为MySQL或任何其他RDBMS这样做。