IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [erlang]自定义方式更新mnesia表结构与更新数据

    庆亮发表于 2014-03-31 12:37:33
    love 0

    mnesia的transform_table可以实现事务性的表结构转换,但是它本身有两个糟糕的地方:占用内存大、速度不够快。

    先来看看mnesia:transform_table到底做了什么:

    transform_table(Tab, Fun, NewAttrs, NewRecName)
      when is_function(Fun), is_list(NewAttrs), is_atom(NewRecName) ->
        schema_transaction(fun() -> do_transform_table(Tab, Fun, NewAttrs, NewRecName) end);
    
    transform_table(Tab, ignore, NewAttrs, NewRecName)
      when is_list(NewAttrs), is_atom(NewRecName) ->
        schema_transaction(fun() -> do_transform_table(Tab, ignore, NewAttrs, NewRecName) end);
    
    transform_table(Tab, Fun, NewAttrs, NewRecName) ->
        {aborted,{bad_type, Tab, Fun, NewAttrs, NewRecName}}.
    
    schema_transaction(Fun) ->
        case get(mnesia_activity_state) of
    	undefined ->
    	    Args = [self(), Fun, whereis(mnesia_controller)],
    	    Pid = spawn_link(?MODULE, schema_coordinator, Args),
    	    receive
    		{transaction_done, Res, Pid} -> Res;
    		{'EXIT', Pid, R} -> {aborted, {transaction_crashed, R}}
    	    end;
    	_ ->
                {aborted, nested_transaction}
        end.
    
    %% This process may dump the transaction log, and should
    %% therefore not be run in an application process
    %%
    schema_coordinator(Client, _Fun, undefined) ->
        Res = {aborted, {node_not_running, node()}},
        Client ! {transaction_done, Res, self()},
        unlink(Client);
    
    schema_coordinator(Client, Fun, Controller) when is_pid(Controller) ->
        %% Do not trap exit in order to automatically die
        %% when the controller dies
    
        link(Controller),
        unlink(Client),
    
        %% Fulfull the transaction even if the client dies
        Res = mnesia:transaction(Fun),
        Client ! {transaction_done, Res, self()},
        unlink(Controller),         % Avoids spurious exit message
        unlink(whereis(mnesia_tm)), % Avoids spurious exit message
        exit(normal).

    看起来其实就是个transaction,不过同时做了schema的更新,这里有个坑后面会提到。总结下transform_table做的事情:
    trans begin -> update schema -> copy records(write to list args) and update records -> trans end
    既然如此,这里使用事务可以保证数据以及结构是同步更新的,而事务的过程中会拷贝至少一份数据(单节点),因此我们可以使用如下的思路来实现:

    1. 保证更新数据时只有一个操作者(串行化)
    2. trans begin -> update schema -> trans end -> update records

    最终实现为:

    %% @doc user-defined的transform
    userdefined_transform() ->
        io:format("userdefined_transform begin time ~p~n",
    ), {atomic, ok} = mnesia:transform_table(db_test_transform, ignore, [k, v, v2]), [begin case mnesia:dirty_read(db_test_transform, K) of {test, K, V} -> mnesia:dirty_write(db_test_transform, {test, K, V, 0}); _ -> ok end end || K <- mnesia:dirty_all_keys(db_test_transform)], io:format("userdefined_transform end time ~p~n",
    ), ok.

    详细代码在这里: https://github.com/qingliangcn/erlang_test

    结果:

    测试自定义transform_table => mnesia_userdefined_transform:test_userdefined_transform/0
    delete table db_test_transform
    create table db_test_transform
    begin to insert test data 1000000
    finish insert test data
    userdefined_transform begin time {{2014,3,31},{20,19,22}}
    userdefined_transform end time {{2014,3,31},{20,19,22}}
    
    测试mnesia:transform_table => mnesia_userdefined_transform:test_mnesia_transform/0
    delete table db_test_transform
    create table db_test_transform
    begin to insert test data 1000000
    finish insert test data
    mnesia_transform begin time {{2014,3,31},{20,19,33}}
    mnesia_transform end time {{2014,3,31},{20,19,41}}
    


沪ICP备19023445号-2号
友情链接