玩转Mysql系列 - 第26篇:聊聊mysql如何实现分布式锁?

本文是Mysql系列的第26篇,介绍了如何使用MySQL实现一个具备重入、超时和自动容错功能的分布式锁。通过乐观锁解决并发修改数据的问题,并提供了具体的建表、工具类代码以及测试案例。文章最后提出了一个思考问题,讨论在锁超时后如何避免并发执行同一业务操作的情况。
摘要由CSDN通过智能技术生成

Mysql系列的目标是:通过这个系列从入门到全面掌握一个高级开发所需要的全部技能。

欢迎大家加我微信itsoku一起交流java、算法、数据库相关技术。

这是Mysql系列第26篇。

本篇我们使用mysql实现一个分布式锁。

分布式锁的功能

  1. 分布式锁使用者位于不同的机器中,锁获取成功之后,才可以对共享资源进行操作

  2. 锁具有重入的功能:即一个使用者可以多次获取某个锁

  3. 获取锁有超时的功能:即在指定的时间内去尝试获取锁,超过了超时时间,如果还未获取成功,则返回获取失败

  4. 能够自动容错,比如:A机器获取锁lock1之后,在释放锁lock1之前,A机器挂了,导致锁lock1未释放,结果会lock1一直被A机器占有着,遇到这种情况时,分布式锁要能够自动解决,可以这么做:持有锁的时候可以加个持有超时时间,超过了这个时间还未释放的,其他机器将有机会获取锁

预备技能:乐观锁

通常我们修改表中一条数据过程如下:


        
        
        
  1. t1:select获取记录 R1
  2. t2:对 R1进行编辑
  3. t3:update R1

我们来看一下上面的过程存在的问题:

如果A、B两个线程同时执行到t1,他们俩看到的R1的数据一样,然后都对R1进行编辑,然后去执行t3,最终2个线程都会更新成功,后面一个线程会把前面一个线程update的结果给覆盖掉,这就是并发修改数据存在的问题。

我们可以在表中新增一个版本号,每次更新数据时候将版本号作为条件,并且每次更新时候版本号+1,过程优化一下,如下:


        
        
        
  1. t1:打开事务start transaction
  2. t2:select获取记录 R1,声明变量v= R1. version
  3. t3:对 R1进行编辑
  4. t4:执行更新操作
  5.     update  R1 set version = version +  1 where user_id=#user_id# and version = #v#;
  6. t5:t4中的update会返回影响的行数,我们将其记录在count中,然后根据count来判断提交还是回滚
  7.      if(count== 1){
  8.          //提交事务
  9.         commit;
  10.     } else{
  11.          //回滚事务
  12.         rollback;
  13.     }

上面重点在于步骤t4,当多个线程同时执行到t1,他们看到的R1是一样的,但是当他们执行到t4的时候,数据库会对update的这行记录加锁,确保并发情况下排队执行,所以只有第一个的update会返回1,其他的update结果会返回0,然后后面会判断count是否为1,进而对事务进行提交或者回滚。可以通过count的值知道修改数据是否成功了。

上面这种方式就乐观锁。我们可以通过乐观锁的方式确保数据并发修改过程中的正确性。

使用mysql实现分布式锁

建表

我们创建一个分布式锁表,如下


        
        
        
  1. DROP  DATABASE  IF  EXISTS javacode2018;
  2. CREATE  DATABASE javacode2018;
  3. USE javacode2018;
  4. DROP  TABLE  IF  EXISTS t_lock;
  5. create table  t_lock(
  6.   lock_key  varchar( 32PRIMARY KEY NOT NULL COMMENT '锁唯一标志',
  7.   request_id  varchar( 64NOT  NULL  DEFAULT  ''  COMMENT  '用来标识请求对象的',
  8.   lock_count  INT  NOT  NULL  DEFAULT  0  COMMENT  '当前上锁次数',
  9.   timeout  BIGINT  NOT  NULL  DEFAULT  0  COMMENT  '锁超时时间',
  10.   version  INT  NOT  NULL  DEFAULT  0  COMMENT  '版本号,每次更新+1'
  11. ) COMMENT  '锁信息表';
分布式锁工具类:

        
        
        
  1. package com. itsoku. sql;
  2. import lombok. Builder;
  3. import lombok. Getter;
  4. import lombok. Setter;
  5. import lombok. extern. slf4j. Slf4j;
  6. import org. junit. Test;
  7. import java. sql.*;
  8. import java. util. Objects;
  9. import java. util. UUID;
  10. import java. util. concurrent. TimeUnit;
  11. /**
  12.  * 工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  13.  * 喜欢的请关注公众号:路人甲Java
  14.  */
  15. @ Slf4j
  16. public  class  LockUtils {
  17.      //将requestid保存在该变量中
  18.      static  ThreadLocal< String> requestIdTL =  new  ThreadLocal<>();
  19.      /**
  20.      * 获取当前线程requestid
  21.      *
  22.      * @return
  23.      */
  24.     public  static  String  getRequestId( ) {
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值