大白话聊聊mysql的悲观锁

298 阅读4分钟
                      目录

                1、什么是悲观锁

                2、mysql的悲观锁实现

                3.  聊聊数据库悲观锁的用途

                4.  聊聊数据库悲观锁的缺点

大家好,我是四九城最豪横的小耳朵。

今天咱们来用大白话聊聊mysql的悲观锁。

1、什么是悲观锁

比如线程A对某个变量进行修改,在这个修改期间,它持悲观心理,认为其他线程在这个期间,也有可能去修改这个变量,所以它就给变量加个锁,保证在它修改期间,别的线程没法去访问这个变量。这个锁就是悲观锁。

2、mysql的悲观锁实现 假设有这么一个场景。你现在负责一个订单模块,要写一个下单的方法。有一张商品表,商品的状态是 “0 未下单 1 已下单”。 正常的逻辑如图:

但是,如果遇到高并发场景,假设2个用户同时对该商品下单,在线程B执行完步骤1的时候,线程A刚好提交了事务,这个时候其实商品的状态已经是“ 1 已下单” 了 ,但是线程B 不知道,继续执行步骤 2 、步骤 3,就会在订单表中出现对该商品的2次下单记录,造成重复下单的问题了。

这种场景,可以考虑用mysql的悲观锁来处理。 怎么使用mysql的悲观锁呢?

 第一步,关闭数据库的自动提交属性。

要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。 我们可以使用命令设置MySQL为非autocommit模式:

   set autocommit=0;

   第二步,代码中手动开启事务,提交事务;

    第三步,在select * 语句 最后加上 for update。

我们来看改造后的代码逻辑,如图

这样改成之后,假设还是上面的高并发场景,2个用户同时对该商品下单的问题。当线程A先执行完步骤二中的sql"select * fron shop where id = xx and status = 0 for update"后,mysql一看,哦哥们,你这条查询sql末尾有for update啊,它就会对这条数据加上一个锁,除非你的线程A执行完步骤5,把事务提交了,否则线程B执行完步骤1就会卡在那里,一直阻塞着,直到线程A提交了事务,它才能执行步骤2~5。

  1. 聊聊数据库悲观锁的用途

用途其实就是类似上面的场景了。比如你负责的项目,就是一些简单的单体系统,用户量也不大,真的遇到类似的业务场景,你可以考虑用数据库悲观锁来优化你的代码。虽然redis也能实现这种锁的功能,但是你想想,redis也需要维护的,万一挂了怎么办?而且也不是每个公司都有钱随便给你申请服务器的。

  1. 聊聊数据库悲观锁的缺点 虽然数据库悲观锁可以保证强一致性,但是缺点也很多。如果你的项目是分布式的,并且用户量很大,或者用户增长预计很快,那你就不能贸然使用数据库悲观锁。尤其是如果发生死锁问题那你就麻烦了。

简单罗列一些数据库悲观锁的缺点:

  1. 高并发情况下,大量请求进来,会导致大部分请求进行排队,影响数据库稳定性,也会耗费服务的CPU等资源。当获得锁的客户端等待时间过长时,会提示:

[40001][1205] Lock wait timeout exceeded; try restarting transaction。高并发情况下,也会造成占用过多的应用线程,导致业务无法正常响应。

  1. 如果优先获得锁的线程因为某些原因,一直没有释放掉锁,可能会导致死锁的发生。

  2. 锁的长时间不释放,会一直占用数据库连接,可能会将数据库连接池撑爆,影响其他服务。

End

作者简介:豪横的小耳朵,一个豪横的程序员。想和大家一起在技术的世界里豪横,用技术的眼光去看待世界。欢迎扫描下方二维码,持续关注,一大波原创系列文章正在路上