Redis 内存故障诊断及常用运维命令

背景

你是否有过这种困扰:我的数据量非常小,但还是报OOM错误?

# 一个简单set提示内存不足
[root@10-186-61-38 redis]# redis-cli -p 9999 set actionsky 1
(error) OOM command not allowed when used memory > 'maxmemory'.

首先我给大家解释下,Redis的OOM分两种

  • 一种是因Redis使用内存超出OS物理内存,OS将Redis进程杀死。
  • 另一种是Redis使用内存超过maxmemory参数配置,引发Redis Server层OOM。
    OOM是Redis最常见的内存故障,它影响很大:
  • 故障发生时,进程并不会退出,能读但无法写入
  • 配置了allkeys-lru、allkeys-lfu等内存淘汰策略场景下,会有大量键失效,导致缓存命中率急剧下降。
    本文中,我会给大家分享下该种内存问题的排查方向及运维命令

Redis内存消耗划分

image.png

简短介绍下Redis内存消耗划分情况,为下文诊断提供思路。上图可以总结Redis消耗内存分如下几块:

  • 对象内存:理论上占用最大,存储所有业务数据,如字符串类型、哈希类型对象等
  • 客户端内存:包括输入客户端(查询或写入命令)、输出客户端使用的内存,因为不受maxmemory参数控制,这块我们需重点排查
  • 复制积压缓冲:所有从库客户端共享、保存固定大小的写入命令用于从库失连后数据补偿
  • Redis自身内存:存储数据元数据信息、过期键字典等
  • AOF缓冲区:AOF持久化、重写缓冲区,一般占用很少,基本不需要关注

内存OOM会导致哪些问题?

  1. Redis 无法写入,只能读取


    image.png
  2. Redis 大量键被逐出内存或过期,导致Redis查询效率降低(maxmemory-policy配置为非默认值noeviction时)
    image.png

排查思路

注意:下文不做特别说明的话,我的maxmemory设置为1G,其它任何参数为默认

是否数据量太大?

使用redis-benchmark持续灌入数据

image.png

检查内存使用情况,发生OOM状态时 used_memory 一定会大于 maxmemory
image.png

检查数据对象内存和其它内存使用情况如下图:
image.png

这里有必要说明下overhead.tatal,它包括除数据外Redis消耗的所有内存,比如前面提到的复制缓冲区、客户端输入输出缓冲区等,另外还包括一些元数据如overhead.hashtable,它是数据库中元数据消耗的内存大小,包括以下三项:

  • 整个数据库是一种hash表,首先就是这张hash表使用的内存
  • 每一个key-value对都有一个dictEntry来记录他们的关系,元信息便包含该db中所有dictEntry使用的内存
  • redis使用redisObject来描述value所对应的不同数据类型(string、list、hash、set、zset),那么redisObject占用的空间也计算在元数据
    大家对这个现象可能有点疑惑,为啥我明明设置maxmemory为1G,你Redis只给我存了990多M数据就满了?
    很好理解,根据上面测试可知数据达到一定规模后,因需消耗额外的元数据、缓存内存,Redis最终将超过maxmemory而OOM。

是否客户端输入缓冲区有问题?

制造输入缓冲区压力(防止干扰,先清空数据再压测)


image.png
# 关键参数解释
-d 表示每个set值的大小,单位为字节
-c 启多少个连接

压测几秒钟后,触发OOM


image.png

检查输入缓冲区内存消耗,能看到客户端输入缓冲区消耗总量为 2.4G左右,远远超过maxmemory参数设置。


image.png

那我如何找到消耗内存量最大的那个连接呢?
image.png

可通过运行上述检查命令,定位到各客户端输入缓冲区的内存消耗(由大到小排序)。
一般如果定位到有连接异常,可以使用如下命令杀掉

# 例如杀掉上图中 id=51421 的连接
127.0.0.1:9999> CLIENT KILL ID 51421
(integer) 1

是否复制积压缓冲区有问题?

为测试方便,我直接把复制积压缓冲区配置为800M。
开启redis-benchmark压测进程


image.png

检查复制积压缓冲区内存消耗,可以看到因为缓冲区设置过大,数据量才存储190多M,Redis就无法写入了。


image.png

是否客户端输出缓冲区有问题?

若客户端输出缓冲区太大如何排查?一般该场景比较少见,常见于用到了redis的monitor命令

注意:monitor 命令功能像MySQL的general-log,能打印Redis所有执行的命令。在生产环境极少使用或禁用

先开启monitor命令

image.png

通过redis-benchmark制造输出缓冲区压力。
image.png

测试一段时间后观察Redis内存消耗
image.png

此时数据库无法写入
image.png

检查输出缓冲区各客户端连接内存消耗、输出缓冲区总消耗内存如下
image.png

可以看到输出缓冲区总内存已远大于maxmemory限制,此时内存自然就OMM。

实用命令

上文排查过程有些Redis运维命令我认为比较实用,整理如下

