事务的隔离级别通俗演义

 一.隔离级别

在操作数据时可能带来 3 个副作用,分别是脏读、不可重复读、幻读。为了避免这 3 中副作用的发生,在标准的 SQL 语句中定义了 4 种隔离级别,分别是未提交读、已提交读、可重复读、可序列化。而在 spring 事务中提供了 5 种隔离级别来对应在 SQL 中定义的 4 种隔离级别,如下:

隔离级别

意义

ISOLATION_DEFAULT

使用后端数据库默认的隔离级别

ISOLATION_READ_UNCOMMITTED

允许读取未提交的数据(对应未提交读),可能导致脏读、不可重复读、幻读

ISOLATION_READ_COMMITTED

允许在一个事务中读取另一个已经提交的事务中的数据(对应已提交读)。可以避免脏读,但是无法避免不可重复读和幻读

ISOLATION_REPEATABLE_READ

一个事务不可能更新由另一个事务修改但尚未提交(回滚)的数据(对应可重复读)。可以避免脏读和不可重复读,但无法避免幻读

ISOLATION_SERIALIZABLE

这种隔离级别是所有的事务都在一个执行队列中,依次顺序执行,而不是并行(对应可序列化)。可以避免脏读、不可重复读、幻读。但是这种隔离级别效率很低,因此,除非必须,否则不建议使用。

 

Read uncommitted 读未提交

     今天隔壁老王格外开心,因为今天是老婆给他发这个月的零花钱的日子,老婆告诉老王现在打钱,老王看到打到银行卡多了500元,本来每月只有是300,居然这个月发了500,正在窃喜之际,老婆临时想到购物车还没清空,于是回滚事务,改成了300,并提交了。(对应我们所说的脏读 ,两个并发的事务,“事务A:老婆给老王发零花钱”、“事务B:老王查询银行账户”,事务B读取了事务A尚未提交的数据(500元)。当隔离级别设置为Read uncommitted 时,就可能出现脏读,如何避免脏读,请看下一个隔离级别)。

Read committed 读已提交

     情人节快到了,老王带着小三来到一家酒店,前台小姐姐查看系统,显示行卡余额300,老王正在窃喜之际,就在此时,老婆在清空购物车时发现钱不够了,于是把老王卡里的200元通过网上转账划到自己的卡上并提交了,老王提交订单发现系统显示自己余额为100元,再看看银行卡余额不是刚才查询的300而是100元,小三当机提出分手,一气之下摔门而去。(这里对应我们所说的不可重复读,两个并发的事务,“事务A:老王消费”、“事务B:老王的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。即一个事务A读到了另一个事务B已经提交的数据(这里是update),导致事务A,在多次查询的结果不一致)。

Sql Server , Oracle数据库的默认级别就是Read committed。

Repeatable read 重复读

      气急败坏的老王找到银行要讨个说法,柜台的小姐姐将老王的银行卡设置改了下,给老王说:”这下好了,一旦银行系统开始读取你卡上的信息,其他人就不能操作修改卡上的余额了(解决了不可重复读的问题)”,老王半信半疑的回了家。路上,老王一个劲的给小三道歉,并答应现在给小三转100元过去当做赔礼,然而,精明的老婆此时此刻正在另一家银行查看老王银行卡上的消费,发现老王最近没在外面用银行卡花钱,心里想着待会买点菜回去做饭奖励一下老王。另一边边老王的100元已经打给了小三,老婆并告诉柜台将老王的流水打印一下,但是此时流水中居然出现给陌生人转钱100元的字段,老婆有点不解,以为出现了幻觉。(这里对应我们所说的可重复读,还是上集故事两个并发的事务,“事务A:老王消费”、“事务B:老王的老婆网上转账”,事务A事先读取了数据,事务B此时不可以更新数据,而事务A再次读取该数据时,数据没有改变。重复读虽然解决了不可重复读的问题,但是又出现了幻读的问题,即一个事务(老婆查流水)读到了另一个事务(老王给小三转账)已经提交的数据(这里是insert,给表里新添加了一条数据),导致另一个事务(老婆查流水),在事务中多次查询的结果不一致)。

Mysql的默认隔离级别就是Repeatable read。

Serializable 序列化

老王回家后,老婆问出了老王今天下午几点几分到底给谁转账,老王打马虎眼,心里嘀咕,这都知道我几点几分了转账了,莫非是对我起了疑心,应该不会,为了防范万一,于是,第二天去银行又升级银行卡了,柜台告诉他:“升级好了,你的卡已经是最高安全级别了,你在转账时,没转完前,别人不可以查看你的流水”。这下老王放心了,但是谁知小三早已拉黑了老王,并换了手机号。(Serializable 是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读)。

  

 

二. 只读

如果在一个事务中所有关于数据库的操作都是只读的,也就是说,这些操作只读取数据库中的数据,而并不更新数据,那么应将事务设为只读模式( READ_ONLY_MARKER  , 这样更有利于数据库进行优化 

因为只读的优化措施是事务启动后由数据库实施的,因此,只有将那些具有可能启动新事务的传播行为(PROPAGATION_NESTED  PROPAGATION_REQUIRED  PROPAGATION_REQUIRED_NEW) 的方法的事务标记成只读才有意义。

如果使用 Hibernate 作为持久化机制,那么将事务标记为只读后,会将 Hibernate  flush 模式设置为 FULSH_NEVER, 以告诉Hibernate 避免和数据库之间进行不必要的同步,并将所有更新延迟到事务结束。

三.事务超时

如果一个事务长时间运行,这时为了尽量避免浪费系统资源,应为这个事务设置一个有效时间,使其等待数秒后自动回滚。与设

只读属性一样,事务有效属性也需要给那些具有可能启动新事物的传播行为的方法的事务标记成只读才有意义。

四.传播行为定义了被调用方法的事务边界。

 

 

 

传播行为

意义

PROPERGATION_MANDATORY

表示方法必须运行在一个事务中,如果当前事务不存在,就抛出异常

PROPAGATION_NESTED

表示如果当前事务存在,则方法应该运行在一个嵌套事务中。否则,它看起来和PROPAGATION_REQUIRED 看起来没什么俩样

PROPAGATION_NEVER

表示方法不能运行在一个事务中,否则抛出异常

PROPAGATION_NOT_SUPPORTED

表示方法不能运行在一个事务中,如果当前存在一个事务,则该方法将被挂起

PROPAGATION_REQUIRED

表示当前方法必须运行在一个事务中,如果当前存在一个事务,那么该方法运行在这个事务中,否则,将创建一个新的事务

PROPAGATION_REQUIRES_NEW

表示当前方法必须运行在自己的事务中,如果当前存在一个事务,那么这个事务将在该方法运行期间被挂起

PROPAGATION_SUPPORTS

表示当前方法不需要运行在一个是事务中,但如果有一个事务已经存在,该方法也可以运行在这个事务中

 

 

posted @ 2019-06-27 19:14  枫糖浆  阅读(240)  评论(0编辑  收藏  举报