mnesia是erlang自帶的分布式數據庫,基於ets和dets實現的。mnesia兼顧了dets的持久性和ets的高性能,可以自動在多個erlang節點間同步數據庫。最關鍵的是,mnesia實現了事務機制。
mnesia數據庫有一個schema表,保存着數據庫相關的一些信息,例如如何將表保存到磁盤、如何加載這些表、在哪些節點間同步數據。構建集群,關鍵是處理這個schema表。
調用函數mnesia:create_schema(ListOfNodes)可以創建schema,這必須在啟動mnesia之前調用。缺省情況下,schema會保存在節點啟動的當前目錄,可以在應用啟動時修改這個參數erl -name SomeName -mnesia dir where/to/store/table
,或者執行函數application:set_env(mnesia, dir, "where/to/store/table")
.
初始化時構建mnesia集群
必須保證每一個Node都沒有schema,如果有,調用mnesia:delete_schema刪除。
在本地啟動兩個erlang 節點:
erl -name a@127.0.0.1 -mnesia dir '"path1"' -s mnesia
erl -name b@127.0.0.1 -mnesia dir '"path2"' -s mnesia
mnesia啟動后會創建schema的,所以需要先刪除schema,再重新創建。
(a@127.0.0.1)1> mnesia:system_info().
===> System info in version "4.11", debug level = none <===
opt_disc. Directory "path1" is NOT used.
use fallback at restart = false
running db nodes = ['a@127.0.0.1']
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [schema]
disc_copies = []
disc_only_copies = []
[{'a@127.0.0.1',ram_copies}] = [schema]
2 transactions committed, 0 aborted, 0 restarted, 0 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
(b@127.0.0.1)1> mnesia:system_info().
===> System info in version "4.11", debug level = none <===
opt_disc. Directory "path2" is NOT used.
use fallback at restart = false
running db nodes = ['b@127.0.0.1']
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [schema]
disc_copies = []
disc_only_copies = []
[{'b@127.0.0.1',ram_copies}] = [schema]
2 transactions committed, 0 aborted, 0 restarted, 0 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
分別在兩個節點上運行下面的命令刪除schema
mnesia:stop(),
mnesia:delete_schema([node()])
在任意一個節點上(選擇的是a@127.0.0.1
)運行創建schema
mnesia:create_schema([node(), 'b@127.0.0.1']).
之后就可以看到在兩個節點上看到新的schema了
(a@127.0.0.1)7> mnesia:system_info().
===> System info in version "4.11", debug level = none <===
opt_disc. Directory "/Users/zhaoxiaosen/work" is used.
use fallback at restart = true
running db nodes = []
stopped db nodes = ['b@127.0.0.1','a@127.0.0.1']
(b@127.0.0.1)5> mnesia:system_info().
===> System info in version "4.11", debug level = none <===
opt_disc. Directory "/Users/zhaoxiaosen" is used.
use fallback at restart = true
running db nodes = []
stopped db nodes = ['b@127.0.0.1','a@127.0.0.1']
最后分別啟動mnesia。
給已有的mnesia集群添加node
新啟動一個節點c@127.0.0.1
erl -name c@127.0.0.1 -mnesia dir '"path3"' -s mnesia
- 在a節點上運行
mnesia:change_config(extra_db_nodes, ['c@127.0.0.1']).
,將c節點連接到集群上,這時,c只是復制了schema - 在c節點上更改存儲方式,
mnesia:change_table_copy_type(schema, 'c@127.0.0.1', disc_copies).
- 將所有表都同步到c節點上
[{Tb, mnesia:add_table_copy(Tb, node(), Type)}
|| {Tb, [{'a@node', Type}]} <- [{T, mnesia:table_info(T, where_to_commit)}
|| T <- mnesia:system_info(tables)]].
這樣,新的節點就加入到mnesia集群上了。
上面的例子中,在啟動節點時啟動了mnesia,主要是為了測試刪除schema
實例代碼
%%啟動
init_once() ->
mnesia:stop(),
mnesia:create_schema([node()]),
mnesia:start(),
mnesia:create_table(tab1, [{disc_copies, [node()]},
{attributes, record_info(fields, config)}]),
mnesia:create_table(tab2, [{ram_copies, [node()]},
{attributes, record_info(fields, watcher)}]),
mnesia:create_table(tab3, [{ram_copies, [node()]},
{attributes, record_info(fields, version)}]).
%%更改存儲方式
change_table_copy_type([]) ->
ok;
change_table_copy_type([Node | T]) ->
mnesia:change_table_copy_type(schema, Node, disc_copies),
mnesia:change_table_copy_type(tab1, Node, disc_copies),
mnesia:change_table_copy_type(tab2, Node, ram_copies),
mnesia:change_table_copy_type(tab3, Node, ram_copies),
change_table_copy_type(T).
%%集群添加節點
sync_data(Node) when is_atom(Node) ->
sync_data([Node]);
sync_data(Nodes) when is_list(Nodes) ->
mnesia:change_config(extra_db_nodes, Nodes),
change_table_copy_type(Nodes),
add_table_copy(Nodes);
sync_data(_) ->
ok.
%%同步數據
add_table_copy([]) ->
ok;
add_table_copy([Node | T]) ->
[mnesia:add_table_copy(Tb, Node, Type)
|| {Tb, [{_, Type}]} <- [{T1, mnesia:table_info(T1, where_to_commit)}
|| T1 <- mnesia:system_info(tables)]],
add_table_copy(T).
%%事務執行,Q為操作命令
do(Q) ->
F = fun() -> qlc:e(Q) end,
mnesia:transaction(F).
其它常用函數
wait_for_tables(TabList, Timeout) -> ok | {timeout, BadTabList} | {error, Reason}
Some applications need to wait for certain tables to be accessible to do useful work. mnesia:wait_for_tables/2
either hangs until all tables in TabList are accessible, or until timeout is reached.