标签 redis 下的文章

先说说redis分布式锁的原理。
加锁:setnx一个key,返回1说明加锁成功,0说明锁已经存在。
解锁:del该key。

原理说完了,再讨论特殊情况。
1.在set之后,如果客户端挂了,锁会一直存在,导致死锁。
那有人会说,用expire。
但是两个命令无法保证原子性。
还好,redis从2.6.12开始,set命令支持一系列参数来修改。

set key value EX seconds NX 

2.对单机来说没问题,但是在集群环境下还是有问题。redis集群数据同步是异步的,当master节点获得锁后崩溃了,数据还没同步到其他master节点,那么新的master还是有可能获得锁。
如何解决单节点故障问题,redis提供了redlock算法。

3.加锁讨论完,再讨论解锁。解锁要保证删除的是自己设置的锁。正常逻辑是先get,判断锁是否存在,存在则删除。但是有个问题,如果程序在加锁后执行时间很长且锁过期时间较短,get到锁后锁自动过期了,并且别的客户端加锁成功,若此时删除就删了别人的锁。
所以,在get和del之间不能有其他命令,redis做不到,可以通过lua实现

'if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end'

因为lua执行可以保证原子性。
lua执行多条命令,由于redis是单线程,中间不会有穿插其他命令。
如果不用lua,执行多条命令可能中间有别得命令执行。

集群模式

上面说的是单机模式下的,如果是集群模式下,刚获得锁主节点就挂了,锁还没有同步到从节点,此时从节点切换为主节点,此时已经没有锁,任何连接都可以设置锁了,但问题是之前的连接还未执行释放锁,若此时执行释放锁操作则会释放别人的锁,这就会出现问题。丧失锁的安全性。针对这个问题,成熟的方案是redlock算法。

redlock算法

sds定义

sds.h/sdshdr 结构定义了sds的结构:

struct sdshdr {
//记录buf数组使用字节数量,即字符串长度
int len;

//记录buf数组未使用字节数量
int free;

//字节数组,保存字符串
char buf[];

len: 字符串长度

- 阅读剩余部分 -

参考redis官方文档redis.io

过期键删除策略

1 惰性删除策略
所有读写数据库的Redis命令在执行之前都会先检查输入键是否已过期,过期则删除之。
CPU友好,内存不友好
2 定期删除策略
每当 Redis 的服务器周期性操作函数 serverCron 执行时,就会调用定期删除过期键的函数,(serverCron默认100毫秒执行一次)在规定时间内,分多次遍历服务器中的各个数据库,从数据库的过期字典中随机检查一部分键的过期时间,并删除其中的过期键。
通过全局变量记录当前处理的数据库,直到所有数据库的过期键都被删除。






- 阅读剩余部分 -