Memcached:故障排查
发布时间:2026-04-25 16:01
一、前言
搞过的人都知道,最烦的是Memcached挂了但应用层面才感知到延迟爆炸,等你反应过来都不知道从哪查起。今天说点实际的,不废话,直接告诉你怎么在生产环境里快速定位Memcached故障,从进程状态到内存分配到连接问题,一条链路撸到底。
二、操作步骤
第一步:确认进程还在不在
命令:
ps aux | grep memcached | grep -v grep
预期输出:
memcache 1234 0.2 1.2 524288 45632 ? Sl 10:30 2:45 /usr/bin/memcached -d -p 11211 -u memcache -m 64 -c 8192
如果输出为空,说明进程已经挂了:
systemctl status memcached # CentOS/RHEL 7+
service memcached status # CentOS 6 / Ubuntu
预期输出:
● memcached.service - Memcached
Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2024-01-16 10:30:00 CST; 2h 30min ago
Main PID: 1234 (memcached)
CGroup: /system.slice/memcached.service
└─1234 /usr/bin/memcached -d -p 11211 -u memcache -m 64 -c 8192
第二步:检查端口监听状态
命令:
netstat -tlnp | grep 11211
预期输出:
tcp LISTEN 0 1024 0.0.0.0:11211 0.0.0.0:* 1234/memcached
tcp LISTEN 0 1024 [::]:11211 [::]:* 1234/memcached
如果只有IPv6监听,应用连不上,检查是不是绑定问题:
ss -tlnp | grep 11211
第三步:验证基本连接和读写
命令(telnet方式):
telnet localhost 11211
预期输出:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
然后输入stats命令测试:
stats
预期输出(关键指标):
STAT pid 1234
STAT uptime 9025
STAT time 1705401234
STAT version 1.6.12
STAT libevent 2.1.12-stable
STAT pointer_size 64
STAT rusage_user 12.345
STAT rusage_system 45.678
STAT curr_connections 15
STAT total_connections 1234
STAT curr_items 4567
STAT bytes 12345678
STAT limit_maxbytes 67108864
STAT evictions 0
...
或者用nc(Ubuntu常用):
echo "stats" | nc -q 1 127.0.0.1 11211
预期输出:
STAT pid 1234
STAT uptime 9025
...
END
第四步:检查内存使用是否达到上限
在stats输出里重点看这几个指标:
STAT bytes 60000000
STAT limit_maxbytes 67108864
STAT evictions 1234
STAT reclaimed 5678
判断逻辑:
- bytes ≈ limit_maxbytes:内存满了,会触发evictions导致缓存命中率暴跌
- evictions > 0且持续增长:说明频繁被踢出缓存对象,这是性能杀手
- reclaimed > 0:Memcached在回收过期key的空间,说明内存紧张
快速检查命令:
echo "stats items" | nc -q 1 127.0.0.1 11211 | head -20
预期输出:
STAT items:1:number 1234
STAT items:1:number_hot 0
STAT items:1:number_warm 1234
STAT items:1:number_cold 0
STAT items:1:age 3600
STAT items:1:evicted 12
STAT items:1:evicted_time 120
...
第五步:检查连接数是否超标
命令:
echo "stats" | nc -q 1 127.0.0.1 11211 | grep -E "curr_connections|max_connections"
预期输出:
STAT max_connections 8192
STAT curr_connections 156
如果curr_connections接近max_connections:
- 应用侧连接池配置有问题
- 存在大量短连接
- 连接泄漏(应用代码bug)
查看系统层连接数:
ss -s | grep memcached
# 或
netstat -an | grep 11211 | grep ESTABLISHED | wc -l
预期输出:
Total: 158 (kernel 0)
TCP: 12 (2)
ESTAB: 10
...
第六步:检查命中率是否正常
命令:
echo "stats" | nc -q 1 127.0.0.1 11211 | grep -E "get_hits|get_misses|cmd_get"
预期输出:
STAT cmd_get 1234567
STAT cmd_set 89012
STAT get_hits 1200000
STAT get_misses 34567
STAT delete_hits 1234
STAT delete_misses 567
...
计算命中率:
echo "scale=4; 1200000 / 1234567 * 100" | bc
预期输出:
97.2000
低于95%的命中率就要警惕了,可能原因:
- 缓存key设计不合理
- 内存不足导致频繁eviction
- 应用逻辑变化,缓存未预热
第七步:排查网络延迟问题
从应用服务器测试延迟:
echo -e "set test_key 0 60 5\r\nvalue\r\nquit\r\n" | nc -q 1 127.0.0.1 11211
预期输出:
STORED
用python脚本测试多次取平均延迟:
python3 -c "
import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 11211))
# 测试set延迟
start = time.time()
s.sendall(b'set test_latency 0 60 1\r\nx\r\n')
s.recv(1024)
print(f'SET延迟: {(time.time()-start)*1000:.2f}ms')
# 测试get延迟
start = time.time()
s.sendall(b'get test_latency\r\n')
s.recv(1024)
print(f'GET延迟: {(time.time()-start)*1000:.2f}ms')
s.close()
"
预期输出:
SET延迟: 0.35ms
GET延迟: 0.28ms
延迟超过5ms就要检查:
- 网络链路
- CPU竞争(其他进程占用CPU)
- 大量小对象导致IO次数过多
第八步:检查日志(如果有配置)
CentOS/RHEL默认日志路径:
/var/log/memcached
Ubuntu默认日志路径:
/var/log/memcached.log
或者查看systemd日志:
journalctl -u memcached -n 50 --no-pager
预期输出:
Jan 16 10:30:00 server01 systemd[1]: Started Memcached Server.
Jan 16 12:00:00 server01 memcached[1234]: Accepting connections on port 11211
三、常见问题FAQ
Q1:stats命令正常,但应用连不上,报Connection Refused
老手解答:这不是Memcached的问题,是绑定地址的问题。Memcached默认只监听localhost,你从远程连肯定连不上。
检查当前配置:
netstat -tlnp | grep memcached
如果看到:
tcp LISTEN 127.0.0.1:11211 0.0.0.0:* 1234/memcached
说明只监听了127.0.0.1,需要改成监听所有地址或指定IP:
vim /etc/sysconfig/memcached # CentOS/RHEL
# 修改 OPTIONS="-l 0.0.0.0"
# Ubuntu
vim /etc/memcached.conf
# 修改 -l 0.0.0.0
⚠️ 警告:监听0.0.0.0之前必须确保iptables/防火墙已配置,否则裸奔!
Q2:Memcached进程突然挂了,没有core dump,怎么查原因?
老手解答:OOM Killer干掉的,先查系统日志:
dmesg | grep -i memcached
预期输出:
[12345.678901] memcached[1234]: segfault at 7f1234567890 ip 00007f1234567890 sp 00007fff12345678 error 4 in libevent-2.1.so.6
或者:
[12345.678901] Out of memory: Killed process 1234 (memcached) total-vm:2048000kB, anon-rss:2048000kB
如果是OOM,解决方案:
- 减少Memcached的-m参数(内存)
- 增加系统vm.overcommit_memory=1
- 或者机器加内存
如果是segfault,升级Memcached版本或者libevent版本。
Q3:内存还有余量,但evictions一直在涨,什么鬼?
老手解答:你设置的max_item_size太小了,大对象直接被拒,客户端那边以为是eviction。
检查当前配置:
memcached -h | grep -i max
默认max_item_size是1MB(1048576 bytes),如果你的对象超过这个大小:
- 要么分割对象
- 要么加大限制:启动时加-I参数,如memcached -I 10m
另一个坑:slab预分配问题。查看stats slab:
echo "stats slabs" | nc -q 1 127.0.0.1 11211
看看各slab的利用率是否均衡,如果某个slab 100%利用而其他空闲很多,说明对象大小分布不均,考虑调整-f factor参数重新分配slab。
Q4:服务重启后缓存全空,命中率归零,怎么处理?
老手解答:Memcached设计就是无持久化的,重启即清空,这是正常行为,不是故障。
如果想平滑过渡:
- 滚动重启:先起新实例,等缓存预热完成再停老实例
- 或者接受冷启动的峰值,在应用层加本地缓存兜底
- 长期方案:考虑Redis Cluster或者Memcached集群做高可用
核心原则:Memcached就是用来丢数据的,重启前要想清楚业务能否接受冷启动。
四、总结
核心要点
- 进程在不在 → ps + systemctl status
- 端口通不通 → netstat -tlnp
- 服务健康否 → echo "stats" | nc localhost 11211
- 内存满了没 → bytes vs limit_maxbytes,evictions是否增长
- 连接超没超 → curr_connections vs max_connections
- 命中率正常否 → get_hits / cmd_get,应该在95%以上
- 延迟大不大 → 本地测试单次操作应该在1ms以内
排障思路总结
从外向内查:网络层(端口监听) → 进程层(进程状态) → 服务层(stats命令) → 资源层(内存/连接/命中率)。大多数问题在前三层就能定位。
延伸阅读