Redis崩溃调试

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: ## 背景 Redis的代码质量一直被业内人士称赞,在极高的业务压力下也能有很好的稳定性。但是极端情况下,Redis也是有可能会Crash的。有时候因为种种原因,系统配置问题,磁盘空间写满了,进程权限不够等等,我们可能不会运气那么好,有一个`core`文件可以拿去调试。这个时候,Redis提供了几种异常崩溃情况下的`Crash Report`,很多时候我们基于Crash Report,再加上

背景

Redis的代码质量一直被业内人士称赞,在极高的业务压力下也能有很好的稳定性。但是极端情况下,Redis也是有可能会Crash的。有时候因为种种原因,系统配置问题,磁盘空间写满了,进程权限不够等等,我们可能不会运气那么好,有一个core文件可以拿去调试。这个时候,Redis提供了几种异常崩溃情况下的Crash Report,很多时候我们基于Crash Report,再加上一定的分析就可以直接定位问题了。

Crash Report

在异常崩溃时,Redis会通过设置的signal handler来生成Crash Report,目前Redis为生成Crash Report捕获的异常信号主要有以下几种:

  • SIGSEGV
  • SIGFPE
  • SIGILL
  • SIGBUS

这4种信号应该能包含大部分程序异常崩溃情况了,最常见的就是SIGSEGV段错误了,除0异常SIGFPE有时候也会遇到。

当Redis收到上面4种信号之一时,会在设置的sigsegvHandler()函数中生成Crash Report,如下,

=== REDIS BUG REPORT START: Cut & paste starting from here ===
[19179] 12 Apr 18:47:42.599 #     Redis 2.8.19 crashed by signal: 11
[19179] 12 Apr 18:47:42.599 #     Failed assertion: <no assertion failed> (<no file>:0)
[19179] 12 Apr 18:47:42.599 # --- STACK TRACE
/home/dejun.xdj/kvs-kernel/src/libredis-server.so(logStackTrace+0x4a)[0x7f5be2d6895a]
/home/dejun.xdj/kvs-kernel/src/libredis-server.so(debugCommand+0x1b0)[0x7f5be2d69ad0]
/lib64/libpthread.so.0(+0xf500)[0x7f5be3c1a500]
/home/dejun.xdj/kvs-kernel/src/libredis-server.so(debugCommand+0x1b0)[0x7f5be2d69ad0]
/home/dejun.xdj/kvs-kernel/src/libredis-server.so(call+0x8a)[0x7f5be2d2f12a]
/home/dejun.xdj/kvs-kernel/src/libredis-server.so(processCommand+0x5dd)[0x7f5be2d3017d]
/home/dejun.xdj/kvs-kernel/src/libredis-server.so(processInputBuffer+0x4d)[0x7f5be2d3b86d]
/home/dejun.xdj/kvs-kernel/src/libredis-server.so(readQueryFromClient+0xf0)[0x7f5be2d3cb70]
/home/dejun.xdj/kvs-kernel/src/libredis-server.so(aeProcessEvents+0x13d)[0x7f5be2d2804d]
/home/dejun.xdj/kvs-kernel/src/libredis-server.so(aeMain+0x2b)[0x7f5be2d2833b]
/home/dejun.xdj/kvs-kernel/src/libredis-server.so(runRedis+0x4f)[0x7f5be2d31eaf]
/home/dejun.xdj/kvs-kernel/src/redis-server /home/dejun.xdj/local/redis/conf/redis_7071.conf *:1071(main+0x180)[0x405db0]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x3d4ac1ecdd]
/home/dejun.xdj/kvs-kernel/src/redis-server /home/dejun.xdj/local/redis/conf/redis_7071.conf *:1071[0x4055e9]
[19179] 12 Apr 18:47:42.599 # --- INFO OUTPUT
...
...
[19179] 12 Apr 18:47:42.599 # --- CLIENT LIST OUTPUT
[19179] 12 Apr 18:47:42.599 # id=2 addr=127.0.0.1:30494 fd=5 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=debug read=0 write=0 type=admin next_opid=-1

[19179] 12 Apr 18:47:42.599 # --- CURRENT CLIENT INFO
[19179] 12 Apr 18:47:42.599 # client: id=2 addr=127.0.0.1:30494 fd=5 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=debug read=0 write=0 type=admin next_opid=-1
[19179] 12 Apr 18:47:42.600 # argv[0]: 'debug'
[19179] 12 Apr 18:47:42.600 # argv[1]: 'segfault'
[19179] 12 Apr 18:47:42.600 # --- REGISTERS
...
...
=== REDIS BUG REPORT END. Make sure to include from START to END. ===

       Please report the crash by opening an issue on github:

           http://github.com/antirez/redis/issues

  Suspect RAM error? Use redis-server --test-memory to verify it.

Crash Report主要包括4部分,

  • STACK TRACE,崩溃时的调用栈信息
  • INFO OUTPUT,崩溃时的Redis info命令输出
  • CLIENT OUTPUT,包括崩溃时的CURRENT CLIENT,这个可以看到崩溃时客户端执行的命令
  • REGISTERS,寄存器信息

