过期策略、内存淘汰机制
1.过期策略:请求时删除 + 定期删除
请求时删除:使用key之前,检查是否过期,属于一种被动的处理方式。
因此,过期时间到了不表示这个key真的被删除了
定期删除:Redis默认每隔100ms检查,有过期key则删除,检查方式为随机抽查。
定期删除属于请求时删除的一种补充机制。
redis使用额外的字典专门存储带expire过期时间的key,redis默认有16个DB,删除时尝试轮询每个DB的expire字典,遍历一遍该字典即可,空间换时间。同时,还会全局记录当前处理到了哪个DB,方便后续从当前位置开始。
删除key,需要考虑一个问题:长时间阻塞,早期版本的redis是通过主线程去删除key的,如果是大key,会造成长时间阻塞。因此,对于大key,采用异步惰性删除(lazy_free),避免阻塞主线程。
从DB中删除过期key时,只是从DB字典中将关系删除,内存还没有释放,而是交给后台异步线程。
在redis.conf中配置 ,默认没有开启。
unlink指令:用于将redis中指定key从DB字典中“摘除”,但是不会真正释放内存。而正常的DEL指令是会去真正释放内存空间的,为了兼容,即让DEL指令也仅摘除 也是可以配置的。
2.兜底机制 -- 内存淘汰机制
场景,为什么需要兜底 ?
采用惰性删除,当使用key的时候去检查是否过期,那如果存在大量key且都没有使用的话,就会一直存储在内存中,这样显然是有问题的,因此提出了内存淘汰机制作为兜底。
内存淘汰机制:当内存不足的时候触发的兜底机制
noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 Key。(推荐使用,目前项目在用这种)(最近最久使用算法)
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 Key。(应该也没人用吧,你不删最少使用 Key,去随机删)
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 Key。这种情况一般是把 Redis 既当缓存,又做持久化存储的时候才用。(不推荐)
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 Key。(依然不推荐)
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。(不推荐)
3.双写一致
强一致性和最终一致性
4.Redis的并发竞争key
多线程修改key场景下,是否需要强调顺序先后:
不强调顺序先后 : 不需要做额外判断,直接采用传统的分布式锁,谁先抢到给谁
强调顺序先后 : 在传统分布式锁基础之上,在set数据的时候,给value字段额外增加一个 时间戳,比如我要维护A,B,C的顺序,预先给A,B,C三个实体定义从小到大顺序的时间戳。当B操作完了之后,A拿到锁,尝试操作修改的时候,发现自己的时间戳比当前数据要早,修改失败。
如果对数据性能要求不高,即只要数据最终一致性,可以考虑放到队列中,去串行执行。
5、redis单线程的思考
我们常说6.0之后,redis引入多线程(IO线程),但其实6.0之前就有多线程(后台线程)的概念了。
6.0之前,redis有三个后台线程close_file
、aof_fsync
和lazy_free
:
-
close_file 表示关闭相应文件描述符对应的文件(释放套接字、数据空间等)。
-
aof_fsync 表示 AOF 刷盘
-
lazy_free 表示惰性释放空间
-
通过
bio_close_file
线程来释放 AOF / RDB 等过程中产生的临时文件资源。 -
通过
aof_fsync
线程将追加至 AOF 内存缓存中的数据写入磁盘。 -
通过
lazy_free
线程释放大对象(已删除)占用的内存空间.