Redis的过期策略和内存淘汰机制
redis采用的是定期删除+惰性删除策略。
为什么不用定时删除?
定时删除,也可以说是实时删除,用一个定时器监控key,过期则删除。虽然即使释放了内存空间,但十分消耗CPU资源。
定期删除,redis默认每100ms检查是否有key过期。不是每隔100ms便将所有key检查一遍,而是随机抽取进行检查。因此,如果只采用定期删除策略,会导致很多key到时间后没有被删除。
惰性删除,在获取某个key时,redis会检查这个key是否过期,如果过期了此时就会被删除。
定期删除+惰性删除能够做的一劳永逸吗?
如果有些key过期后,在定期删除时不会被随机检查到,而且也几乎不会被访问,这些key就会一直存在。
redis的解决方案:内存淘汰机制
在redis.conf中有一行配置:
maxmemory-policy volatile-lru
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
Redis如何处理大量请求
redis是一个单线程程序,即同一时刻只能处理一个客户端请求。(单线程避免了频繁的线程上下文切换)
redis是通过IO多路复用的方式来处理多个客户端请求的,保证系统的高吞吐量。由于redis是基于内存运行,所以其处理单个客户端请求会很快,数据传输过程的阻塞时间完全可以忽略。
也就是说,redis避免了频繁的线程的上下文切换后,CPU不会是其瓶颈,其瓶颈在于内存和网络带宽。
为什么要用IO多路复用这种技术呢?
Redis是跑在单线程的,所有的IO操作都是按照顺序线性执行,但由于一些读写操作等待用户输入或输出是阻塞的,如果其他IO操作都在等待阻塞操作结束,那所有的IO操作都会被阻塞。
redis采用IO多路复用的方式,监听多个套接字,保证了某一操作被阻塞后,redis的单一线程仍能处理其他IO操作。
Redis分布式锁
分布式锁是控制分布式系统不同进程间访问共享资源的一种方式。多用于解决分布式系统下的多进程并发问题。
情景:线程A和线程B都共享某个变量X
如果是单机情况下(单JVM进程),线程之间共享内存,使用线程锁就可以解决并发问题。
如果是分布式情况下(多JVM进程),线程A和B可能不在同一JVM进程中,线程锁便无法起到作用,需要使用分布式锁来解决进程间的冲突问题。
redis实现分布式锁的关键:
排他性:在同一时间只能有一个客户端获取到锁,其他客户端无法获取
避免死锁: 锁信息必须是会过期的,不能让一个线程一直占有锁而导致死锁。
除了锁自动过期之外,还可以解锁,加锁和解锁必须是同一线程。
Redis实现分布式锁,主要是依赖redis自身的原子操作:
SET user_key user_value NX PX 100
NX:只在在键不存在时,才对键进行设置操作,SET key value NX 效果等同于 SETNX key value。
PX 100:设置键的过期时间为100毫秒。
当某个进程设置成功之后,就可以去执行业务逻辑了,等业务逻辑执行完毕之后,再去进行解锁。
解锁很简单,只需要删除这个key就可以了,不过删除之前需要判断,这个key对应的value是当初自己设置的那个。
如果一个锁过期但线程业务还没执行完,怎么办?
可以创建一个守护线程,一直为锁续期。如果主线程没执行完,守护线程也不会死掉,直到主线程执行完毕。