mysql id能修改吗_你真的懂Mysql吗——Max(id) 引起的业务上破坏 Mysql 事物一致性...

a123bb6e0d87b55ee94a5b30ea2cff53.png

场景

业务中有一个日志表,插入数据与同步数据查询强依赖于主键的有序性

数据同步时携带上次同步更新的主键,查询到Max(id)之间的数据,只同步增量部分

意味数据只有一次同步机会

表结构如下

fe855f63a83e3ae6d69851409570a14f.png

问题

Mysql 数据隔离级别是RR

讲道理,两个不同的事务来读取数据后一个事务 B 是无法读取的到先一个事务 A 数据的数据

但是我们这里有个逻辑很特殊Max(id)

这里有两个场景:

A先读,B后写

假设目前数据最后一条记录是3

A事务携带上次同步主键2来请求,Max(id) = 3

这时B事务前来写数据

A事务先于B事务,根据 Mysql MVCC 视图,查询到了id=2,id=3的数据

同时B事务,写入数据id=4,id=5

下次数据同步携带id=3,继续走,业务无误

图示:

e29622ca6d68b13c895c7f49daaf1145.png

AC先写,B后读

假设目前数据最后一条记录是3

A事务开始写入4

C事务开始写入5

事务还没有结束时,B携带2来读

期望同步2~3之间的数据,实际上却读到了2~5,但是实际上可读的数据还是1 2 3

这是为什么呢?

这就牵扯到了InnoDB 主键自增的规则了

InnoDB 的每个表的自增主键都,保存在内存中
偏移量:auto_increment_offset
步长:auto_increment_increment
默认双1
插入数据时获取该值,+后写入1

因为事务A耗时长,事务C耗时短

导致事务 C 先于事务 A提交

就种情况就是对业务有损了!!

图示:

cfea3dd4d5483a75171607154d7d88bb.png

数据4就被永远的丢失了,阿西吧!!!

天知道我查了多久啊!!!

cb62d2386d773e378cf7766fb31fe616.png

解决方案

1.间隙锁

在数据同步查询前加上select max(1) from table for update;增加间隙锁

阻塞读写,但是会有性能瓶颈

注意:间隙锁的范围要控制好

BadCase

1.select * from table limit 1 for update;

本意是只想锁住(max-1,max](max,sumpernum],减少锁间隙

但是忘记主键索引默认正向有序,这样其实是锁住了(-supernum,1](1,2]

正确的写法是:select * from table order by desc limit 1 for update;

2.select max(1) from table for update;

这种写法虽然满足了锁住表的要求,但是锁的是主键索引,也就是全表

当时考虑1会增加一次 Mysql 运算,希望节省性能,但是这样并发就下去了

2.分布式读写锁

以上的问题经过分析后都不是最好的方案,最后修改为分布式读写锁,具体方案见下一篇文章

九人稚:拿了就能用:Redisson分布式读写锁​zhuanlan.zhihu.com
e77d50190ad6c563a07dd2cc7854704d.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值