前言

大家好,我是飓风,今天我们来聊聊分布式锁的原理、以及基于 mysql 怎么来实现分布式锁。

那么大家现在能不能想一想,分布式锁的使用场景都有哪些呢?下面我列举一些分布式锁的场景:

记住一点,一定是在分布式的环境下,所以肯定是多个服务,或者多个进程来操作一个共享资源。

  1. 扣减库存
  2. 订单支付,检查订单是否进行了重复支付的操作
  3. 缓存击穿/缓存雪崩,防止大并发对 DB 的操作

上面的场景大家有没有发现一个共同点,那就是在分布式的环境下,需要一种机制,来保证对共享资源【库存、支付状态、db 中的某个数据】的互斥访问,那么怎么来达到互斥访问的目的呢,那么就需要分布式锁来实现了。

分布式锁的特性

  • 互斥性

必须,即保证不同节点不同线程的互斥访问。

  • 超时机制

必须,即锁拥有的时长,也就是锁要有超时机制,防止其中一个节点一直拥有该锁造成死锁,那么其他节点,将永远也获取不到锁了,另一种情况就是设置了锁的超时时间,但是业务执行了的时间超过了设置的超时时间,那么锁自动释放了,那么此时互斥性就没了。

  • 提供阻塞和非阻塞接口

必须,所谓阻塞接口,就是没有获取到锁,那么就会一直等待锁的到来,不能中断。

非阻塞接口,就是尝试获取锁,锁没有获取到,就立即返回没有获取到锁,需要自己来重试,同时还可以支持一个获取锁的超时时间,在这个超时时间内没有获取到,那么还是需要自己重试的,减少 cpu 的浪费。

  • 可重入性

可选,当同一个节点同一个线程,想再次获取到该锁的时候,应该直接获取,不用在重新竞争了。

  • 公平锁和非公平锁

可选,这个其实很好理解,获取锁的优先级问题,公平就是排队,非公平就是有优先级,级别高的可以插队。

  • 其他

最好是高可用的锁方案和高性能的锁接口

实现--mysql 唯一索引

利用 mysql 唯一索引的特性,这个唯一的索引列就是分布式环境下互斥的资源,如果某个节点先插入了这个唯一索引对应的列值,那么其他节点就会插入失败,也就是获取锁失败了,也就达到了互斥性。

创建表信息

表信息用户维护一些锁的信息。

