事务的定义
所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
例如,银行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。所以,应该把它们看成一个事务。
针对上面的描述可以看出,事务的提出主要是为了解决并发情况下保持数据一致性的问题。
事务具有以下4个基本特征。
- Atomic(原子性):
事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。 Consistency(一致性):
只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态。
所谓“合法”,要满足两个方面的要求:
1.数据库内部规则:数据的类型必须正确,数据值必须在规定的范围内等等。
2.应用(业务)规则。例如, 一般情况下银行账务余额不能是负数,信用卡消费不能超过该卡的信用额度等。Isolation(隔离性):
并行事务的修改必须与其他并行事务的修改相互独立。每一个事务在它的修改全部完成之前,对其他的事务是不可见的。- Durability(持久性):事务结束后,事务处理的结果必须能够得到固化。即使系统出现异常
事务的语句
开始事物:BEGIN TRANSACTION
提交事物:COMMIT TRANSACTION
回滚事务:ROLLBACK TRANSACTION
事务的保存点
SAVE TRANSACTION 保存点名称 –自定义保存点的名称和位置
ROLLBACK TRANSACTION 保存点名称–回滚到自定义的保存点
用户在事务内可以声明被称为保存点的标记。保存点将一个大事务划分为较小的片断。之后用户在对事务进行回滚操作时,就可以选择从当前执行位置回滚到事务内的任意一个保存点。
例如用户可以在一系列复杂的更新操作之间插入保存点,如果执行过程中一个语句出现错误,用户可以回滚到错误之前的某个保存点,而不必重新提交所有的语句。
在开发应用程序时也同样可以使用保存点。如果一个过程内包含多个函数,用户可以在每个函数的开始位置创建一个保存点。当一个函数失败时, 就很容易将数据恢复到函数执行之前的状态,回滚后可以修改参数重新调用函数,或执行相关的错误处理。
当事务被回滚到某个保存点后,将释放由被回滚语句使用的锁。其他等待被锁资源的事务就可以继续执行。需要更新被锁数据行的事务也可以继续执行。
将事务回滚到某个保存点的过程
- 回滚指定保存点之后的语句
- 保留指定的保存点,但其后创建的保存点都将被清除
- 释放此保存点后获得的表级锁与行级锁,但之前的数据锁依然保留。
事务与线程
- 线程之间共享同一片资源,而事务共享的则是数据库内部的数据
- 多线程的意义在于并发执行,提高效率;事务并发执行也能提高程序与数据库交互的效率。
数据库的隔离级别
事务的并发显著地改善了性能,但又会引发隔离性的问题。实际中,数据库大多选择牺牲隔离性来换取并发度。
- Read uncommitted(RU):读取未提交的数据(未授权读取),即其他事务已经修改单未commit的数据,这是最低的隔离级别。允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
- Read committed(RC):允许不可重复读取,在一个事务中,对同一个项,前面的读取跟后面的读取结果可能不一样。例如第一次读取时另一个事务的修改还没有提交,第二次读取时已经提交了。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
- Repeatable read(RR):可重复读取,在一个事务中,对同一个项,前面的读取跟后面的读取结果一样。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
- Serializable(S):可串行化。最高的隔离级别。它要求事务事务只能一个接一个地执行。如果仅仅通过“行级锁”是无法实现事务串行化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别的降低可能导致读取到脏数据或事务执行异常
- Lost update(LU) :两个事务同时修改一个数据项,但后一个事务中途失败退出,则对数据项的两个修改可能都丢失。
- Dirty Reads(DR): 一个事务读取某数据项,但另一个事务更新了此数据项却没有提交,这样所有的操作可能都得回滚。
- Non-repeatable Reads(BRR):一个事务对同一数据项的多次读取可能得到不同的结果。
- Second lost updates problem(SLU):无法重复读取的特例,两个并发事务同时读取和修改同一数据项,则后面的修改可能使得前面的修改失效。
- Phantom Reads(PR):也称为幻读,例如在事务执行过程中,由于前面的查询和后面的查询的期间有另外一个事务插入数据,后面的查询结果中未出现的数据。
用例子来说明事务隔离性(搬运)
假设账户A有1000元,B有1000元,C有1000元
1、操作员u1执行一次转账事务m1
从A转移500元到B,再从A的余额中转移50%平均分配到 A B C D E余额中
2、操作员u2执行一次转账事务m2
从B转移1000元到A
3、操作员u3执行一次转账事务m3
从A转移200元到B
4、操作员u4开户D
账户表为T_C,其包含字段为 账户名称cname 余额money 记录为{A,1000},{B,1000}
事务m1的操作包括
读A,读B,
写A,写B,
提交AB,读A,
读C,写C,写C,
提交AC
事务m2的操作包括
读B,读A,
写B,写A,
提交AB
事务m3的操作包括
读A,读B,
写A,写B,
提交AB
事务m4的操作包括
写D,
提交D
1.若未授权读取ReadUncommitted
m1读A,B,写了A但没写B
此时m2不可以写B,可以读取A和B,但是B是脏读。
隔离级别使用了“排他写锁”。
2.若授权读取ReadCommitted
m1读A,B,写了A但没写B
此时m2不可以写B,可以读取A,不能读取B,因为B是脏读。
隔离级别使用了“排他写锁”。
m1读写了A,B,提交AB,
B 此时m1准备第二次A是允许的。
m3提交了A
隔离级别使用了“瞬间共享读锁”。
(但由于第二次读产生了不可重复读的问题,事务1脱力了元自行,因为逻辑上看事务1中被插入了3,影响了A的余额50%的计算。)
3.若可重复读取RepeatableRead
m1读A,B,写了A。
但没写B此时m2不可以写B,可以读取A,不能读取B,因为B是脏读。
隔离级别使用了“排他写锁”。
m1读写了A,B,提交A“B,
m4提交了D此时m3是能读不能写A并更新提交的。
此时m4是能读能能插入D的。隔离级别使用了“共享读锁”。
(和ReadCommitted比RepeatableRead区别对已经提交的事务可以进行读,但不能写,但是同一张表可以插入新的记录。)
4.序列化Serializable
任何事务都只能等前一事务完全执行完再执行。 但是失去了并发性。
参考链接
http://www.jianshu.com/p/eb150b4f7ce0
http://blog.csdn.net/zdwzzu2006/article/details/5947062
http://www.cnblogs.com/fjdingsd/p/5273008.html