Mnesia 数据库

Summary: Author: 张亚飞 | 阅读时间: 3 minute read | Published: 2015-09-11
Filed under Categories: LinuxTags: Note,

Erlang Mnesia Database 记录摘要


Mnesia

一个非常悲剧的遗留问题,dest 的容量被限制在了2G,使用 dest 的 Mnesia 单个表也被限制在了2G大小了.(注:部分文章说是4G,但是erlang文档上写着 dest 的大小限制为2G.)

Eshell V4.9 (abort with ^G)
1>
1> mnesia:create_schema([node()]).
ok
2> mnesia:start().
ok
3> mnesia:create_table(funky, []).
{atomic,ok}
4> mnesia:info().
---> Processes holding locks <---
---> Processes waiting for locks <---
---> Pending (remote) transactions <---
---> Active (local) transactions <---
---> Uncertain transactions <---
---> Active tables <---
funky : with 0 records occupying 269 words of mem
schema : with 2 records occupying 353 words of mem
===> System info in version "1.0", debug level = none <===
opt_disc. Directory "/tmp/funky" is used.
use fall-back at restart = false
running db nodes = [nonode@nohost]
stopped db nodes = []
remote = []
ram_copies = [funky]
disc_copies = [schema]
disc_only_copies = []
* [{nonode@nohost,disc_copies}] = [schema]
* [{nonode@nohost,ram_copies}] = [funky]
1 transactions committed, 0 aborted, 0 restarted, 1 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
ok

模式函数

  • mnesia:create_schema(NodeList).

该函数用来初始化一个新的空模式,在Mnesia 启动之前这是一个强制性的必要步骤.Mnesia是一个真正分布式的数据库管理系统,而 模式是一个系统表,它被复制到Mnesia系统的所有节点上.如果NodeList中某一个节点 已经有模式,则该函数会失败.该函数需要NodeList中所有节点上的Mnesia都停止之后 才执行.应用程序只需调用该函数一次,因为通常只需要初始化数据库模式一次.

  • mnesia:delete_schema(DiscNodeList).

该函数在DiscNodeList节点上删除旧的模 式,它也删除所有旧的表和数据.该函数需要在所有数据库节点(db_nodes)上的 Mnesia都停止后才能执行.

  • mnesia:delete_table(Tab).

该函数永久删除表Tab的所有副本.

  • mnesia:clear_table(Tab).

该函数永久删除表Tab的全部记录.

  • mnesia:move_table_copy(Tab, From, To).

该函数将表Tab的拷贝从From节点移 动到To节点.表的存储类型{type}被保留,这样当移动一个RAM表到另一个节点时,在 新节点上也维持一个RAM表.在表移动的过程中仍然可以有事务执行读和写操作.

  • mnesia:add_table_copy(Tab, Node, Type).

该函数在Node节点上创建Tab表的 备份.Type参数必须是ram_copies, disc_copies或者是disc_only_copies. 如果我们加一 19 个系统表schema的拷贝到某个节点上,这意味着我们要Mnesia模式也驻留在那里.这个 动作扩展了组成特定Mnesia系统节点的集合.

  • mnesia:del_table_copy(Tab, Node).

该函数在Node节点上删除Tab表的备份,当 最后一个备份被删除后,表本身也被删除.

  • mnesia:transform_table(Tab, Fun, NewAttributeList, NewRecordName).

该函数改变表Tab中所有记录的格式.它对表里所有记录调用参数Fun指明的函数进行处 理,从表中取得旧的记录类型处理后返回新的纪录类型,表的键(key)可以不被改变.

-record(old, {key, val}). 
-record(new, {key, val, extra}). 