模拟Redis压力相关命令

# 1. 持续给Redis灌数据
redis-benchmark -p 9999 -t set -r 100000000 -l
# 2. 模拟输入缓冲区过大
redis-benchmark -p 9999 -q -c 10 -d 102400000 -n 10000000 -r 50000 -t set
# 3. 模拟输出缓冲区过大
redis-benchmark -p 9999 -t get -r 5000000 -n 10000000 -d 100 -c 1000 -P 500 -l

常用Redis内存排查命令

# 1. 快速查看Redis内存是否够用
redis-cli -p 9999 info memory |egrep '(used_memory_human|maxmemory_human|maxmemory_policy)'
# 2. 检查复制积压缓冲区使用情况
redis-cli -p 9999 memory stats|egrep -A 1 '(total.allocated|replication.backlog)'
# 3. 检查客户端输入缓冲区内存使用总量
redis-cli -p 9999 client list| awk 'BEGIN{sum=0} {sum+=substr($12,6);sum+=substr($13,11)}END{print sum}'
# 4. 检查客户端输入缓冲区各客户端连接的内存情况
redis-cli -p 9999 client list|awk '{print substr($12,6),$1,$12,$18}'|sort -nrk1,1 | cut -f1 -d" " --complement
# 5. 检查客户端输出缓冲区内存使用总量
redis-cli -p 9999 client list| awk 'BEGIN{sum=0} {sum+=substr($16,6)}END{print sum}'
# 6. 检查客户端输出缓冲区各客户端连接的内存使用排序
redis-cli -p 9999 client list|awk '{print substr($16,6),$1,$16,$18}'|sort -nrk1,1 | cut -f1 -d" " --complement |head -n10
# 7. 检查数据对象使用内存总量
redis-cli -p 9999 memory stats|grep -A 1 'dataset.bytes'

总结

  • Redis内存问题大部分可以通过上诉排查思路进行定位

  • 整理了一些常用Redis内存排查命令

  • 整理了一份Redis内存检查脚本

#!/bin/bash
# Author:Renzy
# ModifyDate:20210408

if [ $# -ne 1 ];then
    echo -e "\033[31mERROR\033[0m: A port must be provided."
    echo "eg: sh $0 6379"
    exit 1
fi

PORT=$1
CLI_BIN=/data/redis/bin/redis-cli
EXEC="$CLI_BIN -p $PORT "

# Define checking memory available.
checkMem(){
    USED_MEM=`$EXEC memory stats |grep -A 1 total.allocated|tail -n1`
    MAX_MEM=`$EXEC info memory|grep -w maxmemory|awk -F ':' '{print $2}'`
    MEM_POL=`$EXEC info memory|grep -w maxmemory_policy|awk -F ':' '{print $2}'`
    DATA_USE=`$EXEC memory stats|grep -A 1 'dataset.bytes'|tail -n1`
    EXCEPT_DATA=`$EXEC memory stats|grep -A 1 'overhead.total'|tail -n1`
    REPL_USE=`$EXEC memory stats|grep -A 1 'replication.backlog'|tail -n1`
    INOUT_USE=`$EXEC client list| awk 'BEGIN{sum=0} {sum+=substr($12,6);sum+=substr($13,11)}END{print sum}'`
    OUTPUT_USE=`$EXEC client list| awk 'BEGIN{sum=0} {sum+=substr($16,6)}END{print sum}'`

    STATUS=`$EXEC set actionsky 1`
    if [ "$STATUS" = 'OK' ];then
        echo -e "Redis当前内存是否可用: \033\033[32m${STATUS}\033[0m"
    else
        echo -e "Redis当前内存是否可用: \033\033[31m${STATUS}\033[0m"
    fi
    echo "Redis当前内存淘汰策略: $MEM_POL"
    echo "Redis当前已使用的内存(byte): $USED_MEM"
    echo "Redis当前最大内限限制(byte): $MAX_MEM"
    echo "Redis当前数据对象已使用内存(byte): $DATA_USE"
    echo "Redis当前除数据外总内存消耗(byte): $EXCEPT_DATA"
    echo "Redis当前复制积压缓存区消耗(byte): $REPL_USE"
    echo " "
    echo "Redis当前客户端输入缓存总消耗(byte): $INOUT_USE"
    echo "Redis当前客户端输入缓存各连接消耗(TOP10):"
    $EXEC client list|awk '{print substr($12,6),$1,$12,$18}'|sort -nrk1,1 | cut -f1 -d" " --complement
    echo " "
    echo "Redis当前客户端输出缓存总消耗(byte): $OUTPUT_USE"
    echo "Redis当前客户端输出缓存各连接消耗(TOP10):"
    $EXEC client list|awk '{print substr($16,6),$1,$16,$18}'|sort -nrk1,1 | cut -f1 -d" " --complement |head -n10
}

checkMem

脚本执行效果:


image.png

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357

推荐阅读更多精彩内容