30 | 如何使用Redis实现分布式锁?

260 阅读3分钟

基本概念

  • 什么是分布式锁

    • 如果需要控制分布式系统,不能是某个客户端本地的锁
    • 锁是保存在一个共享存储系统中的,可以被多个客户端共享访问和获取\
  • 为什么选择redis

    • 可以被多个客户端共享访问,正好就是一个共享存储系统\

    • 读写性能高,可以应对高并发的锁操作场景

单机上的锁和分布式锁的联系与区别

  • 对于单机锁

    • 变量为0,表示线程未获取到锁
    • 变量为1,表示已经有线程获取到锁
    • 获取锁则把变量置为1
    • 释放锁则把变量置为0
  • 分布式锁

    • 也是通过判断锁的变量值,多个客户端存储数据

    • 新的要求

      • 分布式锁的加锁和释放锁的过程,涉及多个操作(需要保证原子性)\

      • 怎么保证分布式锁的稳定性和可靠性

基于单个 Redis 节点实现高可靠的分布式锁

  • 设置设计分布式锁

    • key锁的名称 value锁的状态
  • 怎么保证读取锁,判断锁和设置锁操作的原子性

    • 使用redis原子命令

      • SETNX 如果存在就设置,如果不存在就不设置+DEL删除释放锁\
    • 使用Lua脚本

  • 使用SETNX的潜在风险

    • DEL操作异常,导致锁一直不释放  通过设置过期时间避免死锁
    • 不是申请的锁的客户端执行DEL会误删除锁 可以将value设为客户端唯一值
    • SET 命令的 NX 和 EX/PX 选项\
  • DEL删除释放锁\

    • 最好使用lua脚本,保证指令的原子性
    • 读取数据,删除释放数据

\

基于多个 Redis 节点实现高可靠的分布式锁

  • Redis 的开发者 Antirez 提出了分布式锁算法 Redlock\

  • 如果客户端能够和半数以上的实例成功地完成加锁操作\

    • 获取锁成功
    • 否则获取锁失败
  • 具体实现步骤

    • 客户端获取当前时间

    • 客户端按顺序依次向 N 个 Redis 实例执行加锁操作(使用指令SETNX value是客户端标识) 顺序加锁\

    • 完成加锁,计算总耗时 判断是否成功

      • 超过半数加锁成功
      • 总耗时未超过过期时间
  • 可以通过redlock保证可靠性

总结

  • 加锁注意点

    • 加锁包括了读取锁变量、检查锁变量值和设置锁变量值三个操作 使用SETNX指令\

    • 锁变量需要设置过期时间,避免死锁\

    • 锁变量的值需要能区分来自不同客户端的加锁操作,以免在释放锁时,出现误释放操作\

  • 释放锁注意点

    • 释放锁也包含了读取锁变量值、判断锁变量值和删除锁变量三个操作\

    • 没有指令能够完成

    • 一般使用lua脚本实现操作的原子性

  • 保证稳定性

    • 可以使用Redlock算法,实现基于多个实例的分布式锁