erlang mnesia事务特性
erlang吧
全部回复
仅看楼主
level 11
a651944226 楼主
介绍下erlang mnesia事务特性,顺便增强些记忆,^_^
2013年06月14日 02点06分 1
level 11
a651944226 楼主

本章描述Mnesia事务系统和事务属性,其使得Mnesia成为一个容错的、分布式的数据库管理系统。
本章的内容也涉及到锁函数,包括表锁(table lock)和粘锁(sticky lock)以及绕开事务机制的替换函数,这些函数被称为“脏操作(dirty operation)”。我们还将描述嵌套事务(nested transaction)。本章包含下列部分:
事务属性,包括原子性,一致性,隔离性,持久性

脏操作
记录名与表名
作业(Activity)概念和多种上下文存取
嵌套事务
模式匹配
迭代
2013年06月14日 02点06分 2
level 11
a651944226 楼主
事务属性
在设计容错和分布式系统时事务是一个重要的工具。Mnesia事务是一种可以将一系列数据库操作作为一个函数块来执行的机制。作为一个事务来运行的函数块被称为函数对象(Fun),它可以对Mnesia记录进行读、写和删除。 Fun作为一个事务,要么提交要么终止,如果事务成功执行,它将在所有相关的节点上复制这个动作,如果出错则终止事务。
2013年06月14日 02点06分 3
level 11
a651944226 楼主
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).
2013年06月14日 02点06分 4
level 11
a651944226 楼主

事务raise(Eno, Raise) - >包括一个Fun,这个Fun被语句mnesia:transaction(F)调用并返回一个值。
Mnesia事务系统通过提供以下重要特性来方便构建可信任的、分布式的系统:
事务处理器保证在一个事务中的Fun在对一些表执行一系列的操作时不会干预在其他事物中的操作。
事务处理器保证事务中的操作要么在所有节点上完全原子化的成功执行,要么失败并且对所有节点没有任何影响。
Mnesia事务有四大特性,即原子性(A),一致性(C),隔离性(I)和持久性(D),简写为ACID,这些特性描述如下。
2013年06月14日 02点06分 5
level 11
a651944226 楼主
原子性
原子性意味着在通过事务对数据库实施的改变或者在全部节点上都被执行,或者没有一个节点执行。换言之,事务要么完全成功,要么完全失败。
当我们希望在同一个事务中写多条记录时,原子性特别重要。前述示例中的raise/2函数仅写入一条记录。第2章里的insert_emp/3函数在写记录employee的时候也将与其关联的at_dept和in_proj记录写到数据库里。如果我们在一个事务中运行这些代码,事务处理机制确保事物要么成功完成,要么根本就不执行。
Mnesia是一个可以将数据复制到多个服务节点上的分布式数据库管理系统。原子性保证一个事务要么在所有的节点都有效,要么没有一个节点有效。
2013年06月14日 02点06分 6
level 11
a651944226 楼主
一致性
一致性保证了事务总是让数据库管理系统保持一致的状态。 例如,当Erlang、Mnesia或者计算机崩溃时,如果有一个写操作正在运行,Mnesia确保不出现数据不一致的情况。
2013年06月14日 02点06分 7
level 11
a651944226 楼主
隔离性
隔离性保证当事务在网络的不同节点上执行时,对相同数据记录的存取操作不会互相干扰。
这使得并发执行raise/2函数成为可能。在并发控制理论里的一个经典问题是“更新丢失问题(lost update problem)” 。
2013年06月14日 02点06分 8
level 11
a651944226 楼主

在出现下列情况时隔离性特别有用:有一个编号为123的雇员和两个进程(P1和P2),这两个并发进程试图给这个雇员加薪,雇员起初的薪水比如说是5。进程P1开始执行,读取该雇员的记录并对其薪水加2 。在这个时点,进程P1由于某种原因暂停而进程P2获得机会运行,P2读取该雇员的记录并对其薪水
加3
,最终写入一条薪水为8的员工记录。 现在P1开始再次运行,写入一条薪水为7的员工记录,于是有效的覆盖和取消了进程P2所执行的工作,P2所作的更新被丢弃。
事务系统使得并发执行的两个或多个进程操作相同记录成为可能。程序员不需要检查更新是否同步,事务处理机制会监督这一点。所有通过事务系统访问数据库的程序都可以认为自己对数据有唯一的访问权限。
2013年06月14日 02点06分 9
level 11
a651944226 楼主
持久性
持久性。事务对数据库管理系统所做的改变是永久的。 一旦事务提交,对数据库所做的任何更改都是持久的——它们被安全的写入磁盘,不会消失或者被损坏。
注意
如果将Mnesia配置为纯内存数据库则持久性不起作用。
2013年06月14日 02点06分 10
level 11
a651944226 楼主
4.2锁
不同的事务管理器使用不同的策略来满足隔离属性。Mnesia使用两阶段锁(two-phase locking)的标准技术,这意味着记录在读写之前被加锁,Mnesia使用5种不同的锁。
2013年06月17日 07点06分 11
level 11
a651944226 楼主

