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)INCRSETNXSET

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

posted @ 2020-12-07 00:11  名不见  阅读(82)  评论(0编辑  收藏  举报