Redis(Remote Dictionary Server)的使用(2) | 青训营笔记
这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天
Redis分布式锁的实现
分布式锁的使用场景: 在多线程进行的一个程序中,为了避免不同线程同时操作一个共享变量时出现数据问题,通常会使用互斥锁来保证共享变量的正确性。这种方式存在的一个明显设定范围是同一个进程中,而现在的微服务架构往往是部署了多个进程处理,当多个进程需要修改MySQL中的同一行记录时,此时就需要引入分布式锁来避免并发操作可能导致的数据问题。
- 方法1:使用SETNX命令(SET IF NOT EXIST)
在Redis中,锁同样以键值对的型式存储为锁变量,通过设置锁变量来实现Redis的数据互斥使用机制。SETNX命令可以用于进程实现加锁,其命令的含义为:当锁变量的key不存在时就设置其值,否则代表数据已被使用,不能继续操作。加锁成功的客户端,就可以去操作共享资源。在使用完数据后需要及时释放锁,操作流程如下:
// 加锁
SETNX lock_key 1
// 业务逻辑
DO THINGS
// 释放锁
DEL lock_key
- 方法2:使用SET命令
为了避免加锁的进程由于逻辑异常或意外终止等情况而持续占用造成死锁的情况,需要对锁设定一个过期时间(EXPIRE命令),在锁过期后需要对该进程进行处理,或者选择再去加锁防止其他线程进入;或者不做处理,放弃该进程的请求;但由于SETNX+EXPIRE毕竟是两条命令顺序执行的组合操作,而非原子操作,就有可能出现设锁后还未来得及设置过期时间就执行失败的情况,无法保证完全避免死锁问题。因此,在设置过期时间时应当使用Redis 2.6.12扩展的SET命令,在加锁的同时指定EXPIRE时间。
SET lock_key 1 EX 10 NX
// 其命令格式如:set($Key,$value, array('nx', 'ex'=>$ttl))
- 方法3:使用INCR命令
该命令本质上与SETNX命令一致,都使用将锁变量作为键值对存储。如果 key 不存在,则初始化值为 0,然后再利用 INCR 进行加 1 操作。后续用户如果获取到的值大于等于 1,说明已经被其他线程加锁。当持有锁的用户在执行完任务后,利用 DECR 命令将 key 的值减 1,则表示释放锁。其同样需要配合EXPIRE命令使用,也可以使用GET/SET命令自动获取当前计数器值并将其重置为零。 使用DECR或INCRBY等其他原子递增/递减命令,可以处理根据用户执行的操作而变大或变小的值。redis Incr 命令基本语法如下:
INCR KEY_NAME