其中对于调试最有帮助的就是STACK TRACE信息了,我们直接以上面的Crash Report来说明一下如何调试。

调试

Redis提供了debug segfault命令用于调试,我们直接给Redis发送这个命令,就可以在日志中生成类似于上面的崩溃报告(请不要在生产环境使用这个命令!!!)。

实际调试之前先说明一下,如果编译时没有加上-g选项,可执行文件中没有调试符号信息,是无法进行后面的调试的,考虑到性能的影响很小,Redis默认编译是带有-g选项的。

上面的STACK TRACE信息直接告诉我们了,出core的点在libredis-server.so中,简单分析可以知道是在执行debugCommand时出现段错误,函数后面的+号带的地址是函数内的代码偏移,我们只要知道函数的起始地址就可以获取出core的函数内代码地址了,进而可以通过addr2line获取地址对应的具体的源文件名和行号。

通过nm工具获取函数起始地址,

$nm -l /home/dejun.xdj/kvs-kernel/src/libredis-server.so  | grep debugCommand
000000000006d920 T debugCommand /home/dejun.xdj/kvs-kernel/src/debug.c:255
000000000007f3f0 T pfdebugCommand       /home/dejun.xdj/kvs-kernel/src/hyperloglog.c:1455

我们可以看到debugCommand的起始地址是0x6d920(十六进制),加上偏移0x1b0,可以知道出core的具体地址是0x6dad0,那么我们就可以很方便的获取到具体的行号了,

$addr2line -e /home/dejun.xdj/kvs-kernel/src/libredis-server.so 0x6dad0
/home/dejun.xdj/kvs-kernel/src/debug.c:304

参考源文件我们可以发现debug.c的304行存在非法地址访问,

*((char*)-1) = 'x';

总结

以上只是为了说明调试流程,举的一个简单例子,有时候定位了出core的点,可能还需要更为细致的分析,结合info输出和client输出。在没有core文件的场景下,Crash Report确实能够提供很大的帮助,上面的流程,有兴趣的同学可以直接做成一个脚本,直接分析日志,自动化的获取出core点的信息。

参考:http://antirez.com/news/43

诚聘英才:阿里云-技术专家-KVstore

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
4月前
|
NoSQL Linux Redis
redis源码调试---vscode使用技巧-----C语言跳转到函数定义
redis源码调试---vscode使用技巧-----C语言跳转到函数定义
73 0
|
28天前
|
NoSQL 关系型数据库 MySQL
开发者福音:用IDEA和Iedis2加速Redis开发与调试
开发者福音:用IDEA和Iedis2加速Redis开发与调试
49 0
开发者福音:用IDEA和Iedis2加速Redis开发与调试
|
4月前
|
缓存 NoSQL 关系型数据库
Redis 7.0 源码调试环境搭建与源码导读技巧
Redis 7.0 源码调试环境搭建与源码导读技巧
62 0
|
NoSQL IDE Java
使用CLion调试Redis源码的超详细步骤
因为我本人主要是写Java的,有强烈的IDE依赖症,不喜欢使用文本编辑器或者命令行这样的工具,所以选择使用CLion搭建一个IDE环境来辅助 Redis 源码阅读。
505 1
使用CLion调试Redis源码的超详细步骤
|
监控 NoSQL 数据库
监控redis命令--monitor--redis调试
monitor是什么? 有时候我们需要知道客户端对redis服务端做了那些命令操作。我们可以试用monitor命令来查看。 他能清楚的看到客户端在什么时间点执行了那些命令 MONITOR 是一个调试命令,每个命令流回来的redis服务器处理。
2774 0
|
NoSQL Java Redis
Redis 一二事 - 在spring中使用jedis 连接调试单机redis以及集群redis
Redis真是好,其中的键值用起来真心强大啊有木有, 之前的文章讲过搭建了redis集群 那么咋们该如何调用单机版的redis以及集群版的redis来使用缓存服务呢? 先讲讲单机版的,单机版redis安装非常简单,不多说了,直接使用命令:  1 [root@nginx bin]# .
726 0
|
存储 NoSQL Redis
redis ins 调试
Redis简介: Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。
797 0
|
Web App开发 NoSQL Java
JAVA Web项目中用到Redis的代码调试
JAVA Web项目中用到Redis的代码调试方法 在项目开发中用到了Redis,需要调试输出数据的正确性, 方法一:修改代码,暂时不从Redis取数据 方法二:清除Redis数据库中的所有key 登录Redis服务器,切换目录到Redis的目录: cd /opt/redis-2.
582 0
|
1月前
|
存储 NoSQL 算法
09- Redis分片集群中数据是怎么存储和读取的 ?
Redis分片集群使用哈希槽分区算法,包含16384个槽(0-16383)。数据存储时,通过CRC16算法对key计算并模16383,确定槽位,进而分配至对应节点。读取时,根据槽位找到相应节点直接操作。
65 12
|
1天前
|
NoSQL 算法 Java
深入浅出Redis(八):Redis的集群模式
深入浅出Redis(八):Redis的集群模式