引自:http://blog.csdn.net/turkeyzhou/article/details/7636165
1 什么是事务
1.1 我们为什么需要事务
数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作。事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关 操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、 一致性、隔离性和持久性)属性。
上面的解释和描述来自百度百科(http://baike.baidu.com/view/1298364.htm),我们为什么需要事务,通俗点说,就是需要“一组操作或者行为,要么全成功,要么全失败,一荣俱荣,一辱俱辱”。
1.2 事务ACID属性
事务的特性可以定义为四种属性:ACID(atomicity,consistency,isolation,durability)。
l atomicity:原子性,在一个事务中的所有操作,相当于一个原子操作,要么全部成功,要么全部失败。
l consistency:一致性,就是在事务执行前后,对于事务本身的用意而言,数据库中的数据是保持一致的,数据库的一致性是建立在原子性的基础之上的,更多的由编码的程序员保证,最经典的案例是A,B帐号之间的转账。
在整个过程中,对于一致性的定义是AB的账户总额保持事务前后一致,而次数是通过事务的原子性以及事务定义的编码共同来实现的(如果你本意是转账,A减去50而B加上100则不符合一致性本意)。
另一方面则表示,一致性状态表示只有合法的数据才能被写入数据库,如果事务执行的某些操作,违反了数据的一致性规则,则 事务会rollback到之前数据一致的状态,另一方面,如果事务执行成功,则会将数据状态从事务执行前的符合数据一致性规则的一种状态,变迁到事务执行 后的另一种数据符合一致性规则的状态。(Definition: Consistency states that only valid data will be written to thedatabase. If, for some reason, a transaction is executed that violates thedatabase’s consistency rules, the entire transaction will be rolled back andthe database will be restored to a state consistent with those rules. On theother hand, if a transaction successfully executes, it will take the databasefrom one state that is consistent with the rules to another state that is alsoconsistent with the rules.)
l isolation:隔离性,事务的隔离性是指事务和事务之间的数据可见性,这也是本文需要详细介绍的地方。数据库定义了各种隔离级别,以在并发性和数据完整性中权衡。
l durability:持久性,事务完成以后,所有的数据都将持久到数据库中,不会因为其他原因而丢失。
2 事务隔离
2.1 事务同时读写所带来的问题
事务的隔离性是指当多个事务并发同时执行的时候,数据的可见性不同而导致的一些问题,主要问题包括如下四种:
l 更新丢失(同时更新);
当两个事务同时对一条数据进行更新的时候,因为执行顺序交叉,在没有协同的情况,可能导致某一个事务的更新被覆盖,我们称之为更新丢失(这在银行转账中是十分危险和不可接受的)。
l 脏读;
顾名思义,脏读,未提交读,均是指事务在同事读写的过程中间,一个事务读取到另一个事务没有commit的更新,此时,这个更新是否会被正式提交,或者发生异常回滚是不可预料的,所以可以认定此时所读取到的数据为脏数据。
l 不可重复读
不可重复读取主要是因为在一次事务中多次读取某条记录,但因为其他事务的介入,而导致多次读取的值不一致。
l 幻读;
幻读主要在事务A中读取记录数为N条,而事务B插入一条,则事务A再次读取时则变成了N+1条。
3 数据库锁
3.1 锁的类型
如同线程一样,数据库也用锁来保证事务之间的协同工作,在数据库中,锁分为共享锁和排他锁,顾名思义,多个共享锁可以共存,而排它锁则是独占锁,共享锁也称之为读锁,而排它锁也称之为独占锁,写锁。
l 排他锁 被加锁的对象只能被持有锁的事务读取和修改,其他事务无法在该对象上加其他锁,也不能读取和修改该对象
l 共享锁 被加锁的对象可以被持锁事务读取,但是不能被修改,其他事务也可以在上面再加共享锁。
特别的,对共享锁: 如果两个事务对同一个资源上了共享锁,事务A 想更新该数据,那么它必须等待 事务B 释放其共享锁。
3.2 锁粒度
锁的范围和粒度可以分为表锁和行锁,其中表锁为数据库实现,而行锁则由存储引擎实现(如innodb引擎)。
3.3 封锁协议(Locking Protocol)
在运用锁来进行事务之间的协议时,还必须基于一定的规则,如何时加锁,何时释放锁,加锁的粒度以及等等。这些称之为封锁协议,不同的封锁协议定义了不同的隔离级别。不同的隔离级别则解决了之前事务协作锁带来的问题,以便服务在吞吐量和并发性以及事务完整性之间做出权衡。
4 事务隔离级别
针对事务协作锁带来的四大问题,于是根据不同的封锁协议,制定了事务间的隔离级别。其实本质上均是通过锁来干涉事务之间的执行顺序。
4.1 事务协作问题原因极其分析
l 丢失更新
原因:读锁可以共享,而记录常驻内存。
解决方式:读锁升级为独占锁,或者重复读取,cas更新。
l 脏读
执行场景(顺序):事务A在事务B更新数据和回滚过程中间读取记录。
解决方式:当事务B更新数据的时候禁止其他事务对记录进行读取。
加锁方式:事务B将写锁(独占锁)保持到事务结束。
l 不可重复读
执行场景(顺序):事务A第一次读取数据在事务B更新数据并提交之前,第二次读取数据在事务B更新记录并提交之后,导致同一次事务中多次读取同一记录不同。
解决方式:事务A在读取数据和提交事务之间不允许其他事务对数据进行修改。
加锁方式:事务A持有共享锁直至事务结束。
l 幻读
解决方式:数据库将行锁升级为表锁;
4.2 隔离级别及对应封锁规则
l read uncommitted(一级封锁规则)
表示可以接受读取未提交的数据(脏读)。封锁规则为:当事务修改记录时,为记录添加共享锁直至事务结束,在次过程中,其他事务可以读取和更新数据。因此此种隔离级别会带来丢失更新,脏读,重复读,幻读各种问题。
l read commited(二次封锁规则)
表示允许读取提交后的数据,意思是允许(不可重复读)现象,但拒绝脏读。
封锁规则为对应的脏读解决方案,事务将写锁(独占锁)保持到事务结束,而读取数据的时候当读取操作完毕则立即释放读锁。
执行顺序会变成:
l repeatable read(三级封锁协议)
表示可以重复读(意思为不允许不可重复读,真纠结)。封锁规则为,保持共享锁到事务结束。
但因为是行锁,无法避免幻读,另外因为事务读锁可以并存,那是否repeatable read在应对丢失更新的情况方面表现如何呢?
你会发现,因为共享锁和排他锁的释放周期均被放大,导致了资源的死锁,数据库如何解决这种死锁状况呢?如SQL Server在处理这种情况时则是引入了Update锁,当读取数据的时候为记录上获取Update锁,哪个事务先修改,则将锁升级为独占锁从而对该数据 进行修改。而mysql innodb则主要是通过两方面来处理死锁问题:
n 死锁检测;当发现死锁时,将一个事务回退(回滚拥有最少排他行级锁的事务,一种对最易回滚事务的大致估算),令一个事务获得锁完成事务,但是innodb也无法检测所有死锁的问题。
n 死锁超时;可以通过设置innodb_lock_wait_timeout来设置获取锁而等待的超时时间。
l Serialization(四级封锁)
直接对 事务中 所 读取 或者 更改的数据所在的表加表锁,也就是说,其他事务不能 读写 该表中的任何数据。这样所有的 脏读,不可重复读,幻读 ,都得以避免。
5 MySql事务隔离级别设置
在mysql中,可以通过如下命令查询当前事务隔离级别:
SELECT@@global.tx_isolation; //全局
SELECT@@tx_isolation;//当前会话
设置会话级别分别为:
setglobal transaction isolation level read committed;
setsession transaction isolation level read committed;