在高并发下,为了增加系统的并发能力通常都会使用缓存技术来缓存热点数据,目的是防止大量的请求打到Mysql上导致Mysql被打垮而崩溃。
假设现在Mysql中千万条数据量并且有几十万条是热点数据,那么如何实现Redis中始终存储的都是热点数据呢?
在介绍Redis如何持久保留热点数据之前先说两种算法:LRU和LFU
1、LRU和LFU算法
LRU:最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。
LFU:最近最不经常使用,选择最近使用次数最少的页面予以淘汰。对于每个节点,都需要维护其使用次数count、最近使用时间time。
针对这两种算法,使用图形演示其保留数据的不同点。
如果队列使用的是LRU算法保留数据
(1)访问数据后的队列数据效果
如果现在是访问了数据3,那么队列中3数据将会在头节点的位置,如下图所示:
(2)添加数据后队列中的数据效果
现在如果有新的数据5添加到队列中,此时队列的头节点是5,尾节点上将数据4移出,如下图所示:
如果队列使用的是LFU算法保留数据,结构如下:
(1)访问数据后的效果
假设现在访问数据2,那么访问之后数据2的次数增加1并且更新最近使用的时间。效果如下:
(2)添加数据
假设现在添加一个数据5,那么添加数据后的效果如下:
LFU删除节点的策略是: 优先删除使用次数Count最小的那个节点,因为它最近最不经常使用所以删除它。如果使用次数相同并且节点有多个,那么在这些节点中删除最近使用时间time最早的那个节点。
2、Redis中热点数据持久保留的方案
Redis的数据是存储在内存中,这也就意味着Redis的内存是有限的。为了解决内存不足的问题,Redis提供了多种数据的淘汰策略,以下是Redis的内存淘汰策略的整理:
淘汰策略
描述
noeviction
当内存不足时,禁止淘汰数据,写入操作报错。这是 Redis 默认的内存淘汰策略。
allkeys-random
当内存不足时,从所有的 key 中,随机选出数据进行淘汰
volatile-random
当内存不足时,从设置了过期时间的 key 中,随机选出数据进行淘汰
volatile-ttl
当内存不足时,从设置了过期时间的 key 中,选出即将过期的数据(按照过期时间的先后,选出最先过期的数据)进行淘汰
allkeys-lru
当内存不足时,从所有 key 中使用 LRU 算法,选出最近使用最少的数据进行淘汰
volatile-lfu
当内存不足时,从设置了过期时间的 key 中使用 LFU 算法,选出使用频率最低的数据进行淘汰
allkeys-lfu
当内存不足时,从所有 key 中使用 LFU 算法,选出使用频率最低的数据,进行淘汰
volatile-lru
当内存不足时,从设置了过期时间的 key 中使用 LRU 算法,选出最近使用最少的数据进行淘汰
在高并发场景下缓存热点数据一般都是采用LRU或者LFU方案实现,因为其他方案对热点数据不友好。
如果采用Reids的LRU方式缓存热点数据,那么会将最近在redis中没有的数据缓存起来,如果空间的不足的情况下会移出队列中最近未访问的数据,如下图所示:
但是本方案存在一个很严重的问题,假设数据4使用了1万次,其他的数据只使用了一次,那么对应的先后使用时间上的关系如下:
此时将数据4移除就不适合了,因为它是使用次数最高的,只是最近时间没有被访问,极端的情况下可能因为大量请求来访问数据4,此时数据4在Redis中刚好被移除,那么请求将会都打到Mysql上进而导致Mysql被打垮
如果Redis中采用LFU算法缓存热点,那么内存不足的情况下会清理到最近不常用的数据,然后存储热点数据:
此方案存储热点数据比LRU方式更加合理,因为它只会清理那些不常用的数据,针对高并发下缓存大批量的热点数据建议采用这种LFU的方式。
那么Redis提供了volatile-lfu、allkeys-lfu,具体使用哪种需要根据实际的业务来选择。Redis中的配置淘汰策略如下:
#redis.conf 配置策略maxmemory-policy allkeys-lfu/volatile-lfu#指定redis使用使用的内存大小maxmemory 1024总结:在高并发且热点数据量很大的情况下,建议使用Redis的lfu方式淘汰数据,因为此方式更加科学合理。在某些热点数据访问量一样的,那么在淘汰数据的时候依据时间来淘汰,极端情况下被清理掉的热点数据下一时刻被大量访问,此时要做一下系统的保护(最简单的是加锁访问数据库)。