聊聊分布式锁

0 概述

本文将what(是什么、使用场景)、how(如何实现,实现原理)、why (为什么这么实现)来分析下分析下分布式锁。

1 什么是分布式锁

1.1 分布式锁简介

分布式锁是控制分布式系统之间同步访问共享资源的一种方式,分布式锁。分布式锁是解决分布式系统之间进程之间访问共享资源同步机制,本地锁如java lock 是解决一个进程中多线程并发同步机制,它不适于分分布式场景(多进程)。

1.2 分布式锁使用场景

分布式系统幂等设计、秒杀、分布式共享资源访问等等

2 如何实现&实现原理

2.1实现分布式锁条件

1.互斥性:在任意时刻,只有一个客户端(线程)能持有锁。其实也就要求加锁必须是原子性的。
2.不能出现死锁:如何某个客户端在加锁成功系统宕机锁没有主动释放,也要保住后续其它客户端(线程)能继续加锁
3.可靠性:多节点的,不会因为某个节点挂掉导致分布锁无法获取和释放

2.2常见的实现方案
  • 基于数据库表(不推荐使用)
    使用SQL的唯一键或者悲观锁(数据库需要支持事务)

CREATE TABLE `lock_table` (
  `id` bigint(20)  NOT NULL AUTO_INCREMENT COMMENT '主键',
  `key` varchar(64) NOT NULL  COMMENT '锁定的key',
  `desc` varchar(128) NOT NULL  '备注信息',
  `create_time` datetime NOT NULL COMMIT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_key` (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='锁表';

唯一键使用,加锁时候直接插入,捕获唯一键冲突转换成加锁失败。释放锁就直接删除key。问题:1)锁没有失效时间,旦解锁操作失败,就会导致锁记录一直在数据库中(可以考虑做个定时任务,定时清理下或者加锁之前查询一下比较时间)2)不可重入,也可考虑先查询一把

  • 基于缓存(redis、tair)实现,如下图使用redis 命令
    NX 代表只在键不存在时,才对键进行设置操作,PX 9000 设置键的过期时间为9000毫秒,设置过期时间可以防止死锁。
 SET lock_key lock_value NX PX 9000

解锁的过程就是将lock_key键删除,但是不能误删,比如:一个客户端A(线程)加锁成功并在处理中,执行时间过长,锁过期释放了;另一个客户端B(线程)加锁成功,执行过程中,等待A执行完就会B的锁释放。因此在执行删除的时候要判断lock_value 是否相等,可以考虑使用lua 脚本让删除变成一个原子操作。

del lock_key

// lua 脚本
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

基于redis 实现分布式锁比较简单,但是基于redis实现分布式锁是不可重入的,另外redis实现的分布式锁一定可靠吗?
答案是否定的,因为redis 是AP(CAP理论)并不是强一致性的。主从复制是异步的,也就是一个客户端(线程)在master加锁成功后,突然宕机,可能此时slave 上还没有数据;那么此时另一个客户端(线程)就能加锁成功。
为了解决这些问题,redis 作者提出了redlock算法
算法描述
1.获取当前时间毫秒
2.尝试使用相同的key和随机的value为N个实例进行加锁。当为每个实例设置锁的时候,客户端用一个较小的超时时间,相比于锁的的过期时间。比如一个锁的过期时间是10s那么每个客户端加锁的超时时间应该在5-50ms。
3.计算出获取锁所消耗是时间如小于锁的过期时间,并且成功设置锁的实例数>= N/2 + 1,那么加锁成功。如果获取失败,要释放所有的实例

redlock 算法也没有从本质上解决掉master 宕机问题导致的锁不可靠,且性能不是不高,目前在企业用到不是特别多。

  • 基于Zookeeper实现分布式锁
    用Zookeeper不能重复创建一个节点的特性来实现一个分布式锁,这看起来和redis实现分布式锁很像。但是Zookeeper是cp 的很好的解决redis 加锁不可靠以及可重入的问题。但是其在性能上&并发控制上上不如使用缓存实现分布式锁且高可用上,其在选主的过程中其是不可用的。

可以直接使用zookeeper第三方库Curator客户端,这个客户端中封装了一个可重入的锁服务。

InterProcessMutex lock = new InterProcessMutex(client, lockPath);
if ( lock.acquire(maxWait, waitUnit) ) 
{
    try 
    {
        // do some work
    }
    finally
    {
        lock.release();
    }
}

3 总结

上面几种实现分布式锁的方式,哪种方式都无法做到完美。就像CAP分布理论只能同时满足其中两个。因此通常需要根据自己的业务场景&基础条件来选择。比如我做交易下单使用缓存做分布式锁+DB层会有写入唯一键做兜底。

参考文献
[1] https://redis.io/topics/distlock
[2]https://www.jianshu.com/p/5d12a01018e1

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下 4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值