最近在写代码调试时,遇到了一个问题。
遇到问题
具体操作如下:
1.调用方法A,并且方法A加上了@Transactional事务注解。
2.在方法A内部,查询并更新某个字段F的值。
3.处理其他逻辑。
4.查询并打印日志,记录关键字段的值,包括字段F。
5.方法A结束。
由于刚刚接手这块代码,而且这个方法又写得很长,所以很多逻辑都没法细看,只能慢慢调试。
我在第4步打了断点,调试时查看日志,感觉数据有问题,将sql复制到数据库里面手动跑了一遍,
发现在方法里和数据库里,执行同一条Sql,结果竟然不一样。
一开始还有点蒙圈,以为是IDE的bug,可能是缓存之类的问题。
然后又调试了几遍,单步调试时观察到手动在数据库里查出来的字段值,总是方法刚开始还未变更时的值。
恍然才明白,原来是事务隔离级别造成的。
事务隔离级别
这些知识点,其实以前面试都背得滚瓜烂熟。现在时间久了,一下子又忘光了。
Q:数据库的隔离级别有哪些?
读未提交(read-uncommitted)读提交(read-committed) 可重复读(repeatable-read)串行化(serializable)。
Q:mysql默认的数据库隔离级别是什么?
可重复读(repeatable-read)
一致性非锁定读
Mysql采用INNODB存储引擎。SELECT操作,使用一致性非锁定读。
在READ COMMITTED事务隔离级别下,对于快照数据,非一致性读总是读取被锁定的行的最新一份快照数据。
在REPEATABLE READ(可重复读)事务隔离级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本。
而我遇到的问题,就是说事务A开启后,变更了数据,还没有提交,然后事务B就去进行查询。
因为默认的REPEATABLE READ事务隔离级别,读取到的是事务开始时的行数据版本。
不同事务隔离级别可能出现的问题
1.脏读
脏读:脏数据是指未提交的数据。脏读是指在不同的事务下,当前事务可以读到另外事务未提交的数据。这显然违反了数据库的隔离性。
脏读发生的事务隔离级别:READ UNCOMMITED。
2.不可重复读
不可重复读:在一个事务内两次读到的数据不一样。比如,A事务还没有结束,B事务对同一数据进行修改,由于B事务的修改,那么A事务两次读到的数据是不一样的。违背了数据库事务一致性的要求。
不可重复读,读到的是已经提交的数据。
不可重复读,发生的隔离级别是READ COMMITTED。
3.幻读:A事务读取了B事务已经提交的新增数据。
4.丢失更新
丢失更新,是指一个事务的更新操作会被另一个事务的更新操作覆盖。
参考资料:《Mysql技术内幕》