Mysql事务及事务隔离

1.事务特性

例如:支票表和储蓄表

start transaction;

Select balance from checking where customer_id=12345;

Update checking set balance=balance-100 where customer_id=12345;

Update savings set balance=balance+100 where customer_id=12345;

Commit;

1.1原子性(atomicity)

一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么完全提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。

1.2一致性(consistency)

数据库总是从一个一致的状态转换到另外一个一致性的状态。在上面的例子中,一致性确保了,即使在执行第三、四语句之间时系统崩溃,支票账户中也不会损失100元钱,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。

1.3隔离性(isolation)

通常来说,一个事务所做的修改在最终提交之前,对其他事务是不可见的。在上面的例子中,当执行完第三条语句、第四条语句还未开始时,此时有另外一个账号汇总程序开始运行,则其看到的支票账户的余额并没有被减去100元钱。

Ps:通常来说的解释,read uncommitted级别

1.4持久性(durability)

一旦事务提交,则其所做的修改就会永久保存到数据库中。此致即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且不可能有能做到100%的持久性保证的策略(如果数据库本身就能做到真正的持久性那么备份又怎么能增加持久性呢?)。

2.事务隔离级别

设置事务隔离级别命令:

set Session | GLOBAL TRANSACTION ISOLATION LEVEL read uncommitted | read committed | repeatable read | serializable;

查看当前事务隔离级别:

select @@tx_isolation;

2.1read uncommitted(读未提交)

在read unCommitted级别,事务中的修改,即使没有提交,对其他事务也是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能上来说,Read unCommitted不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。

2.2read committed(读已提交)

大多数数据库系统的默认隔离级别都是Read Committed(Mysql默认的是Repeatable Read)。

Read Committed满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读(nonrepeatable),因为两次执行同样的查询,可能会得到不一样的结果。

2.3repeatable read(可重复读)

Mysql的默认事务隔离级别

Repeatable Read解决了脏读的问题。在该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom Read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row)。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Controller)解决了幻读的问题。

PS:InnoDB采用MVCC来支持高并发,默认级别为Repeatable Read(可重复读),并且通过间隙锁(next-key locking)策略防止幻读的出现。间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的出现。

2.4serializable(可串行化)

Serializable是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单的说,Serializable会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中也很少用到这个事务隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

表2-1:ANSI SQL事务隔离级别

隔离级别脏读可能性不可重复读可能性幻读可能性加锁读

read uncommittedYesYesYesNo

read committedNoYesYesNo

repeatable readNoNoYesNo

serializableNoNoNoYes

3.隔离级别对锁定的影响

3.1读未提交

设置mysql全局隔离级别为read uncommitted:

set GLOBAL TRANSACTION ISOLATION LEVEL read uncommitted;

T1事件顺序T2

start transaction;

update account

set balance=balance-100

where userID=12345;

select balance 

from account

where userID=12345;

rollback;T1和T2开始。

T2查询一个用户账户中余额,等于500

T1将一个账户的余额减去100元,查询账户余额为400

T2再次查询该用户账户余额为400

T1回滚,使得T2第二次查询结果失效start transaction;

select balance 

from account

where userID=12345;

waiting 00:00:30

select balance 

from account

where userID=12345;

commit;

在事务隔离级别为read uncommitted时,

1.允许其它任务读取持有排它锁的行、表,即读取未提交的数据更改。

2.不为正在搜索的行、表应用共享锁。

3.T2所执行的任何事务修改都获取行、表排它锁,并且在修改操作要更改的表锁定阻塞。

3.2读已提交

设置mysql全局隔离级别为read committed:

set GLOBAL TRANSACTION ISOLATION LEVEL read committed;

4.Spring事务传播特性

Propagation:key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:

4.1PROPAGATION_REQUIRED

——支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚。

4.2PROPAGATION_SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行。

4.3PROPAGATION_MANDATORY

——支持当前事务,如果当前没有事务,就抛出异常。

即必须在一个事务中运行,否则,他就要抛出异常。

4.4PROPAGATION_REQUIRES_NEW

——新建事务,如果当前存在事务,把当前事务挂起。

这个就比较绕口了。  比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。

4.5PROPAGATION_NOT_SUPPORTED

——以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED,那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。

4.6PROPAGATION_NEVER

——以非事务方式执行,如果当前存在事务,则抛出异常。

不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED,  而ServiceB.methodB的事务级别是PROPAGATION_NEVER,那么ServiceB.methodB就要抛出异常了。

4.7PROPAGATION_NESTED

理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。而Nested事务的好处是他有一个savepoint。

猜你喜欢

转载自guwq2014.iteye.com/blog/2393422