2012-08-15 24 views
2

在大多數應用程序中,很難避免需要查詢用戶想要的大量信息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).
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 -------------------------------------------
然後在shell中,我使用該方法:
 
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的實施者沒有看到這個!

現在,這讓我想起我的問題。我一直在網絡上尋找解決方案,您可以查看這些問題出現的鏈接:mnemosyneUlf Wiger's Solution to Cursor ProblemsAMNESIA - 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。我感謝你的時間閱讀所有這些問題。

回答

3

動機是事務抓取(在你的情況下)讀鎖。 鎖定不能保存在交易之外。

如果需要,您可以在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). 
+0

+1優秀的解決方案。非常感謝 !!! – 2012-09-03 10:06:48

1

我認爲這可以幫助你:

使用async_dirty的代替交易

{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 = #md{id='$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). 

記住你不能使用光標移出一個交易

相關問題