CREATE TABLE distributed_lock  (id  bigint(11) NOT NULL AUTO_INCREMENT,lock_resource  varchar(100) NOT NULL DEFAULT '' COMMENT '共享资源,可能某个方法或者数据行',lock_desc  varchar(100) NOT NULL DEFAULT '' COMMENT '锁描述',update_time  datetime NOT NULL,create_time  datetime NOT NULL,PRIMARY KEY (id ),UNIQUE KEY unique_idx_lock_resource (lock_resource )
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

互斥性的实现

利用 mysql 唯一索引来实现,我们要库某个 sku 的扣减库存操作。

获取锁,此时要对 sku=10000 的商品进行扣减库存的 SQL,只要执行成功,那么就获取锁成功了。

INSERT INTO distributed_lock(lock_resource,lock_count,lock_desc,update_time,create_time )VALUES ('10000',1,'库存锁',NOW(),NOW());

释放锁:

DELETE FROM distributed_lock WHERE lock_resource  = '10000';

大家想一想这样,如果按照上面的方式释放锁,会有什么问题?

如果其他节点由于自己的错误原因,误删了这个锁,会怎么样,那么我的任务还没有执行完,我的锁没了,那么其他节点就能获取到该锁,那么互斥性就没了。

所以解决这个问题,就是在删除删除的锁的时候,要知道这是自己的锁,才能删除,如果不是,不会进行锁的删除,也就是锁的释放。

此时 需要给表 加一列为 ' lock_owner' ,也就是锁的拥有者。

DELETE FROM distributed_lock WHERE lock_resource  = '10000' and lock_owner = '';

阻塞等待实现

什么是阻塞呢? 其实阻塞的概念很简单,比如你在进行 io 操作,还没有准备好读或者写操作,那么就需要一直等待,等待就绪,才能开始相应的读写操作,锁也是一样的,如果没有在进行获取锁的时候,获取锁的方法一直没有返回你想要的锁,那么此时你的的程序或者线程需要一直等待,阻塞这里,等待锁的返回。下面我们来看看阻塞等待的代码实现:

@Override
public void lock(String resource) {// 这里通过一个死循环,来模拟了阻塞的实现,lockResource 进行资源的锁定方法 while (!lockResource(resource)){//等待一会再去获取LockSupport.parkNanos(WAIT_TIME);}
}

非阻塞的实现

什么是非阻塞呢?所谓非阻塞,就是你获取锁的时候,没有获取到,也就是获取失败了,不用在一直傻傻等待了,获取锁的方法,会直接告诉你获取锁失败了,要不要继续获取锁,你自己来决定,下面来看下非阻塞的代码实现:

//lockResource 进行资源的锁定方法
@Override
public boolean nonLock(String resource) {return lockResource(resource);
}//加了一个等待获取锁的超时时间
@Override
public boolean nonLock(String resource,long timeout) {long lastTime = System.currentTimeMillis() + timeout;while (!lockResource(resource)){if (System.currentTimeMillis() > lastTime) {return false;}}return true;
}

可重入性的实现

这里的可重入性和你在 java 中的锁 synchronized 和 lock 接口的可重入性的意思是一样,就是如果你已经获取该锁了,如果还需要该锁,那么不需要重新获取,直接获取就可以了,下面是代码的实现:

// 判断是不是自己的锁,如果是更新库内对应 count 的值 + 1
if (Objects.equals(uniqueLock.getLockOwner(),LockUtil.getLockOwner())){return uniqueLockService.incCount(uniqueLock.getId());
}

释放锁的实现

记得一定要释放的是自己的锁哦

@Override
public boolean unLock(String resource) {//这里要注意,释放锁,要保证是自己的锁才可以final UniqueLock uniqueLock = uniqueLockService.findByResource(resource);if (uniqueLock == null) return false;// 是自己的锁,在进行操作 if (Objects.equals(uniqueLock.getLockOwner(),LockUtil.getLockOwner())){// 如果大于 0 说明 count 要减 1if (uniqueLock.getLockCount() > 0){return   uniqueLockService.deCount(uniqueLock.getId());}else {// ==0 ,删除记录就可以了return   uniqueLockService.deleteById(uniqueLock.getId());}}return false;
}

超时时间的实现

锁拥有的最大时间,防止程序出现意外,比如释放锁失败了,而一直拥有该锁,那么其他进程也就无法获取到该锁,那么业务也就无法进行下去了,这里我们可以通过一个定时任务来实现,判断表内,每个锁执行的时间,是否超过该锁设置的最大持有的时长,如果超过了这个时长,就认为锁释放失败了,此时要删除这个锁。

自动续期

没执行完,只要是自己拥有了该锁,那么可以自动续期,如果设置锁的超时时间是 10s 超时,如果 10s 还没有执行完成,那么自动续期的任务,就会检查到超时了,那么就自动续期一个时长如 30s,这里我们可以更新表中该条锁的时长+30s。

只要该锁没有被释放,那么就可以自动续期,所以一定要在你的 finally 中释放锁,来保证锁释放一定会执行。

此时你可能还会疑问,如果在释放锁本身的动作,出现了异常呢?我们该怎么实现呢?

其实也很简单,如果释放锁本身动作出现了异常或者返回失败了,只要我们捕获到该异常或者根据返回标识,然后通知自动续期的任务锁释放失败了,不要续期了,同时也可以帮助我删除掉该锁吧。

总结

今天我们了解了分布式锁以及它的特性。

  • 分布式锁一定是在分布式环境下,对相同的资源进行操作,来会起作用。
  • 分布式锁一定是互斥
  • 分布式锁要提供阻塞和非阻塞实现
  • 分布式锁要有超时时间,不能造成死锁
  • 分布式锁的自动续期,防止业务还没有执行完,锁超时了,结果锁被强制释放了

说明:

这里在说下锁超时和自动续期

自动续期的任务监听到锁没有释放,那么会自动续期锁的超时时间,那么如果还没来的急续期,判断锁超时的任务,判断锁已经超时了,就会强制释放该锁,也就是删除该锁 ,实际上,业务还没有执行完成,所以判断锁超时的任务,可以在锁超时的时长上+5s,一个延迟时间,那么在这个延迟时间内,自动续期的任务就会得到执行,接着更新表中锁的超时时间。

思考:

这里我们没有给锁超时的任务检查代码 和 自动续期的代码实现,大家可以想想怎么去实现?

还有一个思考题,如果没有锁超时的任务检查,只有自动续期的检查,如果释放锁失败了,通知自动续期任务不要进行续期了,同时自动续期任务来释放锁,也就是删除锁,是否可以呢?

源码

快来下载吧

分布式锁的实现- mysql相关推荐

  1. 分布式锁能解决 mysql死锁吗_mysql死锁问题分析

    图4 聚簇索引和二级索引 下面分析下索引和锁的关系. 1)delete from msg where id=2: 由于id是主键,因此直接锁住整行记录即可. 图5 2)delete from msg ...

  2. etcd 笔记(08)— 基于 etcd 实现分布式锁

    1. 为什么需要分布式锁? 在分布式环境下,数据一致性问题一直是个难点.分布式与单机环境最大的不同在于它不是多线程而是多进程.由于多线程可以共享堆内存,因此可以简单地采取内存作为标记存储位置.而多进程 ...

  3. 分布式锁原理及实现方式

    目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们"任何一个分布式系统都无法同时满足一致性(Consistency) ...

  4. 分布式锁的几种实现方式

    目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们"任何一个分布式系统都无法同时满足一致性(Consistency) ...

  5. 基于数据库的分布式锁实现

    一.基于数据库表 要实现分布式锁,最简单的方式可能就是直接创建一张锁表,然后通过操作该表中的数据来实现了.当我们要锁住某个方法或资源的时候,我们就在该表中增加一条记录,想要释放锁的时候就删除这条记录. ...

  6. 分布式锁的三种实现方式_分布式锁的几种实现方式~

    目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们"任何一个分布式系统都无法同时满足一致性(Consistency) ...

  7. 分布式锁的多种实现方式

    转载自 分布式锁的多种实现方式 目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们"任何一个分布式系统都无法同时满足 ...

  8. 分布式锁的三种实现方式_分布式锁的多种实现方式

    目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们"任何一个分布式系统都无法同时满足一致性(Consistency) ...

  9. 分布式锁的几种实现方式~

    目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们"任何一个分布式系统都无法同时满足一致性(Consistency) ...

