分布式锁即在分布式环境下锁定共享资源,让请求处理串行化,实际表现为互斥锁。分布式锁可以解决业务中的幂等性问题。
在用户对商品下单的时候,如果商户正在对商品改价,同时用户正在支付,就可能发生并发问题,这个时候就需要串行化操作,防止出现业务问题。
分布式锁要解决的几个问题:
- 互斥性,同一时刻只能有一台服务器能访问资源
- 安全性,锁只能被持有该锁的客户端删除或者释放
- 容错,在服务器宕机的时候,锁仍能得到释放或者其他服务器可以进行加锁
- 避免死锁
分布式锁设计目标:
- 强一致性
- 服务高可用,系统稳健
- 锁自动续约及自动释放
- 代码高度抽象与业务接入简单
- 可监控管理
Redis实现
参考我早先写的Redis实现分布式锁
set key value [EX seconds] [PX milliseconds] [NX|XX]
复制代码
官方建议redis使用redlock算法来保证,但是redlock算法至少需要3台redis主从实例来完成,维护成本高,redlock相当于自己实现简单的一致性协议,细节繁琐,且容易出错。关于redLock算法,参考redis分布式锁官方介绍
分布式锁方案对比
redis | zookeeker | etcd | |
---|---|---|---|
一致性算法 | 无 | paxos/ZAB | raft |
CAP | AP | CP | CP/AP |
高可用 | 主从 | N+1可用(奇数个) | N+1可用 |
接口类型 | 客户端 | 客户端 | http/grpc |
实现 | set命令 | 临时节点 | restful API |
关于分布式一致性协议,参考我之前的整理:几种常见的分布式一致性协议介绍
- redis无法保证数据一致性
- zk的性能比较差,扩展能力,社区活跃度低于etcd(ZK比较成熟)
- 可以选择基于etcd,付费文章-分布式锁的最佳实践之:基于 Etcd 的分布式锁
基于ectd的分布式锁流程:
- 多个客户端同时到etcd获取同一把锁,但是只能有一个客户端能获取到锁
- 在获取锁的时候,请求中会设置过期时间,获取到锁之后etcd会生成uuid作为唯一凭证
- 后台新启动一个线程,不断的刷新锁的租期
产品要想真正投入使用,要做一些兼容性测试:
- 集群下停掉某个节点观察是否正常工作
- 当只有一个节点时会发生什么情况,(etcd读会停机,写入正常)
- 理论上不是多节点同时停机,线上服务是不会受影响的
etcdV3版本提供gRPC接口,天然提供分布式锁的功能,只需申请锁,释放锁,不用关心锁的租期问题。
分布式锁注意点
- 分布式锁只是同一自然时间的互斥锁,本身不解决幂等性问题 ==> 业务问题
- 锁没有按照预期续租,可能会这种情况。比如网络问题,比如GC回收时间比较长 ==> 极端情况
- etcd内部选主的时候,去拿锁是拿不到的。 ==> etcd内部问题