事务引发的"血案"见的多了也麻木了,这回遇到个事务隔离级别的"案子",坑了我小半天的时间...也怪自己细节不牢..

敲着代码遇到这么一个怪事情:

class XXXService{
@Transactional
public void demo(){
//一堆业务逻辑
rpc.insertOne(); //dubbo调用远程服务插入一条数据
getOne(); //获取刚才插入的数据 } }

其中getOne()的事务的传播属性是required, 因为dubbo是远程调用,所以实际上返回后插入的数据就已经commit了, 一个事务中commit的数据另一个事务中应该可以读取到...本来是这样的,但是呢getOne()方法中却读取不到新插入的数据..

倘使如果不是用dubbo远程调用插入数据的话, 我可能还不会误入歧途, 因为数据库是本地自己搭建的, 平时也没人改默认配置, 按照以往经验Mysql默认配置的情况上面的例子应该是可以读取到的. 所以我把关注点放到这个rpc调用上去了..浪费了很多时间..反正是折腾了很久, 各种推导测试事务传播,想来想去也应该没问题.后来干脆把事务去掉了,发现正常了,传播属性各种测试基本确定没有任何问题了,那么问题可以确定是在事务隔离性上..饶了一大圈这才刚刚走上正道....

SELECT @@session.tx_isolation;

通过命令查询了一下mysql数据库的隔离配置,发现是 Repeatable Read, 咦, 这个和记忆中的默认配置貌似对不上(这里实际上是我记混了,mysql默认配置的确是这个,Oracle才是Read committed)..赶紧查询一下线上的数据库配置果然不对应该是Read Committed

这下问题明了,由于代码中没有写明隔离级别,所以使用的是mysql配置的隔离级别,而mysql的隔离级别是可重复读,故产生了此次问题.

此次问题做一个小结:

产生原因:

查询数据库的时候会建立一个到数据库的连接, 熟称数据库session, 有事务的情况下, 这一次连接就处于一个事务中, rpc调用远程服务,由于不是本地方法,故无法加入本地事务中,所以可以算作是另一个事务,那么rpc所处的事务插入数据后事务就结束并提交了. 而getOne()方法所处的事务实际上并没有完成,还受到事务配置的约束,又由于没有配置事务的隔离属性,故采用的mysql的隔离配置Repeatable Read, 而这个可重复读的意思就是一个数据可以反复读取, 并且读取到的值不会发生变化, 这实际上就是说当建立此次事务的时候就不会再读取到新的值了.那么在事务中途rpc插入的数据也不在getOne()所处事务可以读取的范围内, 故读取不到. 但是如果隔离的属性是Read Committed的话,则可以读取到已经提交的数据, rpc服务虽然是中途插入的数据,但是由于新插入的数据已经提交了事务,故依然可以被getOne()方法读取到.

 解决方法:

set global transaction isolation level read committed;

最简单的方法是直接变更mysql的隔离配置为read committed,这样就一切归位了.如果无法变更数据库隔离级别也依然有办法,由于隔离配置的生效级别是首先按照程序中配置的级别,其次再看数据库配置的,那么在程序中变更隔离级别也可以.

class XXXService{
@Transactional(isolation = Isolation.READ_COMMITTED)
public void demo(){
//一堆业务逻辑
rpc.insertOne(); //dubbo调用远程服务插入一条数据
getOne(); //获取刚才插入的数据 } }

问题到此就结束了,但是这里有一个问题比较好奇,Reaptable Read是如何实现的呢,感觉像是开启事务后就对数据库进行了一个快照一样,但是想想又不可能真这样做,然后百度到了一个介绍mysql mvcc机制的文章.

简单来说就是实际上在每行数据最后有2列隐藏列,一列代表数据的创建版本,一列代表数据的删除版本,列中的值存放的是版本号,而这个版本号就是递增的且和某个事务唯一对应,这样只要查询创建版本小于当前版本,删除版本大于当前版本就可以了,换成白话就是查询在当前版本之前创建,当前版本之后删除的数据,这样就可以保证对于当前版本能查询到的数据的稳定不可变,而对于修改数据的操作,则是拆分为标记原来的数据为删除,并插入一条修改后的数据,这样就完美巧妙的保证修改数据读取的稳定性.

事务隔离级别引发的"血案"的更多相关文章

  1. 30分钟全面解析-SQL事务+隔离级别+阻塞+死锁

    以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化.  本系列主要是针对T-SQL的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础] ...

  2. 事务隔离级别与传播机制,spring+mybatis+atomikos实现分布式事务管理

    1.事务的定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功.其必须遵循四个原则(ACID). 原子性(Atomicity):即事务是不可分割的最小工作单 ...

  3. Spring事务隔离级别与传播机制详解,spring+mybatis+atomikos实现分布式事务管理

    原创说明:本文为本人原创作品,绝非他处转载,转账请注明出处 1.事务的定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功.其必须遵循四个原则(ACID). ...

  4. Sqlserver事务隔离级别详解

    sqlserver存储方式   页    sqlserver是以页的形式存储数据,每个数据页的大小为8KB,sqlserver会把空间分为多个页,sqlserver与数据交互单位最小的io操作就是页级 ...

  5. 事务,Oracle,MySQL及Spring事务隔离级别

    一.什么是事务: 事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败. 二.事务特性(4种): 原子性 (atomicity):强调事务的不可分割:一致性 (consiste ...

  6. SQL Server 事务隔离级别

    一.事务隔离级别控制着事务的如下表现: 读取数据时是否占用锁以及所请求的锁类型. 占用读取锁的时间. 引用其他事务修改的行的读操作是否: 在该行上的排他锁被释放之前阻塞其他事务. 检索在启动语句或事务 ...

  7. SQL Server中锁与事务隔离级别

    SQL Server中的锁分为两类: 共享锁 排它锁 锁的兼容性:事务间锁的相互影响称为锁的兼容性. 锁模式 是否可以持有排它锁 是否可以持有共享锁 已持有排它锁 否 否 已持有共享锁 否 是 SQL ...

  8. 从事务隔离级别谈到Hibernate乐观锁,悲观锁

    数据库的事务,是指作为单个逻辑工作单元执行的一系列操作. 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源.通过将一组相关操作组合为一个要么全部成功要么全部失败的单 ...

  9. 事务以及MySQL事务隔离级别+MySQL引擎的区别

    1.事务的基本要素:ACID 1.原子性(Atomicity): 事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节.事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有 ...

随机推荐

  1. 常见的Java面试题及答案整理

    1. 基础篇 1. 面向对象特征:封装,继承,多态和抽象 封装封装给对象提供了隐藏内部特性和行为的能力.对象提供一些能被其他对象访问的方法来改变它内部的数据.在 Java 当中,有 3 种修饰符: p ...

  2. webpack2的配置属性说明entry,output,state,plugins,node,module,context

    Webpack2配置属性详解 webpack说明 webpack是前端构建的一个核心所在,如果说后端构建就是把高级语言代码编译成机器码,那么前端的构建就是重新组合原有的代码,虽然并不编译成机器码,但实 ...

  3. Android之StrictMode

    1. StrictMode是什么? StrictMode is a developer tool which detects things you might be doing by accident ...

  4. 让browserify接收命令行参数,在打包时parse yml配置文件

    功能需求: 1用browserify把各种js打包成浏览器端的1个bundle.js,含有yml配置文件 约束: 1 yml配置文件不在当前工程里(现在还不知道放哪里,以后也会变),希望在打包时,用命 ...

  5. Python 新建程序

    1.建立一个新的文件夹 2.建一个包 3.建一个程序项目 4.默认模板配置

  6. ios手机域名https协议注意事项

    加载网页版链接框架不能用http 1.下载到本地 2.转换为cdn https

  7. Python 数字(Number)

    Python 数字(Number) Python 数字数据类型用于存储数值. 数据类型是不允许改变的,这就意味着如果改变数字数据类型的值,将重新分配内存空间. 以下实例在变量赋值时 Number 对象 ...

  8. Python 编程快速上手 第十五章 保持时间,计划任务和启动程序

    前言 这一章节的主要内容是: 处理时间类型的数据(使用python 的两个模块: time 和 datetime 来处理) 创建多个线程 (使用 threading 模块来创建多个线程) 进行多个进程 ...

  9. caffe生成log日志

    参考日志: http://blog.csdn.net/sunshine_in_moon/article/details/53529028 http://blog.csdn.net/lishanlu13 ...

  10. Stanford: Creating a Hadoop-2.x project in Eclipse

    Creating a Hadoop-2.x project in Eclipse http://snap.stanford.edu/class/cs246-data-2014/hw0.pdf Hado ...