说明下这里的算法指的是内存淘汰算法。redis作为应用级缓存使用,在内存超过限制时,按照配置的策略,淘汰掉相应的kv,使得内存可以继续留有足够的空间保存新的数据。
算法介绍
redis 提供 6种数据淘汰策略:
- noeviction : 默认,不淘汰
- 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)中任意选择数据淘汰
以上6中淘汰策略已经能够满足大多数应用场景,如何选择淘汰策略?根据实际业务情况进行选择。
源码分析
其缓存管理功能,由redis.c文件中的freeMemoryIfNeeded函数实现。如果maxmemory被设置,则在每次进行命令执行之前,该函数均被调用,用以判断是否有足够内存可用,释放内存或返回错误。如果没有找到足够多的内存,程序主逻辑将会阻止设置了REDIS_COM_DENYOOM flag的命令执行,对其返回command not allowed when used memory > ‘maxmemory’的错误消息。源码如下:
1 | int freeMemoryIfNeeded(void) { |
算法源码解析
淘汰算法redis.h的相关定义
1 | /* The actual Redis Object */ |
1 | unsigned int getLRUClock(void) { |
以最简单的all-keys-lru淘汰策略为例,该策略随机选出16个,通过过期时间对pool内存数据进行淘汰。
1 | /* volatile-lru and allkeys-lru policy */ |
通过lru淘汰返回lru时间,最后与当前lru比较
1 | /* Given an object returns the min number of milliseconds the object was never |
测试验证
要将redis最终应用到生产环境中,稳定性、可靠性测试更为重要,下面将对淘汰算法相关进行测试。对于性能的测试另有文章,请关注之后的性能篇。
说明
在基于Redis Cluster的分布式缓存部署篇(三)中设置maxmemory为100M,保证服务不会在高并发情况下宕机。最后设置maxmemory-policy要测试的淘汰策略。
用例代码
java编写的测试程序,没有讲究代码结构,请见谅。
1 | package jcache; |
用例执行命令
1 | java -cp jedis-2.9.0.jar:commons-pool2-2.3.jar: RedisClusterDataCorrect 100 10000 10 10 1 0 |
用例参数说明
- 第一个参数是线程数量
- 第二个参数是每个线程执行操作次数
- 第三个参数是key的大小(字节)
- 第四个参数是val的大小(字节)
- 第五个参数是运行次数
- 第六个参数是操作类型,0标示set,1标示get
noeviction策略
测试用例
每条数据1K,运行200M的数据,也就是测试204800条数据的读取和写入。
1 | java -cp jedis-2.9.0.jar:commons-pool2-2.3.jar: RedisClusterStrategy 1024 204800 |
测试结果
当集群的运行内存超过最大使用内存后,测试程序抛异常。noeviction策略不淘汰数据。
查看redis的key数量
allkeys-lru策略
测试用例
每条数据1K,运行200M的数据,也就是测试204800条数据的读取和写入。
1 | java -cp jedis-2.9.0.jar:commons-pool2-2.3.jar: RedisClusterStrategy 1024 204800 |
测试结果
内存占用到达最大值后会置换之前的数据,所有测试数据能全部跑完
查看redis的key数量
volatile-lru策略
测试用例
每条数据1K,运行200M的数据,也就是测试204800条数据的读取和写入。
1 | java -cp jedis-2.9.0.jar:commons-pool2-2.3.jar: RedisClusterStrategy 1024 204800 |
测试结果
- 内存占用到达最大值后,如果没有设置过期的数据仍在添加,程序会抛出异常。
- 调整测试用例,前100条不设置过期时间,后面全部设置过期时间,程序能正常跑完。
查看redis的key数量
allkeys-random策略
测试用例
每条数据1K,运行200M的数据,也就是测试204800条数据的读取和写入。
1 | java -cp jedis-2.9.0.jar:commons-pool2-2.3.jar: RedisClusterStrategy 1024 204800 |
测试结果
内存占用到达最大值后会置换之前的数据,所有测试数据能全部跑完
查看redis的key数量
volatile-random策略
测试用例
每条数据1K,运行200M的数据,也就是测试204800条数据的读取和写入。
1 | java -cp jedis-2.9.0.jar:commons-pool2-2.3.jar: RedisClusterStrategy 1024 204800 |
测试结果
- 内存占用到达最大值后,如果没有设置过期的数据仍在添加,程序会抛出异常。
- 调整测试用例,前100条不设置过期时间,后面全部设置过期时间,程序能正常跑完。
查看redis的key数量
volatile-ttl策略
测试用例
每条数据1K,运行200M的数据,也就是测试204800条数据的读取和写入。
1 | java -cp jedis-2.9.0.jar:commons-pool2-2.3.jar: RedisClusterStrategy 1024 204800 |
测试结果
调整测试用例过期时间设置从3600秒开始逐渐增加。内存占用到达最大值后会先移除最近过期的数据,所以程序能全部跑完。
查看redis的key数量
总结
- 启用内存策略之前,需要设置maxmeory配置项,即节点的最大内存使用值。
- 考虑到持久化数据的缓存场景,选择自己的淘汰策略,一般情况建议设置为noeviction策略。
- 根据使用场景,适当调低rewrite-percentage百分比,降低内存此时的最小内存值,以防止在进行rewrite时,使用大量内存导致进程挂死问题。