【问题标题】:Erlang : Mnesia : Lookup and update based on fields other than the keyErlang : Mnesia : 基于键以外的字段进行查找和更新
【发布时间】:2010-12-21 23:17:59
【问题描述】:

我在 mnesia 中有一个表,我需要更新其中记录中的各个字段。根据Erlang : Mnesia : Updating a single field value in a row,如果我这样做:

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

现在据我了解,上面的代码基于Key 读取记录P,获取记录上的写锁,因此在读取和写回记录时没有其他事务修改该记录(或简而言之,更新)。到目前为止一切顺利。

现在我的要求是我需要能够根据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).

有没有办法使用 don't cares 来查找条目?

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

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

【问题讨论】:

    标签: database erlang mnesia


    【解决方案1】:

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

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

    【讨论】:

    • 我的应用程序需要多个客户端同时访问表。如果我首先使用 mnesia:match_object 来获取读取锁定的记录,然后更新记录上的锁,那么在我使用 match_object 获取记录并升级到写入锁之间,是否存在另一个事务的可能性考虑到我的并发访问要求,使用 mnesia:match_object 读取记录?
    • 不,整个 mnesia:transaction 是原子的,它看到的数据版本一致。事务将被重试多次,直到提交通过。
    【解决方案2】:

    这样,

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

    【讨论】:

      【解决方案3】:

      您不必担心。来自 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,只有匹配的记录会被写锁定;如果不指定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),您可以测试各种锁定行为。

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

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

      编辑:以上都是在 set-type 表上完成的,match_object 锁定行为可能在 bag-type 表上有所不同。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • 我考虑过这一点,但我只需要更新并锁定一条记录。该表将被多个客户端同时使用,锁定整个表只更新一个记录会降低吞吐量。
        猜你喜欢
        • 2022-01-11
        • 2011-12-25
        • 1970-01-01
        • 2012-03-22
        • 2019-10-19
        • 1970-01-01
        • 1970-01-01
        • 2019-05-01
        • 2017-06-26
        相关资源
        最近更新 更多