在大多數應用程序中,很難避免需要查詢用戶想要的大量信息browse through
。這是我引導遊標的原因。有了mnesia,遊標使用qlc:cursor/1 or qlc:cursor/2實現。在與他們合作一段時間後,多次面對這個問題,Mnesia Query Cursors - 在實際應用中與它們一起使用
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事務之外調用說:
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應用程序非常困難,用戶需要瀏覽一組特定的結果,按組說:give他/她前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執行器決定在單個事務中強制執行遊標的原因是什麼?即使是調用next_answers
?
3.有什麼,從我所介紹的,我不明白(除了我的壞車圖代碼 - 請忽略這些)?
4. AMNESIA(關於我上面給出的鏈接的第4.7節),具有很好的遊標實現,因爲next_answers的後續調用不需要在同一個事務中,NOR由相同的進程。你會建議任何人從這個遺忘轉向失憶嗎?還有,這個圖書館是否仍然支持?建議使用mnesia:select/4
。我將如何使用它來解決Web應用程序中的遊標問題?
注意:請不要建議我離開mnesia並使用別的東西,因爲我想爲這個特定的問題使用mnesia。我感謝你的時間閱讀所有這些問題。
+1優秀的解決方案。非常感謝 !!! – 2012-09-03 10:06:48