Redis CLIENT LIST 详解:连接排查与内存分析的实战指南 1. 从一条命令说起为什么我建议运维先掌握CLIENT LIST先抛一个很常见的场景某天下午线上Redis实例突然响应变慢INFO里used_memory看着还行hit ratio也没崩但业务方反馈接口超时。你登录服务器敲了个redis-cli info clients看到connected_clients从平时的三百多涨到了一千二可这又能说明什么呢连接多了到底是业务流量涨了还是有连接泄漏哪些连接是空闲的哪些在刷屏哪些是主从复制用的完全是一笔糊涂账。这时候CLIENT LIST才是真正值得先敲的命令。它不是那种花哨的高级特性但如果你能把它的输出看明白绝大多数连接层面的问题都能在五分钟内定位到方向。简单说CLIENT LIST用来查看当前Redis实例上所有客户端连接的详细信息每个连接一行字段很多从哪个IP来的、用了哪个端口、当前在干什么、空闲了多久、有没有事务、是否被阻塞、内存占了多少。它解决的问题很直接当Redis出现异常时快速判断是不是客户端连接导致的并精准揪出“罪魁祸首”连接。适合谁看日常维护Redis的运维工程师、被线上问题折腾的Java/Go开发、以及刚接触Redis想加深理解的初学者。这篇不是官方文档的翻译而是把我在几个项目里用这条命令排查真实故障的完整思路和细节写出来包括那些没人提醒你、但实际一定会踩的坑。2. 核心细节拆解CLIENT LIST输出的每个字段都得看懂2.1 字段全景一行连接信息里到底藏了多少线索执行redis-cli client list后输出大概长这样id3 addr127.0.0.1:56324 laddr127.0.0.1:6379 fd8 name age0 idle0 flagsN db0 sub0 psub0 multi-1 qbuf26 qbuf-free4096 argv-mem10 multi-mem0 obl0 oll0 omem0 tot-mem22518 eventsr cmdclient|list userdefault redir-1 resp2 id5 addr127.0.0.1:56325 laddr127.0.0.1:6379 fd9 name age148 idle0 flagsN db0 sub0 psub0 multi-1 qbuf0 qbuf-free0 argv-mem0 multi-mem0 obl0 oll0 omem0 tot-mem22458 eventsr cmdget userdefault redir-1 resp2首次接触的人很容易被这一长串吓到我最早也差不多后来慢慢摸清规律把它拆成四组去看本质上就通了。第一组是身份信息id连接唯一标识、addr客户端IP和端口、laddrRedis本地地址和端口、fd文件描述符、name通过CLIENT SETNAME设置的连接名称、userACL用户。第二组是状态信息age连接已存在秒数、idle空闲秒数、flags连接状态标志、db当前选中的数据库编号、multi事务内命令计数-1表示不在事务中。第三组是内存/缓冲信息qbuf输入缓冲已用字节、qbuf-free输入缓冲空闲字节、argv-mem、multi-mem、obl、oll、omem、tot-mem。第四组是当前行为信息events正在等待的事件、cmd最近一次执行的命令、redir重定向相关主要用于集群、respRESP协议版本。2.2 flags状态位读懂连接正处于什么生命周期flags是最容易被忽略但信息量最大的字段它不是单值而是字母组合。我实际排查中见得最多的几种N普通客户端无特殊状态这是绝大多数连接的常态。M主节点primary在复制场景下主库连接从库时会带这个标志。S从节点replica从库连上主库后主库视角下这个连接显示为S。x连接正被CLIENT PAUSE暂停或者处于事务中的MULTI/EXEC期间。b客户端被阻塞通常是因为WAIT、BRPOP、BLPOP或SUBSCRIBE等阻塞式命令。O连接即将被关闭可能是客户端发了CLIENT KILL或超过了超时设置。d该连接的key刚被某个事件标记为脏dirty一般不用太关注。A连接被关闭通常是最大内存淘汰策略触发或管理员主动操作。flags里的字母是区分大小写的小写和大写代表完全不同的状态。我见过有同事把S当成“slow”查了半天实际上是replica。掌握这些标志你才能精准回答一个最常见的问题这个连接是正常业务连接还是复制连接还是已经被污染的坏连接。2.3 缓冲与内存字段qbuf、omem和tot-mem的隐藏含义很多人只看addr和cmd忽略了缓冲字段但真正定位大key、慢查询、内存暴涨的时候最有用的是这几个qbuf是输入缓冲区已用字节数。正常情况下客户端发一条GET命令qbuf只有几十字节。如果业务是用管道批量写、或者发送超大key/valueqbuf会飙升。这里有个关键点单个客户端输入缓冲区被硬限制为1GB超过就会被服务端强制断开日志里会出现 Closing client that reached max input buffer length 的提示。如果你在CLIENT LIST里看到某条连接的qbuf高达数百MB那基本可以断定是业务侧有超大请求或者管道发送异常需要立刻处理。omem是输出缓冲区已用字节数它和obl、oll一起看obl表示固定缓冲区的已用字节oll表示链表缓冲区的对象数量omem则是总字节数。输出缓冲区没有1GB那么大的硬限制但如果你用CONFIG GET client-output-buffer-limit查看配置会发现普通客户端默认是normal 0 0 0无限制从库是slave 256mb 64mb 60硬限制256MB软限制64MB持续60秒发布订阅是pubsub 32mb 8mb 60。我踩过一次很深的坑一个业务用Redis做发布订阅消费者处理慢消息堆积在输出缓冲区omem冲到几十MB没触发限制结果就是Redis主线程频繁fork做持久化时内存翻倍整机OOM。后来查CLIENT LIST才发现罪魁祸首正是那几条SUBSCRIBE连接。从此我养成习惯每周扫一次omem超过10MB的连接。tot-mem是这条连接总共消耗的内存包括了输入输出缓冲和内部结构开销是最直观的“这条连接吃了多少内存”的指标。如果你排查内存碎片只看INFO里的used_memory_rss是不够的配合CLIENT LIST按tot-mem排序能快速找出哪几个连接是内存大户。3. 实操玩法不只是看一眼CLIENT LIST的高级用法3.1 按条件精确过滤只查你关心的那类连接Redis 6.0以上版本给CLIENT LIST加了过滤参数这个功能实在太顺手了。用法是CLIENT LIST [TYPE type] [ID client-id]TYPE可选值有三个normal普通客户端排除主从复制和订阅类连接master主节点连接replica从库连接pubsub发布订阅连接实际案例某团队怀疑是主从复制导致主库延迟跑redis-cli client list type replica发现只有两条S连接排除法立刻完成问题焦点转向业务端慢查询。这个过滤逻辑的价值在于你不用再从几百上千行输出里人肉grep了。另外ID过滤是精确查找指定连接。线上用CLIENT LIST ID 12345查一条可疑连接时输出只含那一条排查效率提升一个量级。3.2 和INFO、MONITOR组合一条命令变成一套诊断工具CLIENT LIST单独用是一维视角配上其他命令就成了立体诊断网络。我的习惯顺序是第一步INFO clients看总览connected_clients、blocked_clients、tracking_clients等建立宏观印象。如果blocked_clients很高说明大量连接在阻塞等待需要配合CLIENT LIST看是哪些命令导致。第二步CLIENT LIST全量抓取连接状态重点看flags里的b阻塞、idle异常大的空闲连接、cmd异常的最近执行的命令。第三步SLOWLOG GET 50如果CLIENT LIST里发现多个连接都在执行同一条命令比如KEYS *或HGETALL一个超大哈希那慢日志大概率能对上号。举个例子有一次某游戏服务器出现间歇性卡顿我先看到INFO里connected_clients正常但CLIENT LIST里出现了二十多条连接的cmdhgetall而且多条omem数值不小说明返回结果集很大。再看SLOWLOG果然HGETALL一个包含数十万个字段的哈希耗时3秒多。那个哈希是某运营系统写入的全局配置平时没人读某天新功能上线后每个请求都去读它直接把Redis主线程拖垮了。解决方法是把大哈希拆成多个小哈希或者改为本地缓存。3.3 用CLIENT LIST定位连接数暴涨的根源连接数从几百涨到几千不等于就是业务并发上来了。用CLIENT LIST按addr看来源IP分布能快速分辨是单机集中还是分布均匀。我遇到过addr里90%都来自同一台应用服务器的场景最后查出来是那台机器上某个线程池配置错误每次请求都新建连接且不释放导致连接泄漏。配合netstat -anp | grep 6379 | wc -l可以互相印证但netstat只看TCP层看不出Redis应用层状态CLIENT LIST的不可替代性就在这里。如果你的Redis实例开启了timeout配置空闲连接超时CLIENT LIST里idle字段是判断“哪些连接永远不释放”的最直接依据。我一般这样统计redis-cli client list | awk {print $5} | cut -d -f2 | sort -n | tail -20这条命令按idle秒数排序只取最大的20个连接。如果发现一堆idle只有几秒的连接反复出现说明应用连接池配置有问题如果idle动辄几千秒还没断开说明应用忘记关连接同时Redis的timeout没设置生效。4. 实战复盘一次CLIENT LIST救场的完整过程4.1 问题现象与初步判断某电商项目做促销压测Redis从库节点CPU突然飙到90%主库正常。从库CPU高通常有三种可能过期key主动删除、持久化RDB重写、或者大量读请求打到从库。运维同事第一时间去看从库的INFOconnected_clients稳定在500左右没大波动这有点反常——如果大量读请求打过来CPU高但也应该伴随命令处理量上升可total_commands_processed只涨了一点。4.2 CLIENT LIST抓出真凶这时候我建议他跑redis-cli client list从结果里发现了一个反常细节大量连接的cmdreplconf且flagsS。正常从库只有一条S连接哪来几百条再仔细一看addr全部来自一个IP——正是另一个机房的一台老旧缓存中间件服务器。原来这套Redis做了读写分离有一个历史遗留的Java SDK版本在启动时会重复建立从库连接且重连逻辑有bug断线后不会清理旧连接导致从库节点上堆积了几百条伪复制连接。这些连接会周期性发送replconf心跳虽然单个命令很轻但几百条叠加加上从库本身负载CPU就上去了。CLIENT LIST让我们一眼看出问题不是正常业务流量因为addr和flags的组合太特殊了。后来通过CLIENT KILL TYPE replica先把连接清掉再升级SDK从库CPU立刻恢复正常。事后复盘如果只依赖INFO的计数器这个问题大概率还要排查更久。4.3 CLIENT KILL的正确姿势与注意事项CLIENT LIST负责发现CLIENT KILL负责处理。常见用法# 杀掉某个IP的所有连接 redis-cli CLIENT KILL ADDR 192.168.1.100:12345 # 杀掉某个IP的所有连接旧写法只按IP不含端口 redis-cli CLIENT KILL IP 192.168.1.100 # 按类型杀 redis-cli CLIENT KILL TYPE pubsub # 按ID杀 redis-cli CLIENT KILL ID 12345这里有几个细节必须提醒CLIENT KILL会直接断开客户端连接业务侧如果没有重连机制会立刻报错。执行前先想清楚。新版Redis还支持CLIENT KILL USER、CLIENT KILL MAXAGE等更精细的过滤但我用下来最顺手的是TYPE和ADDR。如果只想断开空闲连接可以用CLIENT KILL ID id配合上面那个awk排序结果来批量处理但建议写成脚本时加个dry-run先打印不要直接执行。5. 踩坑记录与问题排查速查表5.1 容易忽略的细节坑第一个坑CLIENT LIST输出里有些字段只在特定版本存在。比如tot-mem是Redis 4.0引入的argv-mem和multi-mem更晚Redis 2.8时代的老版本没有这些字段。如果你线上是混合版本写脚本解析时按字段名切割就会踩坑。稳妥做法是先redis-cli INFO server确认版本再决定解析哪几个字段。第二个坑发布订阅连接的cmd字段显示的是订阅命令而不是实际推送的消息。比如一条cmdsubscribe的连接它不代表正在执行慢操作只是处于订阅状态。判断订阅连接是否异常要看的是omem和lastinteraction而不是cmd。第三个坑CLIENT LIST输出的addr在IPv6环境下的格式处理。IPv6地址包含冒号用冒号分割字段解析时会把地址截断。我见过有人写脚本用冒号做分隔符在IPv6机器上直接乱套。正确做法是用split( )按空格切再按等号取k/v不要用冒号。第四个坑CLIENT LIST本来就是个O(N)命令N是客户端连接数。连接数几千上万时这个命令本身会占用一些主线程时间。虽然单次执行通常是毫秒级但如果你写个脚本每秒钟跑一次在高连接数场景下就是给Redis添乱。建议监控频率设在30秒以上。第五个坑开启了ACL的环境CLIENT LIST可能不会显示所有连接的完整信息。默认用户可以看到所有连接但受限ACL用户即使有CLIENT LIST权限能看到的信息也会被过滤。如果你发现CLIENT LIST输出内容变少了先检查ACL权限别急着怀疑Redis有bug。5.2 常见问题速查表现象排查命令组合可能的结论CPU高但命令量低CLIENT LIST大量伪复制连接或空闲连接心跳内存增长异常CLIENT LIST看omem/tot-mem排序发布订阅堆积或大key返回间歇性卡顿SLOWLOG CLIENT LIST慢命令集中在个别连接连接数只增不减CLIENT LIST按idle排序连接池泄漏或timeout未配置集群访问异常CLIENT LIST看redir字段客户端未正确处理MOVED重定向写超时但读正常CLIENT LIST type master主从延迟导致写入积压5.3 批量处理脚本辅助awk和python一行的用法排查时经常需要把CLIENT LIST输出转成结构化数据我常用的两种方式# 按tot-mem内存占用排序找出最占内存的20条连接 redis-cli client list | awk -F {for(i1;iNF;i){if($i ~ /^tot-mem/){print $i, $0}}} | sort -t -k3 -nr | head -20 # 按idle空闲时间排序找出最空闲的15条连接 redis-cli client list | awk -F {for(i1;iNF;i){if($i ~ /^idle/){print $i, $0}}} | sort -t -k3 -nr | head -15如果你更习惯用Python一段简单的解析逻辑可以把每行转成字典import subprocess def get_clients(host127.0.0.1, port6379): out subprocess.check_output([redis-cli, -h, host, -p, str(port), client, list]) clients [] for line in out.decode().splitlines(): fields dict(item.split(, 1) for item in line.split( )) clients.append(fields) return clients clients get_clients() big_mem sorted(clients, keylambda x: int(x.get(tot-mem, 0)), reverseTrue)[:10] for c in big_mem: print(c[addr], c[cmd], c[tot-mem])注意dict(item.split(, 1))里的1很关键因为addr字段里的IPv6地址可能本身就含等号虽然少见但用split(, 1)能避免字段被切碎。6. 构建系统化排查给新人一条可落地的命令路径刚接手Redis运维的同事总问我CLIENT LIST这么多字段到底该重点看哪几个我的建议是根据问题症状选视野连接数暴涨看addr分布、idle、flags内存异常看tot-mem、omem、qbuf慢操作看cmd、age、idle主从异常看flags、redir还有一个更系统的做法把CLIENT LIST的监控纳入你的巡检脚本定时抓取并自动告警。不需要太复杂一条bash加crontab就够#!/bin/bash redis-cli client list | awk -F {for(i1;iNF;i){if($i~/^omem/){split($i,a,); if(a[2]10485760) print $0}}}这条脚本可以找出输出缓冲超过10MB的连接一旦触发说明有异常堆积。我把类似逻辑整合到巡检里后有几个明显的收益第一问题从被动等业务反馈变成主动发现。以前是业务方说Redis连接异常我再去查现在是我先看到某条连接omem飙升提前通知业务侧调整。第二因为CLIENT LIST信息太全排查时间从小时级缩短到十几分钟。很多问题只要扫一眼flags和cmd就能锁定方向剩下的只是验证。第三方便沉淀知识库。把每次CLIENT LIST抓到的异常案例截图或记录时间久了就是一份完整的状态特征库新同事遇到相似情况可以直接对号入座。我个人在实际操作中的一个体会是CLIENT LIST虽然只是“看一眼”命令但它比很多花哨的监控工具都可靠。监控系统只能告诉你“出问题了”CLIENT LIST告诉你“具体是哪个连接、哪个IP、在干什么、干了多久”。有了这个维度Redis连接层的排查从“猜”变成了“看”。最后再分享一个扩展思路如果你的连接数长期处于高位CLIENT LIST的输出会有几千甚至上万行人眼看不过来。这时候可以把输出导入到日志分析平台按addr、flags、cmd做聚合统计自动发现“某个IP的500条连接全部在执行同一条命令”这类异常模式。这个思路放在Redis 7的CLIENT LIST输出上同样适用字段更丰富。排查工具不用太多把一条命令吃透胜过装一堆复杂的监控组件。