湖南网站开发公司,游戏特效培训机构排名,百度浏览官网,宁波网络推广加盟过期的key集合
Redis会将每个设置了过期时间的key放入到一个独立的字典中#xff0c;以后会定时遍历这个字典来删除到期的key。除了定时遍历之外#xff0c;他还会使用惰性策略来删除过期的key#xff0c;所谓惰性策略就是在客户端访问这个key的时候#xff0c;redis对key…过期的key集合
Redis会将每个设置了过期时间的key放入到一个独立的字典中以后会定时遍历这个字典来删除到期的key。除了定时遍历之外他还会使用惰性策略来删除过期的key所谓惰性策略就是在客户端访问这个key的时候redis对key的过期时间进行检查如果过期了就立即删除。定时删除是集中处理惰性删除是零散处理。
定时扫描策略
Redis默认会每秒进行十次过期扫描过期扫描不会遍历过期字典中所有的key而是采用了一种简单的贪心策略。
从过期字典中随机20个key。删除这20个key中已经过期的key。如果过期key的比率超过1/4那就重复步骤1。
同时为了保证过期扫描不会出现循环过度导致线程卡死现象算法还增加了扫描时间的上线默认不会超过25ms。
设想一个大型的Redis实例中所有key在同一时间过期了会出现怎样的结果。
毫无疑问Redis会持续扫描过期字典循环多次直到过期字典中过期的key变得稀疏才会停止循环次数明显下降。这就会导致线上读写请求出现明显的卡顿现象。导致这种卡顿的另一种原因是内存管理器需要频繁回收内存页这也会产生一定的CPU消耗。
为什么设置了25ms超时时间仍然会卡顿
假设有101个客户端同时将请求发过来每一个请求都需要经过25ms的超时时间那么第101个指令需要等待2500ms后才能得到执行这个就是客户端的卡顿时间是由服务器不间断的小卡顿积少成多导致的。
所以开发人员一定要注意过期时间如果有大批量的key过期要给过期时间设置一个随机范围而不能全部在同一时间过期。
从库的过期策略
从库不会进行过期扫描从库对过期的处理是被动的。主库在key到期时会在AOF文件里增加一条del指令同步到所有的从库从库通过执行这条del指令来删除过期的key。
因为指令同步是异步进行的所以从库过期的key的del指令没有及时同步到从库的话会出现主从数据的不一致主库没有的数据从库里还存在比如集群环境分布式锁的算法漏洞就是因为这个同步延迟产生的。
LRU淘汰算法
当Redis内存超出物理内存限制时内存的数据会开始和磁盘产生频繁的交换交换会让Redis的性能急剧下降对于访问量比较频繁的Redis来说这样龟速的存取效率基本上等于不可用。
在生产环境中是不允许Redis出现交换行为的为了限制最大使用内存Redis提供了配置参数maxmemory来限制内存超出期望大小。
当实际内存超出maxmemory时Redis提供了几种可选策略来让用户决定如何腾出新的空间以继续提供读写服务。
noeviction: 不会继续服务写请求del请求除外读请求可以继续进行这样可以保证不会丢失数据但是会让线上的业务不能持续进行这是默认的淘汰策略。volatile-lru: 尝试淘汰了设置过期时间的key最少使用的key优先被淘汰。没有设置过期时间的key不会被淘汰这样可以保证需要持久化的数据不会突然丢失。volatile-ttl 跟上面一样除了淘汰策略不是lru而是key的剩余寿命ttl的值ttl越小越优先被淘汰。volatile-random跟上面一样淘汰的key是设置了过期时间的key集合中随机的key。allkeys-lru区别于volatile-lru这个策略要淘汰的key对象是全体的key集合而不是只是过期的key集合这意味着没有设置过期时间的key也会被淘汰。allkeys-random所有的key中随机淘汰。
总结 volatile-xxx该策略只会针对带过期时间的key进行淘汰allkeys-xxx策略会针对所有的key进行淘汰。如果只是使用Redis做缓存应该使用allkeys-xxx客户端写缓存时不必携带过期时间。如果同时使用Redis的持久化功能那就使用volatile-xxx策略这样可以保留没有设置过期时间的key。
Redis的近似LRU算法
Redis使用的是一种近似LRU算法他跟LRU算法不太一样之所以不使用LRU算法是因为需要消耗大量的额外内存需要对现有的数据结构进行较大的改造。近似LRU算法则很简单在现有数据结构的基础上采用随机采样法来淘汰元素能达到和LRU算法非常近似的效果。
Redis为实现近似LRU算法他给每个key增加了一个额外的小字段这个字段的长度是24个bit也就是最后一次被访问的时间戳。
上一节提到处理key过期方式分为集中处理和懒惰处理LRU淘汰不一样他的处理方式只有懒惰处理。当Redis执行写操作时发现内存超出maxmemory就会执行一次LRU淘汰算法随机采样出5可以配置个key然后淘汰掉最旧的key如果淘汰后内存还是超出maxmemory那就继续随机采样淘汰直到内存低于maxmemory为止。
如何采样就是看maxmemory-policy的配置如果是allkeys就是从所有的key字典中随机如果是volatile就从带过期时间的key字典中随机。每次采样多少个key看的是maxmemory_samples的配置默认是5.
在Redis3.0中算法增加了淘汰池进一步提升了近似LRU算法的效果。淘汰池是一个数组他的大小是maxmemory_samples在每次淘汰循环中新随机出来的key列表会和淘汰池中的key列表进行融合淘汰掉最旧的一个key之后保留剩余较旧的key列表放入淘汰池中等待下一个循环。
惰性删除
一直以来我们认为Redis是单线程的不过Redis内部实际上并不是只有一个主线程他还有几个异步线程专门用来处理一些耗时操作。
Redis为什么要惰性删除
删除指令del会直接释放对象的内存大部分情况下这个指令非常快没有明显延迟。不过如果删除的key是一个非常大的对象比如一个包含了千万元素的hash那么删除操作就会导致单线程卡顿。
Redis为了解决这个卡顿问题在4.0版本中引入了unlink指令他能对删除操作进行惰性处理丢给后台线程来异步回收内存。
使用多线程进行内存回收是否存在线程安全问题
不会当unlink指令发出后被unlink的key就再也无法被主线程中的其他指令访问到了。
flush
Redis提供了flushdb和flushall指令用来清空数据库这也是极其缓慢的操作。Redis4.0 同样给这两个指令也带来了异步化在指令后面增加async参数就可以让后台线程慢慢处理同时不会再被主线程访问到其中的key。
异步队列
主线程将对象的引用删除后会将这个key的内存回收操作包装成一个任务塞进异步任务队列后台线程会从这个异步队列中取任务。任务队列被主线程和异步线程同时操作所以必须是一个线程安全的队列。 不是所有的unlink操作都会延后处理如果对应key所占用的内存很小延后处理就没有必要了这时候Redis会将对应的key内存立即回收跟del指令一样。
AOF sync也很慢
Redis需要每秒一次同步AOF日志到磁盘确保消息尽量不丢失需要调用sync函数这个操作会比较耗时会导致主线程的效率下降所以Redis也将这个操作移到异步线程来完成。执行AOF sync操作的线程是一个独立的异步线程和前面的惰性删除线程不是一个线程同样他也有一个属于自己的任务队列队列里只用来存放AOF Sync任务。