redis锁的实现--根据操作系统软件锁的理论来处理
首先了解下操作系统的三个概念:
1、竞争条件,和调度有关的共享数据语义错误(程序代码表达的意思和结果有出入);错误由多个进程并发操作共享数据引起;错误和调度顺序有关,难于发现和调试。
例如:empty = empty - 1;在多个进程的调度执行时的语句顺序可能为下面图示。
注:empty时内存里的数据,计算机需要读到寄存器才能处理。
2、临界区,一次只允许一个进程进入的该进程的那一段代码。
3、临界区代码的保护原则:
1)互斥进入:如果一个进程在临界区中执行,则其他进程不允许进入;(基本原则)
2)有空让进:若干进程要求进入空闲临界区时,应尽快使一进程进入临界区;(好的临界区保护原则)
3)有限等待:从进程发出进入请求到允许进入,不能无限等待。(好的临界区保护原则)
4、根据操作系统的软件锁思想实现redis锁
1)轮换法:
A进程的唯一标识符为“Value_A";B进程的唯一标识符:"Value_B";有一redis的key名为mutex_turn。
A进程的伪代码:
while(GET mutex_turn!= ”Value_A"){}; //不等于则一直等待
临界区
SET mutex_turn "Value_B"
B进程的伪代码:
while(GET mutex_turn != ”Value_B"){}; //不等于则一直等待
临界区
SET mutex_turn "Value_A"
分析:A和B进程可以互斥进入,但A进程执行后,必须等待B进程执行后才能继续获得执行权,若只有A进程执行,多次执行A这段代码,则第二次时一直等待。
结论:满足互斥进入,但不满足有空让进。
2)Peterson算法,标记+轮换
A进程的唯一标识符为“Value_A",B进程的唯一标识符:"Value_B";redis有一个key名为mutex_turn字符串;
redis有一key名为flag的hash,初始值都是0。
A进程的伪代码:
HSET flag A 1
SET mutex_turn "Value_B"
while(cmd1 && cmd2){}; //cmd1是:1==HGET flag B, cmd2是:GET mutex_turn != ”Value_A"
临界区
HSET flag A 0
B进程的伪代码:
HSET flag B 1
SET mutex_turn "Value_A"
while(cmd1 && cmd2){}; //cmd1是:1==HGET flag A, cmd2是:GET mutex_turn != ”Value_B"
临界区
HSET flag B 0
分析:只有A进程执行,多次执行这段代码,满足有空让进;
A,B都falg[A],flag[B]为1,mutex_turn只会是其中一个,假设mutex_turn为"Value_A",则A执行,B阻塞,等A进行完HSET flag A 0后,B获得执行,满足互斥进入;
A进程执行一次后,由于B进入时留下了flag[B]=1,则A不会一直进入临界区,满足有限等待。
结论:满足互斥进入,满足有空让进,满足有限等待。
3)多进程时的处理,面包店算法
每个进入商店的客户都获得一个号码,号码最小的先得到服务;号码相同时,名字靠前的先服务。
实现条件:
一个FIFO的队列Queue_num,队头为当前轮流执行的进程id(唯一标识),可以选择sorted set,每次zadd新元素时,获取set的个数,作为新插入元素的值;
redis有一key名为choosing的hash,初始值都是0。
5、其他实现
由于redis里每条命令都是原子执行的,利用这一点,可以有很多方式实现。
1)INCR
、SETNX
、SET
2)、redlock
3)、使用watch事务
伪代码:
1 WATCH mykey
2
3 val = GET mykey
4 val = val + 1
5
6 MULTI //MULTI 命令用于开启一个事务,它总是返回 OK
7 SET mykey $val
8 EXEC //EXEC 命令被调用时, 所有队列中的命令才会被执行
使用上面的代码, 如果在 WATCH 执行之后, EXEC 执行之前, 有其他客户端修改了 mykey
的值, 那么当前客户端的事务就会失败。 程序需要做的, 就是不断重试这个操作, 直到没有发生碰撞为止。
这种形式的锁被称作乐观锁, 它是一种非常强大的锁机制。 并且因为大多数情况下, 不同的客户端会访问不同的键, 碰撞的情况一般都很少, 所以通常并不需要进行重试。
Reference:
哈工大 李治军 操作系统之进程与线程
http://ukagaka.github.io/php/2017/09/21/redisLock.html
https://redis.io/topics/distlock
http://redisdoc.com/topic/transaction.html