这篇让你彻底搞懂分布式锁

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 这篇让你彻底搞懂分布式锁

分布式锁在项目中有哪些应用场景?


使用分布式锁的场景一般需要满足以下场景:


  1. 系统是一个分布式系统,集群集群,java的锁已经锁不住了。


  1. 操作共享资源,比如库里唯一的用户数据。


  1. 同步访问,即多个进程同时操作共享资源。




分布锁有哪些解决方案?


  1. Reids的分布式锁,很多大公司会基于Reidis做扩展开发。setnx key value ex 10s,Redisson。
    watch dog.


  1. 基于Zookeeper。临时节点,顺序节点。


  1. 基于数据库,比如Mysql。主键或唯一索引的唯一性。




Redis做分布式锁用什么命令?


SETNX


格式:setnx key value 将 key 的值设为 value ,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作,操作失败。


SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。


加锁:set key value nx ex 10s

释放锁:delete key




Redis做分布式锁死锁有哪些情况,如何解决?


情况1:加锁,没有释放锁。需要加释放锁的操作。比如delete key。


情况2:加锁后,程序还没有执行释放锁,程序挂了。需要用的key的过期机制。




Redis如何做分布式锁?


假设有两个服务A、B都希望获得锁,执行过程大致如下:


Step1: 服务A为了获得锁,向Redis发起如下命令: SET productId:lock 0xx9p03001 NX EX 30000 其中,"productId"由自己定义,可以是与本次业务有关的


id,"0xx9p03001"是一串随机值,必须保证全局唯一,“NX"指的是当且仅当key(也就是案例中的"productId:lock”)在Redis中不存在时,返回执行成功,否则执行失败。"EX 30000"指的是在30秒后,key将被自动删除。执行命令后返回成功,表明服务成功的获得了锁。


Step2: 服务B为了获得锁,向Redis发起同样的命令: SET productId:lock 0000111 NX  EX 30000 由于Redis内已经存在同名key,且并未过期,因此命令执行失败,服务B未能获得锁。服务B进入循环请求状态,比如每隔1秒钟(自行设置)向Redis发送请求,直到执行成功并获得锁。


Step3: 服务A的业务代码执行时长超过了30秒,导致key超时,因此Redis自动删除了key。此时服务B再次发送命令执行成功,假设本次请求中设置的value值为0000222。此时需要在服务A中对key进行续期,watch dog。


Step4: 服务A执行完毕,为了释放锁,服务A会主动向Redis发起删除key的请求。注意: 在删除key之前,一定要判断服务A持有的value与Redis内存储的value是否一致。比如当前场景下,Redis中的锁早就不是服务A持有的那一把了,而是由服务2创建,如果贸然使用服务A持有的key来删除锁,则会误将服务2的锁释放掉。此外,由于删除锁时涉及到一系列判断逻辑,因此一般使用lua脚本,具体如下:



if redis.call("get", KEYS[1])==ARGV[1] then
  return redis.call("del", KEYS[1])
else
  return 0
end
复制代码




基于 ZooKeeper 的分布式锁实现原理是什么?


顺序节点特性:


使用 ZooKeeper 的顺序节点特性,假如我们在/lock/目录下创建3个节点,ZK集群会按照发起创建的顺序来创建节点,节点分别


为/lock/0000000001、/lock/0000000002、/lock/0000000003,最后一位数是依次递增的,节点名由zk来完成。


临时节点特性:


ZK中还有一种名为临时节点的节点,临时节点由某个客户端创建,当客户端与ZK集群断开连接,则该节点自动被删除。EPHEMERAL_SEQUENTIAL为临时顺序节点。

根据ZK中节点是否存在,可以作为分布式锁的锁状态,以此来实现一个分布式锁,下面是分布式锁的基本逻辑:


  1. 客户端1调用create()方法创建名为“/业务ID/lock-”的临时顺序节点。


  1. 客户端1调用getChildren(“业务ID”)方法来获取所有已经创建的子节点。


  1. 客户端获取到所有子节点path之后,如果发现自己在步骤1中创建的节点是所有节点中序号最小的,就是看自己创建的序列号是否排第一,如果是第一,那么就认为这个客户端1获得了锁,在它前面没有别的客户端拿到锁。


  1. 如果创建的节点不是所有节点中需要最小的,那么则监视比自己创建节点的序列号小的最大的节点,进入等待。直到下次监视的子节点变更的时候,再进行子节点的获取,判断是否获取锁。




ZooKeeper和Reids做分布式锁的区别?


Reids:


  1. Redis只保证最终一致性,副本间的数据复制是异步进行(Set是写,Get是读,Reids集群一般是读写分离架构,存在主从同步延迟情况),主从切换之后可能有部分数据没有复制过去可能会 「丢失锁」 情况,故强一致性要求的业务不推荐使用Reids,推荐使用zk。


  1. Redis集群各方法的响应时间均为最低。随着并发量和业务数量的提升其响应时间会有明显上升(公网集群影响因素偏大),但是极限qps可以达到最大且基本无异常