最新文章

  1. 简单分析beyond作曲
  2. 需求分析的接口需求_再谈需求分析
  3. oracle expdp数据到txt,[20130727]ORACLE 12C使用expdp导出view数据.txt
  4. 深入理解ARM体系架构(S3C6410)---PWM实例
  5. 将一副图片编译进uboot
  6. Mac的「预览」程序如何旋转图片
  7. 问题:脚本之家的几个页面
  8. 四皇后问题(同理适用于n皇后问题)图片版
  9. php项目代码交接文档,接手项目担当运维,前技术团队应该提供哪些正当规范的交接文档和技术支持...
  10. 如何选择SAP培训?
  11. 局域网故障诊断袖珍手册
  12. vagrant下载速度慢的解决方法
  13. Codeblock 美化字体和主题
  14. 《黑白团团队》第七次作业:团队项目设计完善编码
  15. 白话Elasticsearch59-数据建模实战_ Nested Aggregation/ Reverse nested Aggregation对嵌套的博客评论数据进行聚合分析
  16. iOS面试:简历模版(A4纸正反两面)
  17. 腾讯地图javascript API实现地图模糊搜索标记,经纬度输入及点击双向定位
  18. Ubuntu12.04 设置1080P分辨率
  19. 【无标题】手机电脑被木马病毒感染,私生活被全面监控
  20. ubuntu kylin17 安装字体教程

热门文章

  1. LED显示屏8*8点阵的原理详解与汉字代码
  2. 生日快乐的flash
  3. 机械制造技术基础【1】
  4. 2022年全球与中国3D传感器市场现状及未来发展趋势
  5. 【计算机基础】数据库中的数据模型——概念模型、逻辑模型、物理模型
  6. 请编写函数fun,其功能是计算:s= ln(1)+ln(2)+ln(3)+…+ln(m),s作为函数值返回。
  7. kangle利用ep面板配置https
  8. 英式音标26字母(A-G)
  9. 深入探索 Kdump,第 3 部分
  10. 聊天记录误删了有办法找回吗?微信聊天记录误删怎么恢复