Mnesia 查询游标 - 在实际应用程序中使用它们
Posted
技术标签:
【中文标题】Mnesia 查询游标 - 在实际应用程序中使用它们【英文标题】:Mnesia Query Cursors - Working with them in Practical applications 【发布时间】:2012-08-11 15:37:54 【问题描述】:在大多数应用程序中,不可避免地需要查询用户想要browse through
的大量信息。这就是导致我使用光标的原因。使用 mnesia,游标是使用 qlc:cursor/1 or qlc:cursor/2 实现的。与他们合作了一段时间,多次面对这个问题,
E:\>erl Eshell V5.9(使用 ^G 中止) 1> 失忆症:开始()。 好的 2> rd(obj,键,值)。 对象 3> mnesia:create_table(obj,[attributes,record_info(fields,obj)])。 原子,好的 4> 写 = fun(Obj) -> mnesia:transaction(fun() -> mnesia:write(Obj) end) 结束。 #Fun当你尝试在 mnesia 事务之外调用 say:5> [写入(#objkey = N,value = N * 2) || N 记忆:交易(乐趣()-> QC = cursor_server:cursor(qlc:q([XX || XX
qlc:next_answers/2
时,你会得到一个异常。不仅在事务之外,而且即使该方法是由与创建游标的进程不同的进程执行的,也必然会发生问题。
另一个有趣的发现是,一旦您退出 mnesia 事务,mnesia 游标中涉及的一个进程(显然 mnesia 在后台生成一个进程)就会退出,导致游标无效。看看下面这个:-模块(光标服务器)。 -编译(export_all)。 光标(Q)-> 案例记忆:is_transaction() 的 假-> F = fun(QH)-> qlc:cursor(QH,[]) 结束, mnesia:activity(transaction,F,[Q],mnesia_frag); 真-> qlc:光标(Q,[]) 结束。 %% --- 模块结束 ------------------------------ -然后在外壳中,我使用该方法:
7> QC = cursor_server:cursor(qlc:q([XX || XX , 8> erlang:is_process_alive(list_to_pid(""))。 错误的 9> erlang:is_process_alive(list_to_pid(""))。 真的 10> 自我()。 11> qlc:next_answers(QC,3)。 ** 异常错误:qlc_cursor_pid_no_longer_exists, 在函数 qlc:next_loop/3 中(qlc.erl,第 1359 行) 12>因此,这使得构建一个用户需要浏览一组特定结果的 Web 应用程序变得非常困难,分组说:给他/她前 20 个,然后是下一个 20 等等。这包括,获取第一个结果,将它们发送到网页,然后等待用户单击
NEXT
,然后向qlc:cursor/2
询问下一个 20,依此类推。这些操作无法在挂在 mnesia 事务中时完成!!!唯一可能的方法是生成一个将挂在那里的进程,接收并发送回下一个答案作为消息,并接收 next_answers 请求作为这样的消息:
-定义(CURSOR_TIMEOUT,计时器:小时(1))。
%% 初始请求在下面进行
请求(页面大小)->
我 = 自我(),
CursorPid = spawn(?MODULE,cursor_pid,[Me,PageSize]),
收到
initial_answers,Ans ->
%% 找到隐藏光标 Pid 的方法
%% 在页面中以便后续请求
%% 随之而来
Ans,pid_to_list(CursorPid)
在 ?CURSOR_TIMEOUT -> 超时之后
结尾。
cursor_pid(ParentPid,PageSize)->
F = 乐趣(Pid,N)->
QC = cursor_server:cursor(qlc:q([XX || XX
从 ! next_answers,qlc:next_answers(QC,Num),
%% 这里有问题!如何循环回
%% 检查:Erlang Y-Combinator
删除->
%% 它可能已经死了,所以我们在这里要小心!
试试 qlc:delete_cursor(QC) 的
_ -> 好的
抓住
_:_ -> 好的
结尾,
退出(正常)
在 ?CURSOR_TIMEOUT -> 退出(正常)之后
结尾
结尾,
mnesia:activity(transaction,F,[ParentPid,PageSize],mnesia_frag)。
next_answers(CursorPid,PageSize)->
list_to_pid(CursorPid) ! self(),next_answers,PageSize,
收到
next_answers,Ans ->
Ans,pid_to_list(CursorPid)
在 ?CURSOR_TIMEOUT -> 超时之后
结尾。
这将产生一个更复杂的管理流程出口、跟踪/监控等问题。我想知道为什么 mnesia 实施者没有看到这个!
现在,这让我想到了我的问题。我一直在网上寻找解决方案,您可以查看出现问题的这些链接:mnemosyne、Ulf Wiger's Solution to Cursor Problems、AMNESIA - an RDBMS implementation of mnesia。1. 有没有人知道如何以不同于记录的方式处理 mnesia 查询游标,并且值得分享? 2. mnesia 实现者决定在单个事务中强制使用游标的原因是什么:甚至是对 next_answers
的调用? 3. 从我所介绍的内容来看,有什么我不明白的地方吗(除了我糟糕的错误插图代码 - 请忽略那些)? 4. AMNESIA(在我上面给出的链接的第 4.7 节),有一个很好的游标实现,因为对 next_answers 的后续调用,不需要在同一个事务中,也不需要在同一个过程中。您是否会建议任何人因此而从 mnesia 切换到 amnesia,而且,这个库是否仍然受支持? 5. Ulf Wiger
,(许多 erlang 库的作者,尤其是 erlang 库的作者。 GPROC),建议使用mnesia:select/4
。我将如何使用它来解决 Web 应用程序中的光标问题? 注意:请不要建议我离开 mnesia 并使用其他东西,因为我想使用 mnesia 来解决这个特定问题。感谢您抽出宝贵时间阅读所有这些问题。
【问题讨论】:
【参考方案1】:动机是事务抓取(在您的情况下)读取锁。 锁不能保留在事务之外。
如果你愿意,你可以在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).
【讨论】:
【参考方案2】:我认为这可能对您有所帮助:
使用 async_dirty 而不是 transaction
Record,Cont=mnesia:activity(async_dirty, fun mnesia:select/4,[md,[Match_head,[Guard],[Result]],Limit,read])
然后读取下一个限制记录数:
mnesia:activity(async_dirty, fun mnesia:select/1,[Cont])
完整代码:
-record(md,id,name).
batch_delete(Id,Limit) ->
Match_head = #mdid='$1',name='$2',
Guard = '<','$1',Id,
Result = '$_',
Record,Cont = mnesia:activity(async_dirty, fun mnesia:select/4,[md,[Match_head,[Guard],[Result]],Limit,read]),
delete_next(Record,Cont).
delete_next('$end_of_table') ->
over;
delete_next(Record,Cont) ->
delete(Record),
delete_next(mnesia:activity(async_dirty, fun mnesia:select/1,[Cont])).
delete(Records) ->
io:format("delete(~p)~n",[Records]),
F = fun() ->
[ mnesia:delete_object(O) || O <- Records]
end,
mnesia:transaction(F).
记住你不能在一个事务中使用游标
【讨论】:
以上是关于Mnesia 查询游标 - 在实际应用程序中使用它们的主要内容,如果未能解决你的问题,请参考以下文章