Transformer = fun(X) when record(X, old) ->
    new{key = X#old.key, val = X#old.val, extra = 42} end, 
    {atomic, ok} = mnesia:transform_table(foo, Transformer, record_info(fields, new), new),

Fun的参数也可以是原子ignore,它表示只更新表的元(meta)数据,不推荐使用(因为 它将在元数据和实际数据之间产生矛盾).但有可能用户需要用其在离线时做自己的转换.

  • change_table_copy_type(Tab, Node, ToType).

该函数改变表的存储类型.例如, 将在Node节点上指定的内存类型的表Tab改为磁盘类型的表.

建立数据库的步骤

用一个已知的实例,我们说明怎样在被称为 a@gin 和 b@skeppet 这样两个分开的节点上运行在第2 章描述的公司数据库. 要想在这两个节点上运行公司数据库,每个节点在Mnesia启动前必须有 一个在初始化模式时建立的Mnesia目录.

有两种方式指定Mnesia目录:启动Erlang shell的时候 或 在应用程序脚本中通过一个应用程序参数来指定Mnesia目录.

先前已用过下面的实例为我们的公司数据库创建目录: %erl -mnesia dir ‘“/ldisc/scratch/Mnesia.Company”’ 如果没有输入命令行旗标(flag), 则Mnesia使用当前节点Erlang shell启动时的工作目录作为 Mnesia 目录.

启动我们的公司数据库并使得其运行在两个分开的节点上,我们键入下列命令:

  1. 在节点gin上调用: gin % erl -sname a -mnesia dir ‘“/ldisc/scratch/Mnesia.company”’

  2. 在节点skeppet上调用: skeppet % erl -sname b -mnesia dir ‘“/ldisc/scratch/Mnesia.company”’

  3. 在这两个节点之一上: (a@gin1)>mnesia:create_schema([a@gin, b@skeppet]).

  4. 在两个节点上调用函数mnesia:start().

  5. 在两个节点之一执行以下代码来初始化数据库:

dist_init() ->
  mnesia:create_table(employee, [{ram_copies, [a@gin, b@skeppet]}, {attributes, record_info(fields, employee)}]),
  mnesia:create_table(dept, [{ram_copies, [a@gin, b@skeppet]}, {attributes, record_info(fields, dept)}]),
  mnesia:create_table(project, [{ram_copies, [a@gin, b@skeppet]}, {attributes, record_info(fields, project)}]),
  mnesia:create_table(manager, [{type, bag}, {ram_copies, [a@gin, b@skeppet]}, {attributes, record_info(fields, manager)}]),
  mnesia:create_table(at_dep, [{ram_copies, [a@gin, b@skeppet]}, {attributes, record_info(fields, at_dep)}]),
  mnesia:create_table(in_proj, [{type, bag}, {ram_copies, [a@gin, b@skeppet]}, {attributes, record_info(fields, in_proj)}]).

如上所示,两个目录驻留在不同的节点上. 因为/ldisc/scratch (本地磁盘)存在于这两个不同的节点 上. 通过执行这些命令,我们已经配置了两个Erlang节点并初始化以运行公司数据库. 这些操作仅需 要在设置时做一次,以后这两个节点上的系统将通过调用mnesia:start()来启动系统.


erlang mnesia 常用使用技巧

  • mnesia判断表是否存在
%% 确保已经 mnesia:start(). 
lists:member(Tab, mnesia:system_info(tables)).
  • mnesia检查数据库是否创建
case mnesia:system_info(use_dir) of 
    true -> 
        alread_created; 
    _ -> 
        mnesia:create_schema([node()]) 
    end
  • 改变mnesia表类型
mnesia:change_table_copy_type(person, node(), disc_copies)
  • 遍历mnesia表(效率较高)
lists:foldl(fun(Key, Acc) -> 
    [Result] = mnesia:dirty_read(Tab, Key), 
    [Result|Acc] 
        end, [], mnesia:dirty_all_keys(Tab)).
  • 修改 mnesia 表结构
%% -record(person, {name, age}).
%% 改成
%% -record(person, {name, age, money}).

Fun = fun({person, Name, Age}) ->
    {person, Name, Age, 0}; (X) -> X
    end,
    Attr = record_info(fields, person), %%也就是 [name, age, money]
mnesia:transform_table(db_person, Fun, Attr, person).


Comments

Cor-Ethan, the beverage → www.iirii.com