我在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).
答案 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:
mnesia:match_object
,并获取所有返回对象的读锁定。mnesia:match_object
,并获取对同一对象的读锁定。请注意,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)