日常Bug排查-MVCC和for update混用导致读数据不一致

日常Bug排查-MVCC和for update混用导致读数据不一致

前言

日常Bug排查系列都是一些简单Bug的排查。笔者将在这里介绍一些排查Bug的简单技巧,同时顺便积累素材。

Bug现场

又是喜闻乐见的读数据不一致的问题。这次的问题是这样,业务在一个事务中更新A和B两个表的两个数据。但是在另一个事务中只看到了A的更新,而B依旧是更新之前的值。说好的原子性感觉又被打破了。如下图所示:

思路

在将这两个请求的SQL按照时序画出来的时候,笔者立马就明白了相关问题所在。核心就在于数据库是RR隔离级别的,同时业务在查询A的时候使用的是Select for update,在查询B的时候使用的是普通的Select。这么使用的原因可能是觉得所有的查询都需要先查A再查B,那么只需要对A加锁就行,减少了数据库锁的数量。

但是,这里是有一个问题的,就是对B表的查询用的是普通的Select,也就是使用了MySQL的MVCC机制。而MySQL MVCC的默认创建时刻就是事务的第一个不带for update的普通Select(具体原理见笔者的博客https://my.oschina.net/alchemystar/blog/1927425)。那么我们就可以从上面的SQL顺序可以看到,在事务1开始之前就已经创建了视图,此时的视图是A1和B1。那么由于RR,查询B表的普通Select看到的自然是B1,而select for update不走MVCC,于是看到的是A2。如下图所示:

解决方案

让业务对B表的查询也用Select for update即可,相比于不一致增加的一点非热点行锁的性能可以忽略不计。

总结

MVCC和数据库锁两者采用了不同的机制,如果不清楚其中的原理可能会导致不一致的现象出现。同时,在这次的问题中业务对于B表不用锁这样的优化实际上是一个负优化。这再次提醒我们,不要过早优化!

相关推荐
cookqq4 分钟前
踩坑记:MySQL 连接 URL 缺失useCursorFetch参数引发的 Java 内存溢出惨案
java·mysql
莳花微语15 分钟前
记录一次生产中mysql主备延迟问题处理
数据库·mysql
阿拉伯柠檬30 分钟前
MySQL内置函数(二)
linux·数据库·mysql·面试
杜子不疼.32 分钟前
从 0 到 1:基于 Spring Boot 4 + Redis + MySQL 构建高可用电商后端系统
spring boot·redis·mysql
小蜗牛的路34 分钟前
MySQL-连接很慢,10秒钟才有响应、Temporary failure in name resolution
数据库·mysql
程序猿_极客1 小时前
【node期末作业开发】Node.js+MySQL 实现销售信息管理系统的增删改查(附源码)
数据库·mysql·node.js
IT届小白1 小时前
探讨:20 万数据量下ROW_NUMBER和GROUP BY两条 SQL 性能差异分析(查 10 条 / 查所有)
数据库·mysql
wusp19942 小时前
Django 迁移系统全指南:从模型到数据库的魔法之路
数据库·mysql·django
IT教程资源D2 小时前
[N_093]基于springboot,vue的宠物商城
mysql·vue·前后端分离·宠物商城·springboot宠物商城
IT教程资源C2 小时前
(N_093)基于springboot,vue的宠物商城
mysql·vue·前后端分离·宠物商城·springboot宠物商城