分布式锁的三种实现方案

为什么需要分布式锁

  • 传统的 jdk 锁,比如 synchronized, reentrantlock 只能在单机环境中使用
  • 分布式场景下,多个服务器的线程之间得用分布式锁来保证并发安全

实现一个分布式锁需要考虑的点

  1. 锁的基本实现,加锁,释放锁
  2. 锁超时自动释放
  3. 锁续命:业务代码用时超过默认锁超时时间
  4. 只能释放自己的锁:业务代码用时超过默认锁超时时间的基础上,释放了别人的锁
  5. 阻塞等待
  6. 可重入
  7. 单点故障

如何实现一个分布式锁

在这里插入图片描述

  • 可以理解对一个有限资源的有限操作
    • 比如公交车上有一个座位,我先坐下了,那别人就不能坐了,那可以说我占用了这把
    • 分布式环境下,哪里找一个公共的资源呢,常见的就是 mysql, redis, zookeeper,所以常用的分布式锁就是基于这三个实现的

基于数据库的实现

  1. 原理:依赖数据的主键冲突,一个表里的主键数据不能重复
  2. 加锁:insert 一条有主键的数据
  3. 释放锁:delete 这条数据
  4. 评价:实现简单,但不是一个可落地的方案,有很多地方需要手动优化
  5. 存在的问题
    1. 超时失效问题
      1. 如果不设超时自动失效,一旦某个线程加锁后崩溃,那锁就没法释放
      2. 可以搞个定时任务,超时则自动删除该条数据
    2. 无法实现阻塞等待
      1. 其他线程不能阻塞等待锁释放
      2. 不断重试 insert 操作,直至成功,会极大的浪费服务器和数据库资源
    3. 不可重入
      1. 同一个线程不能重复加锁
      2. 可以加个版本号字段,记录加锁次数,类似 aqs 里的 status

基于 zookeeper 实现

  • 待补充

基于 redis 实现

  1. 原理:使用 setnx 结合 expire 或 setex
    1. 为了保证 setnx 和 expire 的原子性,防止一个线程 setnx 后崩溃,导致锁不能释放,使用 setex
  2. 加锁:setnx
  3. 释放锁:del
  4. 锁超时:expire
  5. 存在的问题
    1. 锁续命:可以额外运行一个子线程,检查业务耗时是否超过锁的有效期,如果时间不够了,就盐城锁的有效期。
    2. 只能释放自己的锁:setex 的时候,value 取 uuid+线程id ,释放锁的时候,判断是否是自己的锁。
    3. redis 高可用模式,锁丢失:如果主节点加锁了,但是还没同步给从节点,此时主节点挂了,从节点作为主节点,那锁就丢失了。 Redisson 里 RedLock 可以解决这个问题。

实际项目中好用的轮子

  • 实际的项目可以直接使用现成的框架,框架里都解决了常见的问题
  • 基于 redis 的 Redisson 框架。提供了常用的 阻塞 或 非阻塞 的方式:Redisson.lock()Redisson.tryLock() ,且底层代码解决了锁超时,锁续命 等问题
  • 基于 zookeeper 的 Curator 框架。提供了以下常用的锁
    • InterProcessMutex:分布式可重入排它锁
    • InterProcessSemaphoreMutex:分布式排它锁
    • InterProcessReadWriteLock:分布式读写锁
    • InterProcessMultiLock:将多个锁作为单个实体管理的容器
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值