读锁。在记录的副本能被读取之前设置读锁。.
写锁。当事务写一条记录时,首先在这条记录的所有副本上设置写锁。
读表锁。如果事务要扫描整张表来搜索一条记录,那么,对表里的记录一条一条的加锁效率很低也很耗内存(如果表很大,读锁本身会消耗很多空间)。因此,Mnesia可以对表设置读锁。
写表锁。如果事务要写大量的记录到表里,则可以对整张表设置写锁。
粘(
Sticky
)锁。即使设置锁的事务终止后,这些写锁也会一直保留在节点上。
2013年06月17日 07点06分 12
level 11
a651944226 楼主

当事务执行时,Mnesia采取的策略是借助诸如mnesia:read/1这样的函数来获得需要的动态锁。Mnesia会自动加锁和解锁,程序员不必对这些操作编码。
当并发进程对相同的记录进行加锁或解锁时可能出现死锁。Mnesia使用“等待-死亡(wait-die)”策略来解决这个问题。当某个事务尝试加锁时,如果Mnesia怀疑可能出现死锁,就强制该事务释放所有的锁并休眠一段时间。包含在事务中的函数(Fun)将被求值一次或多次。
2013年06月17日 07点06分 13
level 11
a651944226 楼主

由于上述理由,保持传递给mnesia:transaction/1的函数(Fun)中的代码干净很重要,否则会引发一些奇怪的结果,例如,通过事务函数(Fun)发送消息。下面的实例演示了这种情况:
bad_raise(Eno, Raise) -> F = fun() -> [E] = mnesia:read({employee, Eno}), Salary = E#employee.salary + Raise, New = E#employee{salary = Salary}, io:format("Trying to write ... ~n", []), mnesia:write(New) end, mnesia:transaction(F).
这个事务可能会将文本“Trying to write ...”写一千次到终端上。尽管如此,Mnesia会保证每个事务最终会运行。结果,Mnesia不仅仅释放死锁,也释放活锁。
2013年06月17日 07点06分 14
level 11
a651944226 楼主

Mnesia程序员不能区分事务的优先级,所以Mnesia DBMS事务系统不适合硬实时应用。但是Mnesia包括其他软实时特性。
当事务执行时Mnesia动态加锁和解锁,所以执行有事务副作用的代码是很危险的。特别是在事务中含有receive语句时会让事务一直挂着而无法返回,这会导致锁不被释放。这种情况会使整个系统停顿,因为其他节点、其他事务的进程会被强制等待这个有问题的事务。
2013年06月17日 07点06分 15
level 11
a651944226 楼主

如果事务异常终止,Mnesia将自动释放该事务的锁。
我们在前面已经示范了一些可以用于事务中的函数。下面会列出可在事务中工作的
最简单的Mnesia函数。重要的是要知道这些函数必须被嵌入到事务中。如果没有封装事务(或其他可用来封装的Mnesia作业)存在,这些函数将失败。
2013年06月17日 07点06分 16
level 11
a651944226 楼主
2013年06月17日 07点06分 17
level 11
a651944226 楼主
4.2.1 粘(Sticky)锁
如上所述,Mnesia使用的锁策略是在读一条记录时锁住该条记录,写一条记录时锁住该条记录的所有副本。但有一些应用使用Mnesia主要是看中了其容错的特点,这些应用可能配置为一个节点承担所有繁重的任务,而另一个备用节点在主节点失败时来接替它。这样的应用使用粘锁来代替普通的锁会更有利。
2013年06月18日 01点06分 18
level 11
a651944226 楼主

粘锁是这样一种锁,在第一次设置这个锁的事务终止后锁依然留在节点的适当位置上。为了演示这一点,假设我们执行下列事务: F = fun() -> mnesia:write(#foo{a = kalle}) end, mnesia:transaction(F).
foo表被复制到N1和N2这两个节点上。
2013年06月18日 01点06分 19
level 11
a651944226 楼主

普通的锁要求:
一个网络远程调用(2条消息)来获取写锁;
三条网络消息来执行两阶段提交协议。
如果我们使用粘锁,必须首先将代码修改如下:
F = fun() -> mnesia:s_write(#foo{a = kalle}) end, mnesia:transaction(F).
2013年06月18日 01点06分 20
1 2 3 尾页