不了解事务隔离级别?你的业务代码可能有问题

背景知识

好多开发同学都知道事务有四大特性ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性),但对隔离性的具体内容却不太清楚。

SQL标准的事务隔离级别有4种:读未提交(read uncommitted,简写ru)、读提交(read committed,简写rc)、可重复读(repeatable read,简写rr)、串行化(serializable)。

事务隔离其实是一种并发保护机制。

MySQL的事务隔离是引擎层实现的,InnoDB默认的事务隔离级别是可重复读(reaptable read)。

可重复读意思是一旦我的事务开启了,其他事务的修改我就不管了(读不到了)

问题

如果我们不了解MySQL事务隔离级别和相应事务隔离级别对应的含义,那么我们写的业务代码可能会遇到一些奇怪的问题。一个常见的问题是依赖的数据读不到,程序逻辑出现异常

举例

下面是一个网友向我寻求帮助的一个的问题。

网友的业务,推测是电商的。

场景

一个账户在指定的时间段内消费有最高限额,达到限额就不允许继续消费。

网友的实现

1、账户消费会保存记录到消费记录表。
2、当用户发起消费时,业务流程如下,
①获取用户账户信息(select for update语句,加锁。)
②获取用户指定时间段消费记录表所有记录
③汇总判断是否超过限额,不超过则允许消费,生成消费记录;超过则不允许消费。整个流程在一个事务里。

问题

高并发情况下,消费记录会超过限额。网友疑问,整个流程已经用了数据库行锁,为什么锁不住还是超过限额?

排查

1、通过仔细分析他发给我的日志,我确定是行锁是有生效的。因为日志时间戳清楚地显示并发时2个线程执行事务,在获取账户信息(select for update,数据库行锁)操作上是有先后顺序的。

2、问题关键在②。MySQL事务隔离级别是rr。假设现在有2个事务,事务A执行①就已开启事务,此时事务B执行到①也开启了事务,但因为行锁阻塞了,事务A继续执行②③之后插入消费记录,然后事务A提交;事务B继续执行②,因为事务隔离级别rr,事务B读不到事务A插入的消费记录,也执行③插入消费记录,然后就超限额了。

解决方案

方法1、修改数据库事务隔离级别为成读提交(read commited)。这样事务A提交后,事务B就能读到它插入的记录。

方法2、修改实现。不采用汇总消费记录来控制限额,而是对每个账户每个消费时间段记录一条消费限额记录,记录剩余金额。流程改为先加锁读取限额记录,然后判断限额,插入记录并更新限额记录剩余金额。这样即使在rr隔离级别下,利用行锁,上面事务A和B例子,事务B能读取到最新剩余金额,完成限额控制逻辑。

结果

最终网友使用方法1在自己开发环境验证了问题,但生成环境不能随便更改事务隔离级别,所以采用方法2解决了问题。

结论

一般情况下,不可重复读和幻读是可以接受的,我们的项目使用读提交(read committed)就可以了。可重复读(reaptable read)为了解决幻读问题,引入了间隙锁,会加大死锁的概率,降低系统并发效率。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值