ZooKeeper:


  1. 使用ZooKeeper集群,锁原理是使用ZooKeeper的临时顺序节点,临时顺序节点的生命周期在Client与集群的Session结束时结束。因此如果某个Client节点存在网络问题,与ZooKeeper集群断开连接,Session超时同样会导致锁被错误的释放(导致被其他线程错误地持有),因此ZooKeeper也无法保证完全一致。


  1. ZK具有较好的稳定性;响应时间抖动很小,没有出现异常。但是随着并发量和业务数量的提升其响应时间和qps会明显下降。



总结:


  1. Zookeeper每次进行锁操作前都要创建若干节点,完成后要释放节点,会浪费很多时间;


  1. 而Redis只是简单的数据操作,没有这个问题。




MySQL如何做分布式锁?


在Mysql中创建一张表,设置一个 主键或者UNIQUE KEY 这个 KEY 就是要锁的 KEY(商品ID),所以同一个 KEY 在mysql表里只能插入一次了,这样对锁的竞争就交给了数据库,处理同一个 KEY 数据库保证了只有一个节点能插入成功,其他节点都会插入失败。


DB分布式锁的实现:通过主键id 或者 唯一索性 的唯一性进行加锁,说白了就是加锁的形式是向一张表中插入一条数据,该条数据的id就是一把分布式锁,例如当一次请求插入了一条id为1的数据,其他想要进行插入数据的并发请求必须等第一次请求执行完成后删除这条id为1的数据才能继续插入,实现了分布式锁的功能。


这样 lock 和 unlock 的思路就很简单了,伪代码:


def lock :
    exec sql: insert into locked—table (xxx) values (xxx)
    if result == true :
        return true
    else :
        return false
def unlock :
    exec sql: delete from lockedOrder where order_id='order_id'



相关文章
|
安全 数据建模 网络安全
免费多域名证书,最多支持保护250个域名
免费多域名证书,最多支持保护250个域名
502 0
|
运维 Java Nacos
nacos常见问题之读取不到配置文件如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
6810 2
|
弹性计算 NoSQL Linux
阿里云ecs Linux平台安装mongodb数据库
MongoDB提供了linux平台上32位和64位的安装包,你可以在官网下载安装包。 下载地址:http://www.mongodb.org/downloads 下载完安装包,并解压 tgz(以下演示的是 64 位 Linux上的安装) 。
2528 0
|
消息中间件 安全 Java
Kafka消息发送的三种模式
消息队列Kafka版是阿里云提供的分布式、高吞吐、可扩展的消息队列服务。消息队列Kafka版广泛用于日志收集、监控数据聚合、流式数据处理、在线和离线分析等大数据领域,已成为大数据生态中不可或缺的部分。目前发送端支持三种消息发送模式:发后即忘(fire-and-forget)、同步(sync)和异步(async)。
5476 0
|
关系型数据库 MySQL
mysql查看当前实时连接数最大连接数
mysql查看当前实时连接数最大连接数
1660 0
|
数据可视化 数据建模
R语言广义加性混合模型(GAMM)分析长沙气象因子、空气污染、PM2.5浓度、显著性检验、逐日变化可视化(下)
R语言广义加性混合模型(GAMM)分析长沙气象因子、空气污染、PM2.5浓度、显著性检验、逐日变化可视化
|
Java Maven Spring
Maven 依赖搜索顺序
Maven在构建时按顺序搜索本地->中央仓库->已配置的远程仓库来查找依赖。若未设置远程仓库,会报错。可将默认仓库替换为阿里云仓库,通过修改settings.xml的mirrors节点或在repositories节点添加仓库地址。在pom.xml中指定依赖后,运行`mvn install`拉取。
|
缓存 Java 编译器
Java 中的 JIT 和 AOT
我们都知道,Java 是一种半编译型,半解释型的语言,其编译部分和 C++ 语言比较类似,解释部分和 Python 语言比较类似,而 Java 则是综合了两种方式的语言。
598 1
|
监控 前端开发 JavaScript
6个顶级SpringCloud微服务开源项目,企业开发必备!
6个顶级SpringCloud微服务开源项目,企业开发必备!
4968 0
6个顶级SpringCloud微服务开源项目,企业开发必备!
GitHub标星10k!基于Alibaba规范的vue+分布式高耦合后台管理系统
一个基于vue3-element-admin开源项目重写Vue3 + TS + ElementPlus,采用插件化 + 扩展包形式 结构解耦 易于扩展的后台管理系统 本框架与RuoYi的功能差异