在分布式环境中,经常遇到多台机器上的多个进程对同一数据的操作,如果这些进程不做互斥处理的话,往往会出现不符合预期的错误,比如商品超卖、红包超发、账号多扣款等。
多台机器上的进程的互斥,可以通过锁来达成。接下来我们介绍几种分布式锁的实现方式。
方式一、Redis setnx
这是比较常见的一种简单实现方式
我们先来看一下Redis setnx的文档介绍:
1 | SETNX key value |
带过期时间的分布式锁实现:
1 | <?php |
看似没问题,想一下这种情况,setnx成功之后,expire操作执行失败了,进程crash,则该key设置的锁永远得不到释放。
这种实现方式的问题在于,setnx与expire操作不是原子操作,存在单点故障时锁无法释放的问题。
方式二、RedLock
这是redis之父antirez提出的分布式锁实现方式。
Redlock获取锁的过程。
1 | 1. 获取开始时间 |
Redlock获取锁的过程。
向各个节点发送del命令,删除锁
。
存在的问题
依赖于分布式机器时钟的同步。
这个问题曾引起分布式专家martin与Redis之父Antirez之间的论战。参见参考文档。
Martin 对 RedLock的指控:
1 | 1. 分布式的锁具有一个自动释放的功能。锁的互斥性,只在过期时间之内有效,锁过期释放以后就会造成多个Client 持有锁。 |
Martin对锁的改进,增加了token,实际是利用了CAS的乐观锁实现:
最后,martin推荐Zookeeper实现分布式锁。
Antirez的回应:
1 | 首先在实际的系统中,从两个方面来看: |
方式三、基于ZooKeeper实现
原理分析:
1 | 1. 锁即Zookeeper上的一个节点 |
初始状态:
获取锁的流程