在大多数应用程序中,很难避免需要查询用户想要浏览
的大量信息。这就是我使用光标的原因。对于 mnesia,光标是使用 qlc:cursor/1 or qlc:cursor/2 实现的。 。与他们合作了一段时间并多次遇到这个问题后,
11> qlc:next_answers(QC,3). ** exception error: {qlc_cursor_pid_no_longer_exists,<0.59.0>} in function qlc:next_loop/3 (qlc.erl, line 1359) 12>我突然想到,整个游标事件必须在一个 mnesia 事务中:作为一个整体执行一次。像下面这样
E:\>erl Eshell V5.9 (abort with ^G) 1> mnesia:start(). ok 2> rd(obj,{key,value}). obj 3> mnesia:create_table(obj,[{attributes,record_info(fields,obj)}]). {atomic,ok} 4> Write = fun(Obj) -> mnesia:transaction(fun() -> mnesia:write(Obj) end) end. #Fun<erl_eval.6.111823515> 5> [Write(#obj{key = N,value = N * 2}) || N <- lists:seq(1,100)],ok. ok 6> mnesia:transaction(fun() -> QC = cursor_server:cursor(qlc:q([XX || XX <- mnesia:table(obj)])), Ans = qlc:next_answers(QC,3), io:format("\n\tAns: ~p~n",[Ans]) end). Ans: [{obj,20,40},{obj,21,42},{obj,86,172}] {atomic,ok} 7>当您尝试在 mnesia 事务之外调用 say:
qlc:next_answers/2
时,您将收到异常。不仅在事务之外,而且即使该方法是由与创建游标的进程不同的进程执行的,也必然会发生问题。另一个有趣的发现是,一旦退出 mnesia 事务,mnesia 游标涉及的进程之一(显然 mnesia 在后台生成一个进程)就会退出,导致游标无效。看看下面这个:
-module(cursor_server). -compile(export_all).然后在 shell 中,我使用该方法:
cursor(Q)-> case mnesia:is_transaction() of false -> F = fun(QH)-> qlc:cursor(QH,[]) end, mnesia:activity(transaction,F,[Q],mnesia_frag); true -> qlc:cursor(Q,[]) end.
%% --- End of module -------------------------------------------
7> QC = cursor_server:cursor(qlc:q([XX || XX <- mnesia:table(obj)])). {qlc_cursor,{<0.59.0>,<0.30.0>}} 8> erlang:is_process_alive(list_to_pid("<0.59.0>")). false 9> erlang:is_process_alive(list_to_pid("<0.30.0>")). true 10> self(). <0.30.0> 11> qlc:next_answers(QC,3). ** exception error: {qlc_cursor_pid_no_longer_exists,<0.59.0>} in function qlc:next_loop/3 (qlc.erl, line 1359) 12>因此,这使得构建一个用户需要浏览一组特定结果的 Web 应用程序变得非常困难,按组说:给他/她前 20 个,然后是下 20 个,等等。这涉及到获取第一个结果,将它们发送到网页,然后等待用户单击
NEXT
,然后询问 qlc:cursor/2
接下来的 20 个结果,依此类推在。当卡在 mnesia 事务中时,这些操作无法完成!唯一可能的方法是生成一个卡在那里的进程,以消息形式接收和发送回下一个答案,并以消息形式接收 next_answers 请求,如下所示:
-define(CURSOR_TIMEOUT,timer:hours(1)). %% initial request is made here below request(PageSize)-> Me = self(), CursorPid = spawn(?MODULE,cursor_pid,[Me,PageSize]), receive {initial_answers,Ans} -> %% find a way of hidding the Cursor Pid %% in the page so that the subsequent requests %% come along with it {Ans,pid_to_list(CursorPid)} after ?CURSOR_TIMEOUT -> timedout end. cursor_pid(ParentPid,PageSize)-> F = fun(Pid,N)-> QC = cursor_server:cursor(qlc:q([XX || XX <- mnesia:table(obj)])), Ans = qlc:next_answers(QC,N), Pid ! {initial_answers,Ans}, receive {From,{next_answers,Num}} -> From ! {next_answers,qlc:next_answers(QC,Num)}, %% Problem here ! how to loop back %% check: Erlang Y-Combinator delete -> %% it could have died already, so we be careful here ! try qlc:delete_cursor(QC) of _ -> ok catch _:_ -> ok end, exit(normal) after ?CURSOR_TIMEOUT -> exit(normal) end end, mnesia:activity(transaction,F,[ParentPid,PageSize],mnesia_frag). next_answers(CursorPid,PageSize)-> list_to_pid(CursorPid) ! {self(),{next_answers,PageSize}}, receive {next_answers,Ans} -> {Ans,pid_to_list(CursorPid)} after ?CURSOR_TIMEOUT -> timedout end.
这将产生管理进程退出、跟踪/监控等更复杂的问题。我想知道为什么 mnesia 实现者没有看到这一点!
现在,这引出了我的问题。我一直在网上寻找解决方案,您可以查看出现问题的这些链接:mnemosyne , Ulf Wiger's Solution to Cursor Problems , AMNESIA - an RDBMS implementation of mnesia .
1. 有谁知道如何以与记录的方式不同的方式处理 mnesia 查询游标,并且值得分享吗?
2. mnesia implemeters 决定在单个事务中强制使用游标的原因是什么:甚至是对 next_answers
的调用?
3. 从我所呈现的内容来看,有什么我不太明白的地方吗(除了我糟糕的错误插图代码 - 请忽略那些)?
4. AMNESIA(在我上面给出的链接的第 4.7 节上)具有良好的游标实现,因为对 next_answers 的后续调用不需要位于同一事务中,也不需要由同一进程进行。您是否会建议任何人因此从遗忘症切换到失忆症,而且这个库是否仍然受支持?
5. Ulf Wiger
(许多 erlang 库,特别是 GPROC 的作者)建议使用 mnesia:select/4
。我如何使用它来解决 Web 应用程序中的光标问题?
注意:请不要建议我离开 mnesia 并使用其他东西,因为我想使用 mnesia 来解决这个特定问题。感谢您花时间阅读所有这些问题。
最佳答案
动机是事务获取(在您的情况下)读锁。 锁不能保留在事务之外。
如果你愿意,你可以在 dirty_context 中运行它,但是你会失去 事务属性,即表可能在调用之间发生变化。
make_cursor() ->
QD = qlc:sort(mnesia:table(person, [{traverse, select}])),
mnesia:activity(async_dirty, fun() -> qlc:cursor(QD) end, mnesia_frag).
get_next(Cursor) ->
Get = fun() -> qlc:next_answers(Cursor,5) end,
mnesia:activity(async_dirty, Get, mnesia_frag).
del_cursor(Cursor) ->
qlc:delete_cursor(Cursor).
关于erlang - Mnesia 查询游标 - 在实际应用中使用它们,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11967660/