要想在a@gin和b@skeppet这两个节点上运行Company数据库,每个节点必须已经具备Mnesia目录并初始化schema 有两种方式指定Mnesia目录 1)通过程序参数来指定
Java代码
?? %erl -mnesia dir '\
%erl -mnesia dir '\
2)如果没有输入命令行参数,则Mnesia使用当前节点的当前工作目录来作为Mnesia目录 以下命令在两个指定节点上运行Company数据库: 1)gin节点上
Java代码
?? gin %erl -sname a -mnesia dir '\
gin %erl -sname a -mnesia dir '\
2)在skeppet节点上
Java代码
?? skeppet %erl -sname b -mnesia dir '\
skeppet %erl -sname b -mnesia dir '\
3)在两个节点之一执行create_schema:
(a@gin1) > mnesia:create_schema([a@gin, b@skeppet]). 4)在两个节点上运行mnesia:start()
5)在两个节点之一执行以下代码来初始化数据库:
Java代码
?? 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,
?? [{ram_copies, [a@gin, b@skeppet]},
?? {attributes, record_info(fields, manager)}]), ?? mnesia:create_table(at_dept,
?? [{ram_copies, [a@gin, b@skeppet]},
?? {attributes, record_info(fields, at_dept)}]), ?? mnesia:create_table(in_proj,
?? [{ram_copies, [a@gin, b@skeppet]},
?? {attributes, record_info(fields, in_proj)}]).
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,
[{ram_copies, [a@gin, b@skeppet]},
{attributes, record_info(fields, manager)}]), mnesia:create_table(at_dept,
[{ram_copies, [a@gin, b@skeppet]}, {attributes, record_info(fields, at_dept)}]), mnesia:create_table(in_proj,
[{ram_copies, [a@gin, b@skeppet]}, {attributes, record_info(fields, in_proj)}]).
数据库只需初始化一次,下次只需mnesia:start()启动即可从硬盘启动系统
mnesia:stop()方法在当前节点上停止Mnesia,start/0和stop/0都在本地Mnesia系统上起作用,没有启动和停止一些节点的方法
启动过程
Mnesia通过调用mnesia:start()来启动 该方法在本地初始化DBMS
配置选择会更改表的位置和加载顺序:
1)表只存储在本地,从本地Mnesia目录初始化
2)根据哪个备份是最新的来决定备份表从本地硬盘还是其它节点的完整表copy来初始化,Mnesia会决定哪份备份是最新的
3)一旦表被加载,则可以被其他节点访问
表的初始化时同步的,如果数据库比较大,则mnesia:start()可能消耗一部分时间 mnesia:wait_for_table(TabList, Timeout)会等待表的加载
mnesia:force_load_table(Tab)会强制从硬盘加载表而不管其他情况,这时可能远程的备份上的操作会丢失
4,创建新表
Mnesia提供方法mnesia:create_table(Name, ArgList)来创建新表,返回{atomic, ok}或{aborted, Reason} 1)Name是表名
2)ArgList是{Key, Value}的tuple -{type, Type}
Type值为set、ordered_set或bag,默认为set
bag可以一个key对应多条record,而set和ordered_set只能一个key对应一条record Mnesia表中不会出现重复的record(同样的key和content) -{disc_copies, NodeList} 表存储在硬盘上
disc_copies类型的表备份的写操作会将数据写到disc和RAM中的表副本中 如果我们有如下需求:
1)读操作必须很快,直接在RAM操作 2)写操作必须写到硬盘中
那么我们可以一个表放到RAM中,一个disc_copies放到硬盘上
对disc_copies的表的写操作会分两步执行,首先将写操作添加到日志文件中,然后再RAM里执行真正的写操作
-{ram_copies, NodeList} 表存储在RAM里
ram_copies类型的表备份可以用mnesia:dump_tables(TabList)方法来导入到硬盘上 -{disc_only_copies, NodeList}
这种类型的表备份只存储在硬盘上,因此访问比较慢,但是这种类型的表消耗更少的内存 -{index, AttributeNameList}
AttributeNameList指定Mnesia构建和维护的索引名,有一个index表来索引list里的每个元素
Mnesia record的第一个field是key,所以不需要额外的索引 -{snmp, SnmpStruct}
表示该表通过简单网络管理协议(SNMP)来访问 -{local_content, true}
表名对所有Mnesia节点可见,但是内容对每个节点唯一 -{attributes, AtomList} 指定表的字段名
AtomList一般使用record_info(fields, record_name),而是硬编码属性列表,这样更易维护和更健壮,如果以后表结构更改则只用改record
-{record_name, Atom}
指定表记录的common name,表里所有的record都以Atom作为第一个元素 例如我们定义如下record:
Java代码
?? -record(funky, {x, y}).
-record(funky, {x, y}).
那么在两个备份节点和y属性上的一个额外索引的bag类型的表的创建代码:
Java代码
?? mnesia:create_table(funky, [{disc_copies, [N1, N2]}, {index, [y]}, {type, bag}, {attributes, record_info(fields, funky)}]).
mnesia:create_table(funky, [{disc_copies, [N1, N2]}, {index, [y]}, {type, bag}, {attributes, record_info(fields, funky)}]).
而mnesia:create_table(stuff, [])将在本地节点创建一个RAM表stuff,没有额外的索引,并且表的字段为[key,val]
Mnesia用户手册:四,事务和其他访问上下文
本章讲述Mnesia事务系统和事务属性,它们让Mnesia成为一个容错的、分布式的数据库管理系统 讲述内容包括锁(table lock和sticky lock)、如何绕开事务(dirty operation)、嵌套事务(nested transaction):
1)事务属性,包括原子性,一致性,隔离性,持久性(ACID) 2)锁 3)脏操作
4)Record名与Table名
5)activity概念和多种访问上下文 6)嵌套事务 7)模式匹配 8)迭代
1,事务属性
在设计容错和分布式系统时事务是一个重要的工具
Mnesia事务是一种机制,通过它,一些数据库操作可以作为一个功能块来执行
作为一个事务来运行的功能块称为一个Functional Object(Fun),它可以对Mnesia记录进行读、
写和删除
Fun作为一个事务,要么提交要么终止
如果事务成功执行,它将在所有相关的节点上备份,如果事务出错则终止
Java代码
? ? ? ? ? ? ? ?
raise(Eno, Raise) -> F = fun() ->
[E] = mnesia:read(employee, Eno, write), Salary = E#employee.salary + Raise, New = E#employee{salary = Salary}, mnesia:write(New) end,
mnesia:transaction(F).
raise(Eno, Raise) -> F = fun() ->
[E] = mnesia:read(employee, Eno, write), Salary = E#employee.salary + Raise, New = E#employee{salary = Salary}, mnesia:write(New) end,
mnesia:transaction(F).
raise(Eno, Raise)包括一个Fun,这个Fun由mnesia:transaction(F)调用并返回一个值 Mnesia事务系统通过提供以下重要特性来方便构建可信任的、分布式的系统:
1)事务handler保证在一个事务中的Fun在对一些表执行一系列的操作时不会干预在其他事物中的操作
2)事务handler保证事务中的操作要么在所有节点上完全原子的成功执行,要么失败并且对所有节点没有任何影响
3)Mnesia事务有四大特性,原子性,一致性,隔离性和持久性,ACID 原子性
原子性意味着事务要么完全成功,要么完全失败
当我们希望在事务里写多条record时,原子性就特别重要 Mnesia是一个分布式DBMS,数据可以在多个服务器节点上备份 原子性保证一个事务要么对所有的节点都生效,要么一个节点也不生效 一致性
一致性保证了事务一直让DBMS处于一个一致的状态