- 浏览: 2703016 次
- 性别:
- 来自: 北京
-
文章分类
最新评论
-
80后的童年2:
深入浅出MongoDB应用实战开发网盘地址:https://p ...
MongoDB入门教程 -
shliujing:
楼主在不是精通java和php的前提下,请不要妄下结论。
PHP、CakePHP哪凉快哪呆着去 -
安静听歌:
希望可以一给一点点注释
MySQL存储过程之代码块、条件控制、迭代 -
qq287767957:
PHP是全宇宙最强的语言!
PHP、CakePHP哪凉快哪呆着去 -
rryymmoK:
深入浅出MongoDB应用实战开发百度网盘下载:链接:http ...
MongoDB入门教程
本章讲述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作为一个事务,要么提交要么终止
如果事务成功执行,它将在所有相关的节点上备份,如果事务出错则终止
raise(Eno, Raise)包括一个Fun,这个Fun由mnesia:transaction(F)调用并返回一个值
Mnesia事务系统通过提供以下重要特性来方便构建可信任的、分布式的系统:
1)事务handler保证在一个事务中的Fun在对一些表执行一系列的操作时不会干预在其他事物中的操作
2)事务handler保证事务中的操作要么在所有节点上完全原子的成功执行,要么失败并且对所有节点没有任何影响
3)Mnesia事务有四大特性,原子性,一致性,隔离性和持久性,ACID
原子性
原子性意味着事务要么完全成功,要么完全失败
当我们希望在事务里写多条record时,原子性就特别重要
Mnesia是一个分布式DBMS,数据可以在多个服务器节点上备份
原子性保证一个事务要么对所有的节点都生效,要么一个节点也不生效
一致性
一致性保证了事务一直让DBMS处于一个一致的状态
例如,当Erlang或者Mnesia或者computer崩溃时如果有一个写操作正在运行,Mnesia保证了不会出现数据不一致的情况
隔离性
隔离性保证了当事务在网络中不同节点上执行时,对同样的数据record的访问和操作不会互相干扰
这意味着可以并发的执行raise/2方法,在并发控制理论里的一个经典问题是“更新丢失问题(lost update problem)”
如果两个进程想同时给一个employee加薪,这时隔离性就特别有用
例如,employee的初始薪水是5,这时P1开始执行,它读取employee的记录,并给薪水加2
同时,P1由于某种原因暂停了,P2开始运行,读取薪水为5,添加3,这样最终薪水为8
现在P1开始继续运行,结果薪水最终被改为7,P2的工作被擦除了,P2的更新丢失了
而一个事务系统保证同步执行多个操作相同数据的进程是可能的
程序员不需要保证更新是同步的,事务handler会监督这一点,对程序员是透明的
所有通过事务系统访问数据库的程序都可以当作自己是对数据有唯一访问权限的
(译者注:俺发现了数据库的王者!有机会宣扬一下真正好用的数据库是什么样的。“球不是这样踢滴!~”)
持久性
持久性保证事务对DBMS的操作是永久性的
一旦一个事务提交,所有对数据库的更改都必须是持久的——它们被安全的写入硬盘,不会消失或者被损坏
注意,如果将Mnesia配置为纯内存数据库则持久性不起作用
2,锁
不同的事务管理器使用不同的策略来满足隔离属性
Mnesia使用两阶段锁(two-phase locking)标准技术
这意味着记录在读写之前被加锁,Mnesia使用5种不同的锁
1)读锁
在record能被读取之前设置读锁
2)写锁
当事务写一条record时,首先在这条record的所有备份上设置写锁
3)读表锁
如果一个事务扫描整张表来搜索一条record,那么对表里的记录一条一条的加锁效率很低也很耗内存(如果表很大,读锁本身会消耗很多空间)
因此,Mnesia支持对整张表加读锁
4)写表锁
如果事务对表写入大量数据,则可以对整张表设置写锁
5)Sticky锁
位于一个节点上,直到事务结束
当事务执行时,Mnesia使用一个策略来动态获得必要的锁
Mnesia自动加锁和解锁,程序员不用在代码里考虑这些操作
当并发进程对同样的数据操作时会出现死锁的情况
Mnesia使用“等待死亡(wait-die)”策略来解决这种问题
当某个事务尝试加锁时,如果Mnesia怀疑它可能出现死锁,那么该事务就会被强制释放所有锁并休眠一会,然后事务将被再次执行
因此,在事务里的Fun必须很纯净,例如如果事务Fun里传递消息,可能会产生一些很奇怪的结果:
这个事务可能会将“Trying to write ...”写一千次到终端
尽管如此,Mnesia会保证每个事务最终会运行
结果,Mnesia不仅仅释放死锁,也释放活锁
Mnesia程序员不能区分事务的优先级,所以Mnesia DBMS事务系统不适合硬实时应用
但是Mnesia包括其他软实时特性
当事务执行时Mnesia动态加锁和解锁,所以执行有事务副作用的代码是很危险的
特别是一个事务中含有receive语句时容易导致事务一直悬挂着而不会返回,这样就导致锁不被释放
这种场景会导致整个系统停止,因为其他节点的其他进程的其他事务会强制等待这个sb事务
如果事务异常终止,Mnesia将自动释放该事务的锁
如果上面这些例子中的方法不包含在事务中,它们将失败
1)mnesia:transaction(Fun) -> {aborted, Reason} | {atomic, Value}
该方法在一个事务中执行参数Fun这个functional object
2)mnesia:read({Tab, Key}) -> transaction abort | RecordList
该方法使用Key来从Tab中读取record
不管表在哪里,该方法有同样的语义
如果表类型是bag,则read({Tab, Key})可能返回一个非常长的list
如果表类型是set,则list长度为1或者list为[]
3)mnesia:wread({Tab, Key}) -> transaction abort | RecordList
该方法和前面的read/1方法的行为一样,除了它会获取一个写锁而不是一个读锁
如果我们执行的事务中读取一个record,然后修改该record,然后写更新,那么立即设置一个写锁会更高效
如果我们先调用mnesia:read/1,然后调用mnesia:write/1,那么当写操作执行时会马上将读锁升级到写锁
4)mnesia:write(Record) -> transaction abort | ok
该方法写record到数据库
Record参数是一个record实例,该方法返回abort或者ok
5)mnesia:delete({Tab, Key}) -> transaction abort | ok
该方法删除Key对应的所有record
6)mnesia:delete_object(Record) -> transaction abort | ok
该方法删除oid为Record的记录
该方法用来删除一些表类型为bag的记录
Sticky Lock
前面说到,Mnesia读一条record时锁住该record,写一条record时锁住所有该record的备份
尽管如此,有些应用使用Mnesia主要用来保障容错性
这些应用可能配置为一个节点做所有繁重的任务,而另一个备用节点在主节点失败时接替它
这样的应用使用sticky lock可能比使用普通的锁更有用
当首先获取一个锁的事务终止时sticky lock会停留在一个节点上,如以下事务:
foo表备份在N1和N2两个节点上
普通的锁需要:
1)1个网络rpc(2个消息)来获取写锁
2)3个网络消息来执行两阶段提交协议
如果我们使用sticky lock,我们必须首先将代码改为如下:
这段代码使用s_write/1方法而不是write/1方法
s_write/1方法设置sticky锁而不是普通锁
如果表没有备份,sticky锁没有任何特殊的效果
如果表示备份的,则我们在N1节点的一条record上上设置sticky锁,Mnesia将会发现锁已经准备好,而不是用网络操作来获得锁
设置本地锁比设置网络锁更高效,因此sticky锁对于使用备份表并且大部分工作都只在其中一个节点上操作的应用更有利
如果N1节点上的一条record打开了sticky锁,这时我们对N2节点上相同的record设置sticky锁,则N1上的record会自动释放sticky锁
这种操作开销很大很消耗性能
表锁
Mnesia支持整表的读锁和写锁来作为单挑record锁的补充
Mnesia自动开锁和解锁,程序员不用对这些操作写代码
但是,如果我们在表上设置表锁时,对某一特定表的大量record的读和写会更高效
这样做会对该表的同步事务阻隔,下面的两个方法是读写操作时显式的对表加锁:
另外的获得表锁的方法:
全局锁
如果表有备份,那么写锁一般会需要在所有节点上打开,而读锁则在一个节点上
mnesia:lock/2用来支持表所,但也可以用在不管表如何备份的场景:
该方法对节点list中所有节点的全局资源GlobalKey上加锁
脏操作
在许多应用里,事务的过度开销可能导致性能丢失
脏操作是绕开事务处理并增加事务速度的捷径
脏操作用在许多场景,例如数据报路由应用里,Mnesia存储路由表,每次接受一个包时期的整个事务非常耗时
因此,Mnesia支持处理表时不加事务的方法,这种方式称为脏操作
但是,意识到避免事务处理的过度开销所带来的交换非常重要:
1)原子性和隔离性丢失了
2)如果我们同步使用脏操作来从同一个表读和写record的话隔离性妥协了,因为使用事务来处理数据的Erlang进程没有从隔离性中得到好处
脏操作的优点是它们比相应的事务处理快的多
如果脏操作处理disc_copies、disc_only_copies类型的表,则写到磁盘
如果脏写操作执行在这种表上,Mnesia也保证所有的表备份一起更新
脏操作将保证某种级别的一致性,例如脏操作不可能返回混乱的record,这样每个单独的读或写操作以一个原子的行为执行
所有的脏操作在失败时调用exit({aborted, Reason}),即使在事务中执行以下方法也不会获取锁:
4,Record名与Table名
Mnesia里表的所有record必须有一样的名字,所有的record必须为相同record类型的实例
但是record名没有必要和table名一样
如果表创建时没有指定record_name属性,那么表里所有的record会有和表名一样的record名
如果多个表创建时指定了相同的record_name属性,那么相同名字的record可以存储在多个表中
访问这些指定了record_name的表时不能简单的使用mnesia:write/1和mnesia:s_write/1,而应该使用mnesia:write/3:
5,Activity概念和多种访问上下文
前面说到,mnesia:transaction/1,2,3的参数functional object(Fun)执行以下表操作:
这些操作将在一个涉及到锁、日志、备份、checkpoint、subscription、提交协议等等的事务上下文里执行
但是,这些方法也可以在其他activity上下文里执行
Mnesia支持以下activity访问上下文:
Fun作为参数传给mnesia:sync_transaction(Fun, [,Args])时会在同步事务上下文里执行
同步事务等待所有活动备份提交事务到硬盘才返回
多于在多个节点上执行并且希望确定远程节点更新已被执行的应用,以及联合事务写和dirty_read的应用,sync_transaction特别有用
sync_transaction对于需要频繁和大量可能对其他节点的Mnesia负载过重的更新的应用也有用
6,嵌套事务
事务可以任意嵌套
一个子事务必须运行在和父亲相同的进程里
当子事务终止时,子事务的调用者会得到返回值{aborted, Reason}并且子事务执行的任何任务都会擦除
如果子事务提交,则子事务写入的record会传播到父事务
当子事务终止时不会释放锁
一个嵌套事务序列创建的锁会一直保持直到最顶端的事务终止
而且,嵌套事务所做的更新只传播到父亲可见
不到最顶端事务结束,不会做最终的提交
这样,即使一个嵌套的事务返回{atomic, Val},如果外层的父事务失败,那么嵌套子事务也失败
具有嵌套事务与顶级事务相同语义的能力会让写操作Mnesia表的库函数更容易
该方法需要作为一个事务来调用
现在假设我们希望写一个方法,该方法调用add_subscriber/1方法,并且本身由一个事务上下文保护
通过在另一个事务里简单的调用add_subscriber/1,则创建了一个嵌套事务
7,模式匹配
当不能使用mnesia:read/3方法时,Mnesia提供了一些方法来对record进行模式匹配:
这些方法对Tab表的所有记录匹配一个Pattern
没有必要对整张表执行昂贵的搜索,通过使用索引和pattern的key的绑定值,该方法所做真正的工作可能压缩为一些哈希查询
如果key部分绑定,则使用ordered_set表可能减少搜索空间
8,迭代
Mnesia提供一些方法来迭代表里的所有记录:
这些方法使用Fun方法来对每条record迭代Tab表
Fun有两个参数,第一个是表的一条记录,第二个参数是accumulator,返回值是一个新的accumulator
如找出所有salary小于10的employee:
(译者注:太多了,api的讲解太过冗繁,所以忽略一部分内容)
讲述内容包括锁(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作为一个事务,要么提交要么终止
如果事务成功执行,它将在所有相关的节点上备份,如果事务出错则终止
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处于一个一致的状态
例如,当Erlang或者Mnesia或者computer崩溃时如果有一个写操作正在运行,Mnesia保证了不会出现数据不一致的情况
隔离性
隔离性保证了当事务在网络中不同节点上执行时,对同样的数据record的访问和操作不会互相干扰
这意味着可以并发的执行raise/2方法,在并发控制理论里的一个经典问题是“更新丢失问题(lost update problem)”
如果两个进程想同时给一个employee加薪,这时隔离性就特别有用
例如,employee的初始薪水是5,这时P1开始执行,它读取employee的记录,并给薪水加2
同时,P1由于某种原因暂停了,P2开始运行,读取薪水为5,添加3,这样最终薪水为8
现在P1开始继续运行,结果薪水最终被改为7,P2的工作被擦除了,P2的更新丢失了
而一个事务系统保证同步执行多个操作相同数据的进程是可能的
程序员不需要保证更新是同步的,事务handler会监督这一点,对程序员是透明的
所有通过事务系统访问数据库的程序都可以当作自己是对数据有唯一访问权限的
(译者注:俺发现了数据库的王者!有机会宣扬一下真正好用的数据库是什么样的。“球不是这样踢滴!~”)
持久性
持久性保证事务对DBMS的操作是永久性的
一旦一个事务提交,所有对数据库的更改都必须是持久的——它们被安全的写入硬盘,不会消失或者被损坏
注意,如果将Mnesia配置为纯内存数据库则持久性不起作用
2,锁
不同的事务管理器使用不同的策略来满足隔离属性
Mnesia使用两阶段锁(two-phase locking)标准技术
这意味着记录在读写之前被加锁,Mnesia使用5种不同的锁
1)读锁
在record能被读取之前设置读锁
2)写锁
当事务写一条record时,首先在这条record的所有备份上设置写锁
3)读表锁
如果一个事务扫描整张表来搜索一条record,那么对表里的记录一条一条的加锁效率很低也很耗内存(如果表很大,读锁本身会消耗很多空间)
因此,Mnesia支持对整张表加读锁
4)写表锁
如果事务对表写入大量数据,则可以对整张表设置写锁
5)Sticky锁
位于一个节点上,直到事务结束
当事务执行时,Mnesia使用一个策略来动态获得必要的锁
Mnesia自动加锁和解锁,程序员不用在代码里考虑这些操作
当并发进程对同样的数据操作时会出现死锁的情况
Mnesia使用“等待死亡(wait-die)”策略来解决这种问题
当某个事务尝试加锁时,如果Mnesia怀疑它可能出现死锁,那么该事务就会被强制释放所有锁并休眠一会,然后事务将被再次执行
因此,在事务里的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不仅仅释放死锁,也释放活锁
Mnesia程序员不能区分事务的优先级,所以Mnesia DBMS事务系统不适合硬实时应用
但是Mnesia包括其他软实时特性
当事务执行时Mnesia动态加锁和解锁,所以执行有事务副作用的代码是很危险的
特别是一个事务中含有receive语句时容易导致事务一直悬挂着而不会返回,这样就导致锁不被释放
这种场景会导致整个系统停止,因为其他节点的其他进程的其他事务会强制等待这个sb事务
如果事务异常终止,Mnesia将自动释放该事务的锁
如果上面这些例子中的方法不包含在事务中,它们将失败
1)mnesia:transaction(Fun) -> {aborted, Reason} | {atomic, Value}
该方法在一个事务中执行参数Fun这个functional object
2)mnesia:read({Tab, Key}) -> transaction abort | RecordList
该方法使用Key来从Tab中读取record
不管表在哪里,该方法有同样的语义
如果表类型是bag,则read({Tab, Key})可能返回一个非常长的list
如果表类型是set,则list长度为1或者list为[]
3)mnesia:wread({Tab, Key}) -> transaction abort | RecordList
该方法和前面的read/1方法的行为一样,除了它会获取一个写锁而不是一个读锁
如果我们执行的事务中读取一个record,然后修改该record,然后写更新,那么立即设置一个写锁会更高效
如果我们先调用mnesia:read/1,然后调用mnesia:write/1,那么当写操作执行时会马上将读锁升级到写锁
4)mnesia:write(Record) -> transaction abort | ok
该方法写record到数据库
Record参数是一个record实例,该方法返回abort或者ok
5)mnesia:delete({Tab, Key}) -> transaction abort | ok
该方法删除Key对应的所有record
6)mnesia:delete_object(Record) -> transaction abort | ok
该方法删除oid为Record的记录
该方法用来删除一些表类型为bag的记录
Sticky Lock
前面说到,Mnesia读一条record时锁住该record,写一条record时锁住所有该record的备份
尽管如此,有些应用使用Mnesia主要用来保障容错性
这些应用可能配置为一个节点做所有繁重的任务,而另一个备用节点在主节点失败时接替它
这样的应用使用sticky lock可能比使用普通的锁更有用
当首先获取一个锁的事务终止时sticky lock会停留在一个节点上,如以下事务:
F = fun() -> mnesia:write(#foo{a = kalle}) end, mnesia:transaction(F).
foo表备份在N1和N2两个节点上
普通的锁需要:
1)1个网络rpc(2个消息)来获取写锁
2)3个网络消息来执行两阶段提交协议
如果我们使用sticky lock,我们必须首先将代码改为如下:
F = fun() -> mnesia:s_write(#foo{a = kalle}) end, mneisa:transaction(F).
这段代码使用s_write/1方法而不是write/1方法
s_write/1方法设置sticky锁而不是普通锁
如果表没有备份,sticky锁没有任何特殊的效果
如果表示备份的,则我们在N1节点的一条record上上设置sticky锁,Mnesia将会发现锁已经准备好,而不是用网络操作来获得锁
设置本地锁比设置网络锁更高效,因此sticky锁对于使用备份表并且大部分工作都只在其中一个节点上操作的应用更有利
如果N1节点上的一条record打开了sticky锁,这时我们对N2节点上相同的record设置sticky锁,则N1上的record会自动释放sticky锁
这种操作开销很大很消耗性能
表锁
Mnesia支持整表的读锁和写锁来作为单挑record锁的补充
Mnesia自动开锁和解锁,程序员不用对这些操作写代码
但是,如果我们在表上设置表锁时,对某一特定表的大量record的读和写会更高效
这样做会对该表的同步事务阻隔,下面的两个方法是读写操作时显式的对表加锁:
mnesia:read_lock_table(Tab) mnesia:write_lock_table(Tab)
另外的获得表锁的方法:
mnesia:lock({table, Tab}, read) mnesia:lock({table, Tab}, write)
全局锁
如果表有备份,那么写锁一般会需要在所有节点上打开,而读锁则在一个节点上
mnesia:lock/2用来支持表所,但也可以用在不管表如何备份的场景:
mnesia:lock({global, GlobalKey, Nodes}, LockKind) LockKind ::= read | write | ...
该方法对节点list中所有节点的全局资源GlobalKey上加锁
脏操作
在许多应用里,事务的过度开销可能导致性能丢失
脏操作是绕开事务处理并增加事务速度的捷径
脏操作用在许多场景,例如数据报路由应用里,Mnesia存储路由表,每次接受一个包时期的整个事务非常耗时
因此,Mnesia支持处理表时不加事务的方法,这种方式称为脏操作
但是,意识到避免事务处理的过度开销所带来的交换非常重要:
1)原子性和隔离性丢失了
2)如果我们同步使用脏操作来从同一个表读和写record的话隔离性妥协了,因为使用事务来处理数据的Erlang进程没有从隔离性中得到好处
脏操作的优点是它们比相应的事务处理快的多
如果脏操作处理disc_copies、disc_only_copies类型的表,则写到磁盘
如果脏写操作执行在这种表上,Mnesia也保证所有的表备份一起更新
脏操作将保证某种级别的一致性,例如脏操作不可能返回混乱的record,这样每个单独的读或写操作以一个原子的行为执行
所有的脏操作在失败时调用exit({aborted, Reason}),即使在事务中执行以下方法也不会获取锁:
mnesia:dirty_read({Tab, Key}) mnesia:dirty_write(Record) mnesia:dirty_delete({Tab, Key}) mnesia:dirty_delete_object(Record) mnesia:dirty_first(Tab) mnesia:dirty_next(Tab, Key) mnesia:dirty_last(Tab) mnesia:dirty_prev(Tab, Key) mnesia:dirty_slot(Tab, Slot) mnesia:dirty_update_counter({Tab, Key}, Val) mnesia:dirty_match_object(Pat) mnesia:dirty_select(Tab, Pat) mnesia:dirty_index_match_oject(Pat, Pos) mnesia:dirty_index_read(Tab, SecondaryKey, Pos) mnesia:dirty_all_keys(Tab)
4,Record名与Table名
Mnesia里表的所有record必须有一样的名字,所有的record必须为相同record类型的实例
但是record名没有必要和table名一样
如果表创建时没有指定record_name属性,那么表里所有的record会有和表名一样的record名
如果多个表创建时指定了相同的record_name属性,那么相同名字的record可以存储在多个表中
访问这些指定了record_name的表时不能简单的使用mnesia:write/1和mnesia:s_write/1,而应该使用mnesia:write/3:
mnesia:write(subscriber, #subscriber{}, write) mnesia:write(my_subscriber, #subscriber{}, sticky_write) mnesia:write(your_subscriber, #subscriber{}, write)
5,Activity概念和多种访问上下文
前面说到,mnesia:transaction/1,2,3的参数functional object(Fun)执行以下表操作:
mnesia:write/3 (write/1, s write/1) mnesia:delete/3 (delete/1, s delete/1) mnesia:delete object/3 (delete object/1, s delete object/1) mnesia:read/3 (read/1, wread/1) mnesia:match object/2 (match object/1) mnesia:select/3 (select/2) mnesia:foldl/3 (foldl/4, foldr/3, foldr/4) mnesia:all keys/1 mnesia:index match object/4 (index match object/2) mnesia:index read/3 mnesia:lock/2 (read lock table/1, write lock table/1) mnesia:table_info/2
这些操作将在一个涉及到锁、日志、备份、checkpoint、subscription、提交协议等等的事务上下文里执行
但是,这些方法也可以在其他activity上下文里执行
Mnesia支持以下activity访问上下文:
transaction sync_transaciton async_dirty sync_dirty ets
Fun作为参数传给mnesia:sync_transaction(Fun, [,Args])时会在同步事务上下文里执行
同步事务等待所有活动备份提交事务到硬盘才返回
多于在多个节点上执行并且希望确定远程节点更新已被执行的应用,以及联合事务写和dirty_read的应用,sync_transaction特别有用
sync_transaction对于需要频繁和大量可能对其他节点的Mnesia负载过重的更新的应用也有用
6,嵌套事务
事务可以任意嵌套
一个子事务必须运行在和父亲相同的进程里
当子事务终止时,子事务的调用者会得到返回值{aborted, Reason}并且子事务执行的任何任务都会擦除
如果子事务提交,则子事务写入的record会传播到父事务
当子事务终止时不会释放锁
一个嵌套事务序列创建的锁会一直保持直到最顶端的事务终止
而且,嵌套事务所做的更新只传播到父亲可见
不到最顶端事务结束,不会做最终的提交
这样,即使一个嵌套的事务返回{atomic, Val},如果外层的父事务失败,那么嵌套子事务也失败
具有嵌套事务与顶级事务相同语义的能力会让写操作Mnesia表的库函数更容易
add_subscriber(S) -> mnesia:transaction(fun() -> case mneisa:read( ......
该方法需要作为一个事务来调用
现在假设我们希望写一个方法,该方法调用add_subscriber/1方法,并且本身由一个事务上下文保护
通过在另一个事务里简单的调用add_subscriber/1,则创建了一个嵌套事务
7,模式匹配
当不能使用mnesia:read/3方法时,Mnesia提供了一些方法来对record进行模式匹配:
mnesia:select(Tab, MatchSpecification, LockKind) -> transaction abort | [ObjectList] mnesia:select(Tab, MatchSpecification, NObjects, Lock) -> transaction abort | {[Object], COntinuation} | '$end_of_table' mnesia:select(cont) -> transaction abort | {[Object], COntinuation} | '$end_of_table' mnesia:match_object(Tab, Pattern, LockKind) -> transaction abort | RecordList
这些方法对Tab表的所有记录匹配一个Pattern
没有必要对整张表执行昂贵的搜索,通过使用索引和pattern的key的绑定值,该方法所做真正的工作可能压缩为一些哈希查询
如果key部分绑定,则使用ordered_set表可能减少搜索空间
Pat = #employee{sex = female, _ = '_'}, F = fun() -> mnesia:match_object(Pat) end, Females = mnesia:transaction(F).
8,迭代
Mnesia提供一些方法来迭代表里的所有记录:
mnesia:foldl(Fun, Acc0, Tab) -> NewAcc | transaction abort mnesia:foldr(Fun, Acc0, Tab) -> NewAcc | transaction abort mnesia:foldl(Fun, Acc0, Tab, LockType) -> NewAcc | transaction abort mneisa:foldr(Fun, Acc0, Tab, LockType) -> NewAcc | transaction abort
这些方法使用Fun方法来对每条record迭代Tab表
Fun有两个参数,第一个是表的一条记录,第二个参数是accumulator,返回值是一个新的accumulator
如找出所有salary小于10的employee:
find_low_salaries() -> Constraint = fun(Emp, Acc) when Emp#employee.salary < 10 -> [Emp | Acc]; (_, Acc) -> Acc end, Find = fun() -> mnesia:foldl(Contraint, [], employee) end, mnesia:transaction(Find).
(译者注:太多了,api的讲解太过冗繁,所以忽略一部分内容)
发表评论
-
ECUG III -- Elrang中国用户组第三次活动
2008-11-06 11:00 2283详情请见: http://ecug.org/ 大家也帮忙宣传 ... -
Erlang内存管理和运行模式笔记
2008-09-25 16:40 5425Erlang进程非常轻量级 进程间通过消息传递进行通讯 进程接 ... -
Erlang:一个通用的网络服务器
2008-09-24 16:50 6183原文: Erlang: A Generalized TCP S ... -
Erlang里的make
2008-09-22 17:38 3774Erlang自带一个make工具 我们看一个例子 目录结构: ... -
介绍Erlang里的Record
2008-09-12 15:52 8620原文: Erlang: An Introduction to ... -
Erlang与ActionScript3采用JSON格式进行Socket通讯
2008-09-02 16:37 6337前提: 需要下载as3corelib来为ActionScrip ... -
Erlang的JSON库
2008-09-02 15:40 9385使用下列JSON库: http://www.lshift.ne ... -
Erlang和ActionScript3的Socket通讯
2008-09-02 13:18 2901server.erl -module(server). ... -
Erlang和Ruby的Socket通讯
2008-09-01 22:12 2345server.erl -module(server). ... -
Erlang实现简单Web服务器
2008-09-01 17:59 5808转贴一个简单的Web服务器: httpd.erl %% h ... -
Mnesia用户手册:五,Mnesia高级特性
2008-09-01 17:27 7082本章描述了构建分布式、容错的Mnesia数据库相关的高级特性: ... -
Mnesia用户手册:三,构建Mnesia数据库
2008-08-27 21:46 9280本章详细介绍了设计Mnes ... -
Mnesia用户手册:二,Mnesia快速上手
2008-08-27 14:09 9162本章介绍了Mnesia: 1) ... -
Mnesia用户手册:一,介绍
2008-08-26 15:47 7819Mnesia是一个分布式数据 ... -
OTP Design Principles: Supervisor Behaviour
2008-08-26 00:06 3144Supervisor Behaviour是一个用来实现一个su ... -
gen_event例子:terminal_logger
2008-08-25 16:23 1688定义三个terminal_logger: $$ termina ... -
OTP Design Principles: Gen_Event Behaviour
2008-08-25 16:06 18391,事件处理原则 在OTP里,event manager是一个 ... -
gen_fsm例子:code_lock
2008-08-22 18:35 2176改了一下代码,可以run了: %% code_lock.erl ... -
OTP Design Principles: Gen_Fsm Behaviour
2008-08-22 17:29 18801,有限状态机 FSM,有 ... -
gen_server Hello World
2008-08-22 13:45 1722简单的gen_server Hello World程序 代码 ...
相关推荐
4、事务和其他上下文存取 4.1.事务属性 4.2.锁 4.3.脏操作 4.4.记录名与表 4.5.作业(Activity)概念和多种存取上下文 4.6.嵌套事务 4.7.模式匹配 4.8.迭代 5、其它.Mnesia.特性 5.1.索引 5.2....
4 、事务和其他上下文存取 . . .. . .. . .. . 29 4.1 事务属性 . . .. . .. . .. . 29 4.2 锁 . . .. . .. . .. . 30 4.3 脏操作 . . .. . . .. . 33 4.4 记录名与表 . . .. . .. . . .. . 34 4.5 作业( ...
内容概要:本文详细介绍了在C# Winform环境中实现Socket TCP通信的一种高效方式,即通过封装的服务端和客户端控件来简化开发流程。文中不仅讲解了控件的基本使用方法,如服务端监听、客户端连接、数据传输等核心功能,还探讨了控件内部的工作原理,包括异步通信、事件驱动机制以及线程安全管理等方面。此外,文章还提供了一些典型应用场景的具体实现,如聊天程序、文件传输等,帮助开发者更快地上手并解决实际问题。 适合人群:具有一定C#编程基础,希望快速掌握Socket TCP通信开发的程序员。 使用场景及目标:适用于需要在网络编程中快速搭建稳定可靠的通信系统的项目,旨在提升开发效率,降低开发难度,使开发者能够专注于业务逻辑而非底层通信细节。 其他说明:控件源码公开,便于进一步学习和定制化开发;附带多个应用案例源码,涵盖常见网络通信任务,有助于理解和实践。
内容概要:本文详细解析了欧姆龙CJ2M PLC控制系统的架构及其对12个伺服电机和气缸的控制方法。主要内容涵盖主控程序、手动模式、复位逻辑、定位控制、通讯与HMI交互以及生产计数模块。文中介绍了状态切换逻辑、伺服使能与时序处理、绝对与相对定位、EtherNet/IP通讯协议的应用、以及各种实用的调试技巧和常见问题解决方案。此外,强调了模块化设计思想和异常处理机制的重要性。 适合人群:从事自动化控制领域的工程师和技术人员,尤其是对PLC编程有一定基础并希望深入了解欧姆龙CJ2M系列产品的读者。 使用场景及目标:帮助读者掌握复杂的多轴伺服控制系统的设计思路与实现方法,提高实际项目的开发效率和稳定性。适用于工业生产线、机器人集成等应用场景。 其他说明:文章提供了丰富的实战经验和代码片段,有助于读者更好地理解和应用相关技术和理念。
内容概要:本文介绍了基于QT框架开发的步进电机上位机控制程序,该程序支持串口、TCP、UDP三种通信方式,适用于不同操作系统(Windows、Linux、macOS)。文章详细讲解了各个通信方式的具体实现方法,包括代码示例和相关技术要点。此外,还讨论了跨平台适配、异常处理、线程安全等问题,并提供了实用的开发经验和优化建议。通过这种方式,开发者可以根据实际需求灵活选择最适合的通信方式,提高步进电机控制的精度和效率。 适合人群:具有一定编程基础,尤其是熟悉C++和QT框架的研发人员,以及从事自动化控制系统开发的技术人员。 使用场景及目标:①适用于各种自动化控制项目,如工业生产线、实验室设备等;②帮助开发者掌握跨平台开发技能,提升程序的兼容性和灵活性;③提供详细的代码实现和技术指导,便于快速搭建稳定的步进电机控制系统。 其他说明:文中不仅涵盖了基本的通信实现,还包括一些高级功能,如运动轨迹预测、电机参数自动识别等。同时强调了程序的稳定性和安全性,建议加入异常处理机制和紧急停止功能。
少儿编程scratch项目源代码文件案例素材-回拨电话.zip
少儿编程scratch项目源代码文件案例素材-回声石.zip
内容概要:本文详细介绍了将暗通道先验算法应用于FPGA平台进行图像去雾处理的技术实现过程。首先,作者在Matlab中展示了暗通道先验算法的基本原理和实现方法,包括计算暗通道、获取大气光值以及估算透射率等步骤。随后,重点讨论了如何在Quartus 13.0环境下利用Verilog语言将这些算法转换为硬件电路的具体实现方式,如构建最小值计算模块、大气光估计模块和透射率优化模块。此外,文中还探讨了在浓雾区域和天空区域处理中存在的问题及解决方案,指出了现有实现的局限性和未来的改进方向。 适合人群:从事图像处理、FPGA开发的研究人员和技术爱好者,尤其是对图像去雾算法感兴趣的开发者。 使用场景及目标:适用于希望深入了解暗通道先验算法在FPGA平台上实现的读者,旨在帮助他们掌握相关技术和解决实际应用中的难点。 其他说明:文章不仅提供了详细的理论解释和技术实现细节,还分享了许多实践经验,有助于读者更好地理解和应对可能出现的各种挑战。
内容概要:本文详细介绍了RK3568和RK356X系列处理器的开发资料,包括硬件原理图、PCB设计以及SDK开发。硬件部分提供了两种版本的PCB设计文件(Allegro和PADS),并附有详细的GPIO控制示例代码。软件部分则涵盖了Buildroot和Yocto双环境支持,以及多媒体开发示例,如视频播放功能。此外,还提供了丰富的库文件和开发示例,帮助开发者快速上手。文中还提到了一些常见的调试技巧和注意事项,如DDR初始化、电源配置等。 适合人群:嵌入式系统开发工程师、硬件设计师、软件开发者,尤其是那些希望深入理解和应用RK3568/356X平台的人群。 使用场景及目标:①硬件设计:通过原理图和PCB设计文件,帮助工程师快速构建硬件原型;②软件开发:借助SDK和示例代码,加速应用程序的开发和测试;③调试与优化:提供常见问题的解决方案和调试技巧,提高系统的稳定性和性能。 其他说明:资料总量达34GB,内容详尽全面,适用于从初学者到资深工程师的不同层次用户。建议新手先从外设驱动入手,逐步深入硬件设计和高级功能开发。
内容概要:本文详细介绍了基于MATLAB/Simulink平台搭建Vienna整流器的电压电流双闭环控制系统以及空间矢量脉宽调制(SVPWM)的具体实现方法。首先探讨了电压外环采用带有前馈补偿的PI控制器来稳定直流侧电压,解决了传统PI控制器无法抑制电压波动的问题。接着深入分析了电流内环的设计,通过对比不同坐标系下的控制方式,选择了静止坐标系下的PR控制器以降低总谐波失真率(THD),并加入了谐振项提高基频响应能力。对于SVPWM调制部分,则着重讲解了扇区判断、作用时间和矢量选择等关键技术细节,确保调制波形的质量。此外,文中还分享了许多实用的小技巧,如参数设置、死区补偿及时序安排等方面的经验。 适合人群:从事电力电子研究的技术人员、高校相关专业师生及对Vienna整流器感兴趣的工程爱好者。 使用场景及目标:适用于希望深入了解Vienna整流器内部工作机制的研究者,在进行实验设计或者产品开发过程中可以作为参考资料;同时也为初学者提供了一个完整的项目案例,帮助他们掌握从理论到实践的操作流程。 其他说明:文中提供了大量MATLAB/Simulink代码片段供读者参考学习,强调了实际调试过程中的注意事项,有助于提升读者解决复杂工程问题的能力。
少儿编程scratch项目源代码文件案例素材-光环:致远星火燎原.zip
内容概要:本文详细介绍了如何利用Matlab实现卷积神经网络(CNN)与长短期记忆网络(LSTM)相结合的时间序列分类预测。首先,文章讲解了数据预处理步骤,包括数据生成、标准化以及划分训练集和测试集的方法。然后,重点阐述了CNN-LSTM模型的构建过程,具体涉及卷积层、池化层、LSTM层等关键组件的设计及其参数选择。此外,还讨论了训练选项的设置,如优化器的选择、学习率调度机制等,并提供了训练和评估模型的具体代码示例。最后,针对可能出现的问题提出了多种优化建议,例如调整卷积核大小、增加Dropout层、采用双向LSTM等方法。 适合人群:对时间序列数据分析感兴趣的科研人员、工程师以及希望深入理解深度学习应用于时间序列领域的学生。 使用场景及目标:适用于需要处理带有时空特性的时间序列数据的任务,如金融交易预测、医疗健康监测、工业设备故障诊断等领域。通过构建并优化CNN-LSTM模型,能够提高时间序列分类预测的准确性。 其他说明:文中提供的代码片段可以直接运行于Matlab R2019a及以上版本环境,同时附带了一些实用的小贴士帮助读者更好地理解和应用相关技术。
内容概要:本文介绍了一个使用LabVIEW开发的压力位移监控系统的实现细节。该系统主要用于监控压装过程中压力和位移的变化,通过采集卡或PLC获取数据并在XY图上实时绘制曲线。用户可以通过鼠标在XY图上拖动区域来设定合格范围,系统会自动判断曲线是否超出该区域,并在超出时发出警告。此外,系统还支持数据保存和历史数据回放功能,便于后续分析和调试。文中详细描述了数据采集、鼠标事件处理、曲线判断以及数据存储的具体实现步骤和技术要点。 适合人群:对LabVIEW有一定了解,从事工业自动化、数据采集和监控系统开发的技术人员。 使用场景及目标:适用于需要监控压装过程或其他类似工艺的工厂和实验室,帮助技术人员快速判断产品质量,提高生产效率和质量控制水平。 其他说明:文中提供了详细的代码片段和实现技巧,如坐标转换、事件处理、数据存储等,有助于读者更好地理解和应用LabVIEW进行相关项目的开发。
内容概要:本文介绍了利用LabVIEW进行非标自动化设备开发的一种创新方法——表格驱动开发。这种方法将传统的代码编写转变为通过Excel表格配置参数,从而大幅提高了开发效率和灵活性。文章详细描述了如何通过表格定义硬件配置、逻辑流程、状态机迁移以及变量管理等功能,并展示了具体的代码实现和应用案例。此外,还讨论了该方法的实际效果及其对开发流程的影响。 适合人群:从事非标自动化设备开发的工程师和技术人员,尤其是那些希望提高开发效率、减少重复劳动的人群。 使用场景及目标:适用于需要频繁调整硬件配置和逻辑流程的非标自动化项目。主要目标是通过简化开发流程,缩短开发周期,降低维护成本,使工程师能够专注于更高层次的设计和优化工作。 其他说明:该方法不仅提升了开发效率,还使得硬件兼容性和逻辑迭代变得更加容易。通过将复杂的技术细节封装在表格配置中,即使是新手也能快速上手,而经验丰富的工程师则可以集中精力于系统的性能优化和异常处理。
二维码批量识别工具,借助先进图像识别技术,能快速准确读取大量二维码信息。适用于物流与供应链管理,如库存盘点和货物追踪;可用于资产管理,像固定资产盘点与设备巡检;还能助力数据收集与市场调研,比如问卷调查与活动签到。它能将识别信息导出为 Excel 等常见表格,表格结构清晰,方便用户对海量二维码数据高效采集、整理与分析,大幅提升工作效率
内容概要:本文详细介绍了MADYMO软件在汽车安全仿真领域的应用,涵盖气囊折叠模拟、安全带建模、碰撞仿真等方面。MADYMO将多体动力学与显式有限元计算相结合,提供了高效且精准的解决方案。文中展示了如何利用XML定义气囊折叠路径、Fortran代码实现安全带接触力计算、Python脚本进行参数优化以及混合建模策略的应用。此外,还讨论了MADYMO在处理复杂接触问题、优化仿真效率方面的独特优势。 适合人群:从事汽车安全工程、碰撞仿真研究的专业人士和技术爱好者。 使用场景及目标:适用于需要进行汽车安全性能评估、碰撞测试优化、安全设备设计验证等场景。主要目标是提高仿真精度、缩短开发周期、降低实验成本。 其他说明:MADYMO以其强大的多体动力学和显式有限元耦合能力,在汽车安全仿真领域占据重要地位。通过合理的参数设置和混合建模策略,能够显著提升仿真的可靠性和效率。
本代码用于将zTC1插线板通过自建的mqtt服务器接入homeassistant智慧家居系统。 前提是自己建了mqtt服务器。 安装homeassistant容器之后,在linux操作系统下的/opt/docker/homeassistant/config目录下可以找到configuration.yaml文件,用文本编辑器打开,将本资源的代码加进去。 注意,如果以前曾经添加过mqtt的sensor和switch实体,那么本代码中的sensor或switch就不需要了,将sensor下面的内容合并到以前的sensor下面代码后面,将switch下面的代码合并到以前的switch代码后面。
本研究利用Sen+MK方法分析了特定区域内的ET(蒸散发)趋势,重点评估了使用遥感数据的ET空间变化。该方法结合了Sen斜率估算器和Mann-Kendall(MK)检验,为评估长期趋势提供了稳健的框架,同时考虑了时间变化和统计显著性。 主要过程与结果: 1.ET趋势可视化:研究利用ET数据,通过ET-MK和ET趋势图展示了蒸散发在不同区域的空间和时间变化。这些图通过颜色渐变表示不同的ET水平及其趋势。 2.Mann-Kendall检验:应用MK检验来评估ET趋势的统计显著性。检验结果以二元分类图呈现,标明ET变化的显著性,帮助识别出有显著变化的区域。 3.重分类结果:通过重分类处理,将区域根据ET变化的显著性进行分类,从而聚焦于具有显著变化的区域。这一过程确保分析集中在具有实际意义的发现上。 4.最终输出:最终结果以栅格图和png图的形式呈现,支持各种应用,包括政策规划、水资源管理和土地利用变化分析,这些都是基于详细的时空分析。 ------------------------------------------------------------------- 文件夹构造: data文件夹:原始数据,支持分析的基础数据(MOD16A2H ET数据 宁夏部分)。 results文件夹:分析结果与可视化,展示研究成果。 Sen+MK_optimized.py:主分析脚本,适合批量数据处理和自动化分析。 Sen+MK.ipynb:Jupyter Notebook,复现可视化地图。
内容概要:本文详细介绍了如何使用Matlab实现基于门控循环单元(GRU)网络进行时间序列预测的方法。首先,通过生成带噪声的正弦波数据并进行预处理,将其划分为训练集和测试集。接着,构建了一个包含GRU层的神经网络,并设置了合理的训练参数。训练过程中采用了Adam优化器,并通过调整学习率和批处理大小等参数确保模型的有效收敛。预测阶段使用了滚动预测方法,确保预测结果的准确性。最后,通过可视化展示了预测结果,并讨论了一些常见的陷阱和改进措施。 适合人群:具有一定编程基础和技术背景的研究人员、工程师以及对时间序列预测感兴趣的开发者。 使用场景及目标:适用于需要对未来数据进行预测的各种应用场景,如电力负荷预测、股市走势分析等。主要目标是帮助读者掌握GRU在网络实现中的具体应用,提高时间序列预测的能力。 其他说明:文中提供了详细的代码示例和实践经验分享,有助于读者更好地理解和应用GRU网络进行时间序列预测。同时,还提到一些优化技巧,如数据归一化、调整隐藏单元数等,进一步提升了模型性能。
内容概要:本文详细介绍了如何利用MATLAB实现四旋翼无人机的姿态控制,特别是PID控制算法的应用。首先解释了四旋翼飞控的基本原理,即通过调节四个电机的转速来维持无人机的平衡。然后展示了PID控制器的具体实现代码,强调了增量式PID的特点以及各参数(比例、积分、微分)的作用。文中还讨论了常见的调试技巧,如逐步调整参数、处理积分饱和、使用低通滤波器减少噪声干扰等。此外,作者分享了一些实用的经验,例如根据电池电压动态调整积分参数、通过观察无人机异常行为反向推测参数设置是否合理等。最后,文章提到了仿真工具Simulink的使用,演示了如何通过图形界面进行参数实时调整,使整个调参过程更加直观高效。 适合人群:对无人机控制系统感兴趣的工程师和技术爱好者,尤其是有一定MATLAB基础并希望深入了解PID控制机制的人群。 使用场景及目标:适用于希望掌握四旋翼无人机姿态控制原理及其PID参数调优方法的学习者。目标是在理解理论的基础上,能够独立完成简单的四旋翼无人机姿态控制系统的建模、仿真和参数优化。 其他说明:文章不仅提供了详细的数学公式和代码片段,还穿插了许多实践经验,使得读者不仅能学到理论知识,还能获得宝贵的实操指导。