gpt4 book ai didi

erlang - Mnesia:如何同时锁定多行以便我可以写入/读取 "consistent"记录集

转载 作者:行者123 更新时间:2023-12-04 12:49:14 25 4
gpt4 key购买 nike

我多么希望我的问题开头是这样的

拿一张有 26 个键的表,a-z 并让它们具有整数值。
创建一个过程,哎哟,一遍又一遍地做两件事

  • 在一笔交易中,为 a、b 和 c 写入随机值,使得这些值总和为 10
  • 在另一个事务中,读取 a、b 和 c 的值,如果它们的值总和不为 10
  • 则提示。

    如果你启动这些进程中的几个,你会很快看到 a、b 和 c 处于它们值总和不为 10 的状态。我相信没有办法让 mnesia“锁定这 3 条记录”在开始写入(或读取)之前”,只能让 mnesia 在记录到达时锁定记录(可以这么说),这允许记录集的值违反我的“总和为 10”约束。

    如果我是对的,这个问题的解决方案包括
  • 在写入(或读取)3 条记录之前锁定整个表——我讨厌将整个表锁定 3 条记录,
  • 创建一个过程来跟踪谁正在读取或写入哪些 key ,并保护批量操作不被其他任何人写入或读取,直到操作完成。当然,我必须确保所有进程都使用了这个......废话,我想这意味着编写我自己的 AccessMod 作为事件/4 的第四个参数,这似乎是一个不平凡的练习
  • 还有一些我不够聪明无法弄清楚的事情。

  • 想法?

    好的,我是一个雄心勃勃的 Erlang 新手,很抱歉,如果这是一个愚蠢的问题,但是

    我正在构建一个特定于应用程序的内存分布式缓存,我需要能够在一个事务中写入一组键、值对,并在一个事务中检索一组值。换句话说,我需要
    1) 将 40 个键值对写入缓存,并确保在此多键写入操作期间没有其他人可以读取或写入这 40 个键中的任何一个;和,
    2) 在一次操作中读取 40 个键并返回 40 个值,因为知道从这次读取操作开始到结束,所有 40 个值都没有改变。

    我能想到的唯一方法是在 fetch_keylist([ListOfKeys]) 的开头或 write_keylist([KeyValuePairs], 但我不想这样做,因为我有许多进程同时进行自己的 multi_key 读取和写入,我不想在任何进程需要读取/写入相对较小的记录子集时锁定整个表。

    帮助?

    试图更清楚:我不认为这只是使用普通事务

    我想我在问一个比这更微妙的问题。想象一下,我有一个进程,在一个事务中,它遍历 10 条记录,并在执行过程中锁定它们。现在想象这个过程开始了,但在它迭代到第三条记录之前,另一个过程更新了第三条记录。就事务而言,这会很好,因为第一个进程还没有锁定第 3 条记录(还),并且 OTHER 进程修改了它并在第一个进程到达它之前释放了它。我想要的是保证一旦我的第一个进程开始,在第一个进程完成之前,没有其他进程可以触及这 10 条记录。

    问题解决了 - 我是个白痴......我猜......
    谢谢你们所有的病人,特别是 Hynek -Pichi- Vychodil!
    我准备了我的测试代码来显示问题,实际上我可以重现这个问题。然后我简化了代码的可读性,问题就消失了。我无法再次重现该问题。这对我来说既尴尬又神秘,因为我有这个问题好几天了。此外,mnesia 从未提示过我在事务之外执行操作,并且我的代码中没有任何脏事务,我不知道我是如何将这个错误引入我的代码的!

    我已经把隔离的概念敲入我的脑海,并且不会怀疑它再次存在。

    感谢您的教育。

    实际上,问题是在事务中使用 try/catch 围绕 mnesia 操作。见 here更多。

    最佳答案

    Mnesia 事务会为你做这件事。除非您进行脏操作,否则这就是事务。因此,只需将您的写入和读取操作放在一个 mnesia 会休息的事务中。一个事务中的所有操作都是作为一个原子操作完成的。 Mnesia 事务隔离级别有时被称为“可序列化”,即最强的隔离级别。

    编辑:

    看来你错过了 Erlang 并发进程的重要一点。 (公平地说,它不仅在 Erlang 中如此,而且在任何真正的并发环境中都是如此,当有人争论其他人时,它不是真正的并发环境。)除非您进行一些同步,否则您无法区分哪个 Action 先发生,哪个 Action 第二次发生。您可以执行此同步的唯一方法是使用消息传递。你只保证了 Erlang 中消息的一件事,即从一个进程发送到另一个进程的消息的排序。这意味着当您发送两条消息时 M1M2来自进程A处理 B他们以相同的顺序到达。但是如果您发送消息 M1来自 AB和留言 M2来自 CB他们可以按任何顺序到达。仅仅因为你怎么知道你先发送了哪条消息?如果你发消息 M1 就更糟了来自 AB然后 M2来自 ACM2到达 C发送 M3来自 CB你没有保证M1到达 B之前 M3 .在当前的实现中,它甚至会发生在一个 VM 中。但是你不能依赖它,因为它不能保证并且甚至可以在下一个版本的 VM 中更改,只是由于不同调度程序之间的消息传递实现。

    它说明了并发进程中的事件排序问题。现在回到 mnesia 事务。 Mnesia 事务必须无副作用 fun .这意味着可能没有任何消息从事务外部发送。因此,您无法分辨哪个事务首先开始以及何时开始。唯一可以判断交易是否成功并且他们订购的东西只能通过其效果来确定。当你考虑到这一点时,你微妙的澄清是没有意义的。一个事务会在原子操作中读取所有键,即使它在事务实现中被实现为一键读取,并且您的写操作也将作为原子操作执行。在您读取第一个事务中的第一个键之后,您无法判断是否在第二个事务中写入了第四个键,因为从外部无法观察到它。两个事务都将作为单独的原子操作按特定顺序执行。从外部的角度来看,所有的键都将在同一时间点被读取,强制它是 mnesia 的工作。如果你从交易内部发送消息,你违反了 mnesia 交易属性,你不会感到惊讶它会表现得很奇怪。具体来说,这个消息可以发送很多次。

    编辑2:

    If you spin-up even a few of these processes you will see that very quickly a, b and c are in a state where their values do not sum to 10.



    我很好奇你为什么认为它会发生或者你测试过它?向我展示您的测试用例,我将展示我的:
    -module(transactions).

    -export([start/2, sum/0, write/0]).

    start(W, R) ->
    mnesia:start(),
    {atomic, ok} = mnesia:create_table(test, [{ram_copies,[node()]}]),
    F = fun() ->
    ok = mnesia:write({test, a, 10}),
    [ ok = mnesia:write({test, X, 0}) || X <-
    [b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z]],
    ok
    end,
    {atomic, ok} = mnesia:transaction(F),
    F2 = fun() ->
    S = self(),
    erlang:send_after(1000, S, show),
    [ spawn_link(fun() -> writer(S) end) || _ <- lists:seq(1,W) ],
    [ spawn_link(fun() -> reader(S) end) || _ <- lists:seq(1,R) ],
    collect(0,0)
    end,
    spawn(F2).

    collect(R, W) ->
    receive
    read -> collect(R+1, W);
    write -> collect(R, W+1);
    show ->
    erlang:send_after(1000, self(), show),
    io:format("R: ~p, W: ~p~n", [R,W]),
    collect(R, W)
    end.

    keys() ->
    element(random:uniform(6),
    {[a,b,c],[a,c,b],[b,a,c],[b,c,a],[c,a,b],[c,b,a]}).

    sum() ->
    F = fun() ->
    lists:sum([X || K<-keys(), {test, _, X} <- mnesia:read(test, K)])
    end,
    {atomic, S} = mnesia:transaction(F),
    S.

    write() ->
    F = fun() ->
    [A, B ] = L = [ random:uniform(10) || _ <- [1,2] ],
    [ok = mnesia:write({test, K, V}) || {K, V} <- lists:zip(keys(),
    [10-A-B|L])],
    ok
    end,
    {atomic, ok} = mnesia:transaction(F),
    ok.

    reader(P) ->
    case sum() of
    10 ->
    P ! read,
    reader(P);
    _ ->
    io:format("ERROR!!!~n",[]),
    exit(error)
    end.

    writer(P) ->
    ok = write(),
    P ! write,
    writer(P).

    如果它不起作用,那将是非常严重的问题。有一些重要的应用程序,包括依赖它的支付系统。如果您有测试用例显示它已损坏,请在 erlang-bugs@erlang.org 报告错误

    关于erlang - Mnesia:如何同时锁定多行以便我可以写入/读取 "consistent"记录集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7942879/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com