背景
跑一个程序,程序采取redis存储任务状态及任务值,程序通过redis获取任务后进行任务下发
现象
正在运行的任务进度突然停止,查看执行应用日志,发现报错
日志报错信息
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
处理过程
根据报错猜测的可能原因有:
- 1 Jedis 对象用完以后,没有释放掉,被一直占用,所以会出现无法获取新的资源
- 2 redis服务找不到了
考虑到只有一个程序在连接redis,应该不会出现连接不够用的情况
所以先从redis的状态开始排查
1 查看redis状态
ps -ef | grep redis
发现redis进程没了,查看历史登陆和历史命令,没有人工停止的迹象
因为没发现什么问题,就手动重启了redis
/redis-server ../redis.conf &
重启后观察应用日志,发现开始又报新的错误:
redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.
redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk.
通过日志可以看到,报空间不足,所以进行查看服务器状态
查看磁盘空间显示是充足的
查看内存状态
free -m
发现内存剩余不足
登陆redis客户端查看内存使用情况
redis-cli -h ip -p port
info memory
参数详解:
used_memory : 由 Redis 分配器分配的内存总量,以字节(byte)为单位
used_memory_human : 以人类可读的格式返回 Redis 分配的内存总量
used_memory_rss : 从操作系统的角度,返回 Redis 已分配的内存总量(俗称常驻集大小)。这个值和 top 、 ps 等命令的输出一致
used_memory_peak : Redis 的内存消耗峰值(以字节为单位)
used_memory_peak_human : 以人类可读的格式返回 Redis 的内存消耗峰值
used_memory_lua : Lua 引擎所使用的内存大小(以字节为单位)
mem_fragmentation_ratio : used_memory_rss 和 used_memory 之间的比率
mem_allocator : 在编译时指定的, Redis 所使用的内存分配器。可以是 libc 、 jemalloc 或者 tcmalloc
对比几个值
1)当 rss > used ,且两者的值相差较大时,表示存在(内部或外部的)内存碎片。
内存碎片的比率可以通过 mem_fragmentation_ratio 的值看出。
2)当 used > rss 时,表示 Redis 的部分内存被操作系统换出到交换空间了,在这种情况下,操作可能会产生明显的延迟
参数参考:
https://jingyan.baidu.com/article/2c8c281dbd079f0008252a0f.html
发现redis使用内存已经4G了,比最开始启动时的内存多出来很多,查看相关资料了解:
Redis的内存如果不在启动前限制,默认是无限制的,32位的redis会限制在3G,目前使用的是64位,所以在使用过程中因为key的增加内存不断的增加
调节配置参数
总结实际修改过的的步骤:
- 1 将stop-writes-on-bgsave-error设置为no
stop-writes-on-bgsave-error no
- 2 配置maxmemory(部分起到作用)
maxmemory 内存大小(字节)
- 3 系统层面添加overcommit_memory
vim /etc/sysctl.conf
vm.overcommit_memory=1
使文件生效
sysctl -p /etc/sysctl.conf
- 4 修改redis
清理不使用的key(实际解决问题的办法)
实际试错的排查过程
1 查询资料说可能是bgsave失败,临时的解决办法是
stop-writes-on-bgsave-error yes修改为 stop-writes-on-bgsave-error no
bgsave方法的介绍:
结果:
修改了,重启,没效果
2 又看到需要修改系统层面参数
vim /etc/sysctl.conf
vm.overcommit_memory=1
使文件生效
sysctl -p /etc/sysctl.conf
结果:
内存仍然没有下去,不可用
3 修改redis key过期算法
maxmemory-policy volatile-lru
LRU是Least Recently Used 近期最少使用算法。
- volatile-lru -> 根据LRU算法生成的过期时间来删除。
- allkeys-lru -> 根据LRU算法删除任何key。
- volatile-random -> 根据过期设置来随机删除key。
- allkeys->random -> 无差别随机删。
- volatile-ttl -> 根据最近过期时间来删除(辅以TTL)
- noeviction -> 谁也不删,直接在写操作时返回错误。
结果:
内存占用还是4G,没有消减
4 修改maxmemory
查看redis相关资料,发现:
如果不设置maxmemory或者设置为0,64位系统不限制内存,32位系统最多使用3GB内存。
所以通过修改redis.conf配置,指定可使用的最大内存
修改maxmemory 3221225472(3G)
重启
结果:
启动后直接报错:
redis.clients.jedis.exceptions.JedisDataException: OOM command not allowed when used memory > 'maxmemory'.
使用config get * 命令得到redis 的maxmemory
>config get *
查看修改的配置是生效的
查看实际使用的内存:
>info memory
实际使用还是4G
看来还是key太大了,自动清理机制还是不能解决问题
解决
为了任务能继续跑下去,把key消费完,手动的调低同服务器其他机器的jvm启动参数,程序继续跑了下去
5 手动清理key
在任务跑的差不多时候,手动清理不需要的key值
总结
这次问题最原始的原因是没有做内存限制,导致随着key的不断增加,内存逐渐变多
以后在安装配置64位的redis时,最开始就要设置最大使用内存和清理key的算法,这样不会影响其他服务