记录一次线上mysql事务隔离级别引发的思考

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/fanrenxiang/article/details/102650127

books 场景:我们公司在客户方(某证券公司)部署了几套程序,用来监听客户方内网kafka消息(kafka消息是我们公司写入的),一旦监听到消息便即时消费写入到相关的表中(一个主表及6个子表),然后客户部门再同步数据给他们的下游业务部门使用。某天客户来电说:"需要主表增加一个字段,用来存储与其相关联的子表数据,拼成一个json格式存储在主表字段中",原因是下游部门在使用数据的时候发现:比如,某个时刻A主表关联子表查询,子表关联出了3条记录,接着在很短的时间间隔内B再次读取(执行相同的SQL)发现子表关联出了5条记录(给我的感觉就是"幻读"),于是就有了上面客户提的需求。

当然出现这种现象,可以是我们程序写入顺序及事务的隔离级别引起的,也有可以是客户同步给下游部门(定时任务同步的)时候,一个一个表来同步的,也有可能会导致这个问题。

接到电话后,首先想到的是我们程序在往mysql插入数据时代码里显式的使用了@Transactional事务注解,同时客户那边安装的mysql隔离级别是"读已提交",插入多个表,一个一个表插入合理;其次,内心是比较抗拒客户的加字段需求的,因为主表现在1千多万数据,占用了200多G磁盘空间,加字段非常吃力,而且还得停掉程序(大表加字段,你们懂的)。

于是乎就得回顾一下mysql的事务隔离级别知识了:

books 1、mysql的默认隔离级别:

可重复读

books 2、mysql事务的并发问题:

脏读:事务A读取了事务B(未提交)更新后的数据,B事务回滚导致事务A读取到的数据为脏数据

不可重复读:事务A(未提交)读取数据,此时事务B(已提交)更新了数据,导致事务A在事务B提交前后多次读取的结果不一致

幻读:事务A(未提交)读取数据,此时事务B(已提交)插入或删除了某些数据,导致事务A再次读取时发现数据不一致

幻读与不可重复读的现象相似,但是可以看出幻读侧重于insert/delete操作,不可重复读侧重于update操作

books 3、mysql事务隔离级别测试:

(一)读未提交

(1)设置当前事务隔离级别为读未提交,客户端A开启一个事务,然后查询表初始数据

(2)客户端A提交事务事前,客户端B开启新事务更新了order_info表数据,并且B的事务未提交

(3)在客户端A上查询数据,发现读取到了客户端B的更新操作

(4)客户端B设置回滚,客户端A读取的便是脏数据了

(5)再次在客户端A上读取数据,发现又是未更新前的操作,这个时候程序会使用第四步骤读到的脏数据……bug就来了

(二)读已提交

(1)设置当前事务隔离级别为"读已提交",客户端A开启一个事务,然后查询表初始数据

(2)客户端A提交事务事前,客户端B开启新事务更新了order_info表数据,并且B的事务未提交

(3)在客户端A上查询数据,并没有读取到客户端B的更新操作,数据正常

(4)客户端B提交修改操作的事务

(5)客户端此时再次读取数据,读取到了客户端B更改的数据,即体现了不可重复读的含义~

(三)可重复读

(1)设置当前事务隔离级别为"可重复读",客户端A开启一个事务,然后查询表初始数据

(2)客户端A提交事务事前,客户端B开启新事务更新了order_info表数据,并且B的事务已提交,查询表数据

(3)在客户端A(此时未提交A的事务)上查询数据,id=4298的stock并没有变成800-50=750,还是步骤1中的stock=800,体现了可重复读的含义~

(4)客户端A提交事务,再次查询到的数据才是客户端B更新后的stock

(5)如果在步骤4停止,不提交A的事务,此时在客户端A执行 update order_info set stock=stock-50 where id=4298会发现

stock变成的了700而不是750,也就是说客户端A在update时用到了客户端B更新的结果,这是由于:可重复读这种隔离级别在select操作时候会使用快照版本,update、insert、delete时会使用当前实时版本以避免数据不一致性问题(下面是update的情况,delete和insert留给大家自己测试)

(6)接着测试一下可重复读隔离级别的幻读问题,客户端A开启新事务进行更新操作并查询表数据

(7)新开客户端B进行插入操作并提交事务

(8)客户端A再次进行查询操作,没有查到客户端B新插入的三星盖乐世S9记录,没有出现幻读的现象

(四)串行化

(1)设置当前事务隔离级别为"串行化",客户端A开启一个事务,然后查询表初始数据

(2)客户端B设置串行化隔离级别,开启事务并查询、更新数据,发现可以查询但无法更新,因为客户端A的查询事务导致锁表

串行化隔离级别的并发量极低,但是能较好的避免脏读、幻读问题.

books4、spring的4种事务特性、5种隔离级别、7种事务传播行为

猜你喜欢

转载自blog.csdn.net/fanrenxiang/article/details/102650127