6

我在 mnesia 中有一个表,我需要更新其中记录中的各个字段。根据Erlang : Mnesia :如果我执行以下操作,则连续更新单个字段值:

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

现在据我了解,上面的代码P基于 a读取记录Key,获取记录上的写锁,以便在读取和写回(或简而言之,更新)时没有其他事务修改该记录。到现在为止还挺好。

现在我的要求是我需要能够读取基于Key表中的和另一个字段的记录,然后对其执行更新。执行此查找的函数是mnesia:match_object. 现在的问题是,根据http://www.erlang.org/doc/man/mnesia.html#match_object-3 ,该函数只支持读锁,不支持写锁。

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

简而言之,我需要编写 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

4 回答 4

2

即使它在read锁定状态下返回它们,您仍然可以在之后使用 write 更新它们。整个 read/wread 事情只是一个优化。

您可以使用 ordered_set 表在复合键上实现此 mnesia-trick

于 2009-12-01T10:51:41.997 回答
2

像这样,

        case mnesia:match_object(#rec{name=Name, _='_'}) of
            [] -> not_found;
            [First|Rest] -> something
        end,
于 2010-08-15T06:01:18.677 回答
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。

请注意,T2 可能会重试多次,具体取决于 T1 完成所需的时间(即释放其锁)。

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

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

您可以通过执行以下操作自己测试它:

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),您可以测试各种锁定行为。

附录:参见Ulf Wiger关于同一主题的这篇文章。

附录 2:参见Mnesia 用户指南中的“隔离”部分。

编辑:以上都是在集合型表上完成的,match_object锁定行为在袋型表上可能会有所不同。

于 2009-12-02T22:02:26.383 回答
0

您不能事先将表锁定在您的事务中mnesia:write_lock_table(Tab)吗?

于 2009-12-01T10:51:27.657 回答