Erlang:Mnesia:根据键以外的字段查找和更新

时间:2009-12-01 10:29:08

标签: database erlang mnesia

我在mnesia中有一个表,我需要更新其中记录中的各个字段。根据{{​​3}},如果我做了类似的事情:

update_a(Tab, Key, Value) ->
  fun() ->
    [P] = mnesia:wread({Tab, Key}),
    mnesia:write(Tab, P#rec{a=Value}, write)
  end.

据我了解,上面的代码基于P读取记录Key,获取记录上的写锁定,以便在读取记录时没有其他事务修改此记录。回写(或简称,更新)。到目前为止一切都很好。

现在我的要求是我需要能够根据表中的Key和另一个字段读取记录,然后对其执行更新。查找此功能的函数是mnesia:match_object。现在的问题是,根据Erlang : Mnesia : Updating a single field value in a row,该函数仅支持读锁定,而不支持写锁定。

这样做的结果是,假设在上面的函数中我使用的是mnesia:match_object,我将得到一个(一组)记录,所有记录都带有读锁。在我读取记录之后,我需要对检索到的数据执行一些检查,然后仅在条件满足时写回更新的记录。现在,假设有两个并行事务T1和T2由两个不同的源运行启动。 T1和T2都同时访问相同的记录。由于它们被读锁定,因此T1和T2都能够并行读取记录。 T1和T2都将对同一记录执行相同的检查,如果条件匹配,则两者都将继续执行更新。但是,在我的代码中,如果T1和T2已经连续执行,T1将对记录进行更改,在T2中,它将读取这些已更改的记录,并且条件将失败并且不会进行更新。 / p>

简而言之,我需要编写由mnesia:match_object返回的锁记录。文档明确指出只支持读锁定。有没有其他选择?

更新 我一直在尝试一下,我认为可能的解决方案是使用复合键。假设我将数据写入如下表:

mnesia:transaction(fun() -> mnesia:write(mytable, #rec{i={1,2}, a=2, b=3}, write) end).

有没有办法查找条目,使用不关心?

我尝试了这些,但都返回了空结果:

mnesia:transaction(fun()-> mnesia:read(mytable, {1,'_'}, read) end).
mnesia:transaction(fun()-> mnesia:read(mytable, {1,_}, read) end).

4 个答案:

答案 0 :(得分:2)

即使它在read锁定下返回它们,您仍然可以通过写入更新它们。整个read / wread只是一个优化。

您可以使用ordered_set表来实现this mnesia-trick on compound keys

答案 1 :(得分:2)

像这样,

        case mnesia:match_object(#rec{name=Name, _='_'}) of
            [] -> not_found;
            [First|Rest] -> something
        end,

答案 2 :(得分:1)

你不必担心它。来自mnesia文档:

  

可以共享读锁,这意味着如果一个事务设法获取项上的读锁,则其他事务也可以获取对同一项的读锁。但是,如果某人有读锁定,则没有人可以在同一项目中获取写锁定。如果某个人有写锁定,则没有人可以在同一个项目中获取读锁定或写锁定。

如果某个事务对象具有读锁定,则该对象不能被另一个事务编辑。

假设您有两个并行执行的事务T1和T2:

  1. T1执行mnesia:match_object,并获取所有返回对象的读锁定。
  2. T2执行等效的mnesia:match_object,并获取对同一对象的读锁定。
  3. T2尝试获取对象的写锁定(进行编辑)。
  4. Mnesia自动中止T2,稍后重试。
  5. T1获取对象的写锁定,并对其进行编辑。
  6. T1结束。
  7. Mnesia重试T2。
  8. 请注意,T2可能会重试多次,具体取决于T1完成所需的时间(即释放其锁定)。

    根据我的测试,mnesia:match_object的锁定行为并不一致。例如,mnesia:match_object(mytable, {mytable, 2, '_'}, LockType)将仅使用密钥2锁定记录,但mnesia:match_object(mytable, {mytable, '_', test}, LockType)将锁定整个表。

    另请注意,文档不正确,mnesia:match_object(Table, Pattern, write)确实有效,并且似乎遵循与“读取”相同的模式,即。如果指定密钥,则只有匹配的记录将被写入锁定;如果您没有指定密钥,则整个表将被写入锁定。

    您可以通过以下方式自行测试:

    test() ->
        mnesia:transaction(fun()-> 
            Table = mytable,
            Matches = mnesia:match_object(Table, {Table, 2, '_'}, write),
            io:format("matched: ~p~n", [Matches]),
            spawn(fun()->mnesia:transaction(fun()->
                            io:format("trying to read~n",[]),
                            io:format("read: ~p~n", [mnesia:read(Table, 2, read)])
                            end) end),        
            timer:sleep(1000),
            RereadMatches = lists:map(fun(#mytable{id=Id}) -> mnesia:read(Table, Id, write) end, Matches),
            io:format("reread matches: ~p~n", [RereadMatches])
            end).
    

    通过更改传递给match_object的模式和锁定类型,以及在生成的进程中传递给mnesia:read的键号和锁类型(或使用mnesia:write),您可以测试各种锁定行为。

    附录:在同一主题上查看此post by Ulf Wiger

    附录2:请参阅Mnesia用户指南中的section on "Isolation"

    编辑:以上内容全部在集合类型表上完成,match_object在行李类型表上的锁定行为可能不同。

答案 3 :(得分:0)

您是否可以事先使用mnesia:write_lock_table(Tab)

锁定事务中的表格