1.分布式锁定义:
分布式环境下,锁定全局唯一资源, 请求处理串行化,实际表现互斥锁
2.分布式锁目的
2.1.交易订单锁定
防止重复下单
解决业务层幂等问题
2.2.MQ消息消费幂等性
发送消息重复
消息消费端去重
比如手机提现
2.3.在用户对商品下单后,订单状态为待支付,在某一时刻用户正在对该订单做支付操作,商家对该订单进行改价操作
状态的修改行为需要做串行处理,避免出现数据错乱
3.基于Redis分布式锁
redis特点:唯一线程串行处理。
3.1.实现方案
Redis Setnx(SET if Not exists)命令在指定的key不存在时,为key设置指定的值
SETNX Key_name value Expire Time
设置成功,返回1, 设置失败, 返回0
3.2.存在问题:
锁时间不可控
无法续租期
单点问题:
单实例存在进程一旦死掉,会彻底阻塞业务流程
主从方式,主从数据异步,会存在锁失效问题
3.3. 官方建议
Redis本身建议使用RedLock算法来保证,但是问题是需要至少三个Redis主从实例来完成,维护成本相对较高。
Redlock等同于自己实现简单的一致性协议,细节繁锁,且容易出错。
总结:Redis本身是AP模型,所以在严格需要一致性时,这种方案不适用。
4.高可用分布式锁设计目标
4.1.设计目标:
4.1.1.强一致性
4.1.2.服务高可用,系统稳健
4.1.3.代码高度抽象业务接入极简
4.1.4.可视化管理后台,监控及管理
4.2..存储产品对比
设计分布式锁需要基于分布式存储,
存储产品对比
redis: 一致性 : 无; CAP: AP; 高可用:主从;接口类型:客户端;实现:setNX
zookeeper:一致性:paxos ; CAP:CP; 高可用:N+1可用;接口类型:客户端;实现:createEphemeral
etcd:一致性:raft; CAP:CP/AP; 高可用:N+1可用;接口类型:http/grpc; 实现: restful API
总结:1)、由于Redis无法保证数据的一致性
2)、Zookeeper对锁实现使用创建临时节点和watch机制,执行效率、扩展能力、社区活跃度等方面低于etcd
3)、最终选择基于etcd实现
5.etcd介绍:
5.1.简单kv
5.2.强一致
5.3.高可用, 无单点
5.4.数据高可靠,持久化
6.分布式锁整体方案
6.1. 分布式Client + etcd
Client TTL模式
6.2. Client TTL模式
假设有两个客户同是拿锁 : key:锁的id, ttl为时间 , vlue:随意;uuid:不需要填,在拿锁成功后,etcd会成uuid,代表锁的唯一凭证,要对锁进行操作需要提供uuid
ClientA -> etcd ->("key", "ttl", "value", "uuid")
ClientB -> etcd ->("key", "ttl", "value", "uuid")
etcd保存只有一个线程拿到锁, 如 clientA拿锁成功, ClientB拿锁失败
A服务需要对etcd保持后台心跳线程, 比如:key的租期10ms,后台心跳线程为3ms,心跳线程负责在拿到key之后每3ms cas唯一凭证uuid, 每3ms钟会将租期修改为10ms
6.3.使用场景
6.3.1.业务方申请资源锁,调用提供key, ttl
如 key:order id , ttl:10ms
6.3.2.etcd生成uuid,作为当前锁的唯一凭证, 将(key,uuid,ttl)写入etcd
6.3.3.检查etcd中此key 是否存在,如没有尝试写入key, 写入失败,拿锁失败,写入成功拿锁成功
6.3.4.拿锁后,心跳线程启动,心跳线程维持时间为ttl/3, compare and swap uuid,从而将key值续租
6.3.5.相关etcd api
7.etcd兼容性测试
etcd提供了独有的集群管理模式,方便进行极端case下的测试,以三个节点为的etcd集群为例:
1)、单节点停机,不影响持续写入,不影响读,结果有一致性
2)、当只有一个节点时,读会停机,写入正常
3)、理论上只要不是多节点同时停机,线上服务不会受影响
8.etcd恢复/版本
8.1.etcd自有数据恢复方式:如果服务停机后,可以将所有数据转移后重启
8.2.etcd的增删节点 ,节点迁移等部署相关,均有相关操作方式
8.3.etcd版本选择,选择使用etcd3.2.9但是因为V3API暂时还不够完备,建议使用V2方式实现
V3提供gRPC接口
天然提供分布式锁功能
只需要申请锁,释放锁
不用关注锁的租期问题
9.分布锁特殊场景
9.1.特殊场景 一:
分布式锁只是在同一自然时间的互斥锁,本身不解决幂等性问题
接入业务需要完善从获得锁到释放锁中间数据幂等逻辑
9.2.特殊场景二:锁没有按照预期续租
心跳续租没成功
马上启动GC,GC时间够长
9.3.特殊场景三:etcd内部协调发生问题
leader节点挂了,选主中,是拿不到锁
Raft日志数据同步发生错误 或者不一致问题
以上情况可能需要做2套分布式锁