Redis进程异常退出排查

Redis进程异常退出排查

一、排查思路

1. 是否因为系统内存不足被oom killer杀掉;

如果是oom killer杀掉的话,一般会在/var/log/*留下日志,dmesg也应该能查到,可以使用命令搜索:

dmesg | egrep -i 'killed process'
grep oom /var/log/*
grep total_vm /var/log/*

如果确实是由于内存不足被oom killer杀掉,可以考虑:

  • 加大系统内存;
  • 修改redis.conf配置,设置合理的maxmemory,保证机器有20%~30%的闲置内存,并使用适当的回收策略;
maxmemory 512mb
maxmemory-policy volatile-lru
  • 降低redis进程的oom adj分数;

“OOM killer会在可用内存不足时选择性的杀掉用户进程,它的运行规则是怎样的,会选择哪些用户进程“下手”呢?OOM killer进程会为每个用户进程设置一个权值,这个权值越高,被“下手”的概率就越高,反之概率越低。每个进程的权值存放在/proc/{progress_id}/oom_score中,这个值是受/proc/{progress_id}/oom_adj的控制,oom_adj在不同的Linux版本的最小值不同,可以参考Linux源码中oom.h(从-15到-17)。当oom_adj设置为最小值时,该进程将不会被OOM killer杀掉。”

设置方法如下:

echo -17 > /proc/${process_id}/oom_adj
  • 优化redis的内存使用与设置;
    详情参考下文的介绍以及文末的相关链接。

2. 是否被人为kill掉

  • 如果是人为kill进程,可以搜索last和history的记录,查看是什么登陆用户在什么时间杀掉的;
  • 如果系统有安装audit,也可以使用ausearch搜索命令记录:
ausearch -sc kill

3. 是否因为redis-cli发送了shutdown命令导致

如果是cli发送的shutdown命令导致redis退出,redis本身的log会有如下记录:

3803:S 11 Oct 18:12:51.251 # User requested shutdown...
3803:S 11 Oct 18:12:51.251 * Removing the pid file.
3803:S 11 Oct 18:12:51.252 # Redis is now ready to exit, bye bye...

如果还没打开redis log,需要在redis.conf中指定路径然后重启:

dir "/opt/redis-4.0.9/redis-cluster/7000"

避免进程被cli远程关闭,可以设置密码,或者在redis.conf中禁用shutdown命令来实现,我们选择采用后者:

rename-command SHUTDOWN ""

4. 是否redis本身的bug导致崩溃

  • 设置系统ulimit,在进程崩溃时候生成core dump:
ulimit -c unlimited

修改/etc/sysctl.conf文件,加入如下配置指定core dump文件生成的名称和路径:

kernel.core_uses_pid = 1
kernel.core_pattern = /var/core/core_%e_%p

注意不同linux发行版可能设置的方式略有不同。

  • 使用strace命令跟踪进程行为:
screen -S redis_trace_7000
strace -T -tt -e trace=all -p `ps -ef|grep redis|grep 7000|grep -v -i screen|grep -v grep|awk '{split($0,a);print a[2]}'`

然后ctrl-a d退出screen

  • 使用gdb attach到redis进程

同样建议使用独立的screen执行gdb,另外,gdb需要使用ptrace,如果发现ptrace: Operation not permitted的报错,需要关闭其他已经使用ptrace命令的进程,例如strace命令。

gdb /usr/local/bin/redis-server 58414
(gdb) continue

使用redis-cli连上server,发送debug segfault指令可以使redis server崩溃,这时候可以:
使用bt查看栈信息

(gdb) bt
#0  debugCommand (c=0x7ffc32005000) at debug.c:220
#1  0x000000010d246d63 in call (c=0x7ffc32005000) at redis.c:1163
#2  0x000000010d247290 in processCommand (c=0x7ffc32005000) at redis.c:1305
#3  0x000000010d251660 in processInputBuffer (c=0x7ffc32005000) at networking.c:959
#4  0x000000010d251872 in readQueryFromClient (el=0x0, fd=5, privdata=0x7fff76f1c0b0, mask=220924512) at networking.c:1021
#5  0x000000010d243523 in aeProcessEvents (eventLoop=0x7fff6ce408d0, flags=220829559) at ae.c:352
#6  0x000000010d24373b in aeMain (eventLoop=0x10d429ef0) at ae.c:397
......

使用info registers查看寄存器信息

(gdb) info registers
rax            0x0  0
rbx            0x7ffc32005000   140721147367424
rcx            0x10d2b0a60  4515891808
rdx            0x7fff76f1c0b0   140735188943024
rsi            0x10d299777  4515796855
rdi            0x0  0
rbp            0x7fff6ce40730   0x7fff6ce40730
rsp            0x7fff6ce40650   0x7fff6ce40650
r8             0x4f26b3f7   1327936503
r9             0x7fff6ce40718   140735020271384
r10            0x81 129
......

最后,使用gcore产生core dump文件

(gdb) gcore
Saved corefile core.58414

二、内存使用优化

  1. 缩减键值对象
    在条件允许的情况下建议字符串长度控制在39字节以内,减少创建redisObject内存分配次数;
    缩减键(key)和值(value)的长度;
    key长度:如在设计键时,在完整描述业务情况下,键值越短越好。
    value长度:值对象缩减比较复杂,常见需求是把业务对象序列化成二进制数组放入Redis。首先应该在业务上精简业务对象,去掉不必要的属性避免存储无效数据。其次在序列化工具选择上,应该选择更高效的序列化工具来降低字节数组大小。在内存紧张的情况下,可以使用通用压缩算法压缩json,xml后再存入Redis,从而降低内存占用,例如使用GZIP压缩后的json可降低约60%的空间。
  2. 共享对象池
    使用共享对象池。整数对象池在Redis中通过变量REDIS_SHARED_INTEGERS定义,不能通过配置修改。可以通过object refcount 命令查看对象引用数验证是否启用整数对象池技术。使用共享对象池后,相同的数据内存使用降低30%以上。可见当数据大量使用[0-9999]的整数时,共享对象池可以节约大量内存。注意,当设置maxmemory并启用LRU相关淘汰策略如:volatile-lru,allkeys-lru时,Redis禁止使用共享对象池。
  3. 字符串优化
    尽量减少字符串频繁修改操作如append,setrange, 改为直接使用set修改字符串,降低预分配带来的内存浪费和内存碎片化。
  4. 编码优化
    针对性能要求较高的场景使用ziplist,建议长度不要超过1000,每个元素大小控制在512字节以内。
    当集合只包含整数且长度不超过set-max-intset-entries配置时使用intset编码。
    对于大量小对象的存储场景,非常适合使用ziplist编码的hash类型控制键的规模来降低内存。

三、参考资料

设置Redis最大占用内存
Redis的内存优化
Redis禁用命令、危险命令及规避方法
Redis debugging guide
Playing with ptrace, Part I
神器strace, ltrace
How to disable Redis RDB?
Redis研究(十九)—命令属性
Virtual memory settings in Linux - The Problem with Overcommit
What is the logic behind killing processes during an Out of Memory situation?
How to reproduce a condition which invokes the OOM-Killer ?

你可能感兴趣的:(Redis)