本篇簡介紹如何使用Mnesia的RAM replicates功能。使用erlang啟動數個node並將之相連接,這些node可以在單台機器上執行,也可以是在不同機器上執行。假設對某一個node裡的mnesia插入資料時,其它的node裡的mnesia也會一併插入此資料。這樣一來如果某一個node毀掉後其它node還能保留著完整資料以達fault-tolerant。
本篇示範在同一台機器上執行2個node,步驟如下
1.建立目錄給mnesia儲存檔案。在disk裡先建立2個資料夾分別給node1, node2使用。例如
/Users/james/Develop/erlang-test-node/node1
/Users/james/Develop/erlang-test-node/node2
2.啟動node1(注意啟動erlang時的路徑)
cd /Users/james/Develop/erlang-test-node/node1
erl -sname node1
Erlang R16B02 (erts-5.10.3) [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]
Eshell V5.10.3 (abort with ^G)
(node1@jamestekiMacBook-Pro)1>
3.啟動node2(注意啟動erlang時的路徑)
cd /Users/james/Develop/erlang-test-node/node2
erl -sname node2
Erlang R16B02 (erts-5.10.3) [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]
Eshell V5.10.3 (abort with ^G)
(node2@jamestekiMacBook-Pro)1>
4.將node1, node2相連
(node1@jamestekiMacBook-Pro)2> net_adm:ping('node2@jamestekiMacBook-Pro'). (1)
pong
(node1@jamestekiMacBook-Pro)3> nodes(). (2)
['node2@jamestekiMacBook-Pro']
(1)node1執行net_adm:ping/1指令來ping node2。收到回傳pong就代表連上了
(2)列出與自已相連的node,可以從回傳值看到已連接'node2@jamestekiMacBook-Pro'
到node2查看是否已與node1相連
(node2@jamestekiMacBook-Pro)1> nodes().
['node1@jamestekiMacBook-Pro']
5.準備測試用程式碼。將test_mnesia.erl分別copy至node1, node2所執行的當前目錄,即/Users/james/Develop/erlang-test-node/node1、/Users/james/Develop/erlang-test-node/node2
程式碼的重點會在於create_schema跟create_table指令的參數要指定nodes
例如 do_this_once()裡的
mnesia:create_schema([node(),'node2@jamestekiMacBook-Pro']),
其中node()指的是當前的node,'node2@jamestekiMacBook-Pro'指的是node2
本例子的do_this_once()只要在node1執行一次即可,node2不需執行。
test_mnesia.erl
-module(test_mnesia).
-import(lists, [foreach/2]).
-compile(export_all).
-include_lib("stdlib/include/qlc.hrl").
-record(shop, {item, quantity, cost}).
-record(cost, {name, price}).
-record(design, {id, plan}).
do_this_once() ->
mnesia:create_schema([node(),'node2@jamestekiMacBook-Pro']),
mnesia:start(),
mnesia:create_table(shop, [{ram_copies, [node(), 'node2@jamestekiMacBook-Pro']},{attributes, record_info(fields, shop)}]),
mnesia:create_table(cost, [{ram_copies, [node(), 'node2@jamestekiMacBook-Pro']},{attributes, record_info(fields, cost)}]),
mnesia:create_table(design, [{ram_copies, [node(), 'node2@jamestekiMacBook-Pro']},{attributes, record_info(fields, design)}]),
mnesia:stop().
start() ->
mnesia:start(),
mnesia:wait_for_tables([shop,cost,design], 20000).
reset_tables() ->
mnesia:clear_table(shop),
mnesia:clear_table(cost),
F = fun() ->
foreach(fun mnesia:write/1, example_tables())
end,
mnesia:transaction(F).
example_tables() ->
[%% The shop table
{shop, apple, 20, 2.3},
{shop, orange, 100, 3.8},
{shop, pear, 200, 3.6},
{shop, banana, 420, 4.5},
{shop, potato, 2456, 1.2},
%% The cost table
{cost, apple, 1.5},
{cost, orange, 2.4},
{cost, pear, 2.2},
{cost, banana, 1.5},
{cost, potato, 0.6}
].
demo(select_shop) ->
do(qlc:q([X || X <- mnesia:table(shop)]));
add_shop_item(Name, Quantity, Cost) ->
Row = #shop{item=Name, quantity=Quantity, cost=Cost},
F = fun() ->
mnesia:write(Row)
end,
mnesia:transaction(F).
do(Q) ->
F = fun() -> qlc:e(Q) end,
{atomic, Val} = mnesia:transaction(F),
Val.
6.編譯並執行
在node1執行資料庫初始化與replicates
(node1@jamestekiMacBook-Pro)3> c('test_mnesia'). (1)
{ok,test_mnesia}
(node1@jamestekiMacBook-Pro)4> test_mnesia:do_this_once(). (2)
stopped
(node1@jamestekiMacBook-Pro)5>
=INFO REPORT==== 10-Sep-2014::15:37:39 ===
application: mnesia
exited: stopped
type: temporary
(node1@jamestekiMacBook-Pro)5>test_mnesia:start(). (3)
ok
(node1@jamestekiMacBook-Pro)6> test_mnesia:reset_tables(). (4)
{atomic,ok}
(node1@jamestekiMacBook-Pro)7> test_mnesia:demo(select_shop). (5)
[{shop,potato,2456,1.2},
{shop,apple,20,2.3},
{shop,orange,100,3.8},
{shop,pear,200,3.6},
{shop,banana,420,4.5}]
(1)編譯test_mnesia
(2)此function會先初始化db,接著建立3個table,當執行完後可以發現2個node裡的當前執行路徑下已自動建立下列資料夾Mnesia.node1@jamestekiMacBook-Pro or Mnesia.node2@jamestekiMacBook-Pro
(3)start mnesia(因為do_this_once()裡最後有再將mnesia做stop)
(4)清除table資料,並插入一些測試資料
(5)將shop table的資料印出,可以看出shop table已有一些資料
7.至node2查詢shop table是否也有跟node1相同的資料
(node2@jamestekiMacBook-Pro)2> c('test_mnesia'). (1)
{ok,test_mnesia}
(node2@jamestekiMacBook-Pro)3> test_mnesia:start(). (2)
ok
(node2@jamestekiMacBook-Pro)4> test_mnesia:demo(select_shop). (3)
[{shop,potato,2456,1.2},
{shop,apple,20,2.3},
{shop,orange,100,3.8},
{shop,pear,200,3.6},
{shop,banana,420,4.5}]
(1)在node2編譯test_mnesia
(2)start mnesia(註:不需要再初始化db了,因為在node1已經執行了會將table、資料同步過來)
(3)查詢table資料, 由結果可知資料已經有replicates
8.將node1關掉(直接把console關掉),並在node2插入資料
(node2@jamestekiMacBook-Pro)5> test_mnesia:add_shop_item(iphone6, 10, 20). (1)
{atomic,ok}
(node2@jamestekiMacBook-Pro)6> test_mnesia:demo(select_shop). (2)
[{shop,iphone6,10,20},
{shop,potato,2456,1.2},
{shop,apple,20,2.3},
{shop,orange,100,3.8},
{shop,pear,200,3.6},
{shop,banana,420,4.5}]
(1)執行插入一筆記錄 ex:iphone6
(2)查詢看是否有插入成功
9.將node1重新開啟並做連結,最後查看mnesia是否有做replicates
jamestekiMacBook-Pro:~ james$ cd /Users/james/Develop/erlang-test-node/node1
jamestekiMacBook-Pro:node1 james$ erl -sname node1Erlang R16B02 (erts-5.10.3) [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]
Eshell V5.10.3 (abort with ^G)
(node1@jamestekiMacBook-Pro)1> net_adm:ping('node2@jamestekiMacBook-Pro'). (1)
pong
(node1@jamestekiMacBook-Pro)2> nodes(). (2)
['node2@jamestekiMacBook-Pro']
(node1@jamestekiMacBook-Pro)3> test_mnesia:start(). (3)
ok
(node1@jamestekiMacBook-Pro)4> test_mnesia:demo(select_shop). (4)
[{shop,iphone6,10,20},
{shop,potato,2456,1.2},
{shop,apple,20,2.3},
{shop,orange,100,3.8},
{shop,pear,200,3.6},
{shop,banana,420,4.5}]
(1) node1啟動後跟node2做連結
(2) 確認是否已經連上node2
(3) start mnesia
(4) 確認資料是否已經replicates
小結
本篇示範在同一台機器上啟動2個node來做replicates,如果要在不同機器上要做replicates的話需要再設定cookie才能work,有興趣的人請再參考相關資料。
參考資料
Programming Erlang, Second Edition.