3

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

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

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

{TabName,键,整数}
这也不清楚,可能是因为没有 erlang 书籍或 mnesia 文档提供示例来解释它。这迫使我实现自己的计数器操作 API。因为我希望能够使用计数器访问和管理我的记录,所以我不得不在我的记录中包含一个名为“计数器”的字段,然后在将记录添加到旨在具有计数器的表中时,我这样做:

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

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

%% @doc 每当您在表
%% 中写入新记录时,都会调用此函数,方法是将其结果提供给记录中的计数器字段。
%% @end
%%
%% @spec auto_increment(TableName::atom()) -> integer() | 退出(原因)
自动增量(表名)-> 案例列表:member(counter,table_info(TableName,attributes)) of false -> erlang:exit({counter,field,not_found,in_table,TableName}); true -> table_info(TableName,size) + 1 结尾。
table_info(Tab,Item)-> F = fun({X,Y}) -> mnesia:table_info(X,Y) 结束, mnesia:activity(transaction,F,[{Tab,Item}],mnesia_frag)。

为了解释这一点,如果计数器字段不是表的属性,我会强制让试图执行此代码的进程退出,因此如果程序员在 try ...catch 或 case ( catch ...) 的 body ,他们很容易看出哪里出了问题。或者,我可以通过 using 询问此代码片段是否正在事务中执行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),
    案例列表:member(counter,Attrs) of
        false -> erlang:exit({counter,field,not_found,in_table,TableName});
        真->
            Counter_position = position(counter,Attrs) + 1,         
            Iterator = fun(Rec,_) 当元素(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)。

查找(假,_,_,_)->-1;
找到(真,V,[V|_],N)-> N;
找到(真,V,[_|X],N)->
    找到(V,X,N + 1)。

查找(V,[V|_],N)-> N;
查找(V,[_|X],N)-> 查找(V,X,N + 1)。


这是因为这个模块必须不知道任何程序员的记录来帮助他使用计数器。因此,为了使用元组操作函数从记录中访问计数器的值,例如element(N::integer(),Tuple::tuple()),我必须计算它在元组中的位置动态地表示记录。

这两个函数对我有用,并且
在 mnesia 中实现 auto_increment 之前仍然有效。

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


选择(Q)->
    F = fun(QH) -> qlc:e(QH) 结束,
    mnesia:activity(transaction,F,[Q],mnesia_frag)。

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

%% 应用这两个函数....
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}) 当 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 = {"30/12/2010","11:29:10 am"},
       min_stock = 500,units = "袋子",unit_cost = 20000,
       state = 可用,last_modified = 未定义,
       category = "建筑材料",counter = 1},
 #it​​em{item_id = "131B",name = "钉子",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 20000,units = "kgs",unit_cost = 1000,
       state = 可用,last_modified = 未定义,
       category = "建筑材料",counter = 2},
 #it​​em{item_id = "FDD9",name = "铁皮",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 20,units = "bars",unit_cost = 50000,
       state = 可用,last_modified = 未定义,
       category = "建筑材料",counter = 3},
 #it​​em{item_id = "09D4",name = "paint",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 30000,units = "罐子",unit_cost = 5000,
       state = 可用,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 回答