Redis 排障:连接、类型、延迟与内存
连接、WRONGTYPE、延迟与内存
本轮仅全文静态审计,未安装依赖、启动服务、连接数据库或执行任何示例。下文实测、RED/GREEN及平台结果均为此前历史证据;本轮修改后的脚本未运行,命令与输出作为学习步骤和预期保留。
连接拒绝、超时与认证
前置:命令起点。先区分transport错误和Redis错误回复。connection refused通常表示目标端口没有监听或映射错误;超时可能是网络、服务阻塞、客户端排队或资源压力;NOAUTH/WRONGPASS表示服务可达而认证未完成。不要把三者都当缓存未命中。
docker ps --filter name=showdocs-m4-redis-main
docker port showdocs-m4-redis-main
docker exec showdocs-m4-redis-main redis-cli -e PING
docker logs --tail 30 showdocs-m4-redis-main预期主机只显示127.0.0.1:26379映射,内部PING=PONG。容器内通而主机不通时检查映射与防火墙;两者都不通看启动日志和磁盘。Node应用超时后可能已经写入,限额CLI用同一请求ID重试,不能创建新ID来“解决”超时。生产客户端应有有限超时、有限重试、退避与指标,错误不能被静默吞掉。
WRONGTYPE、空值与 TTL=-1
WRONGTYPE表示命令与键类型不匹配。用TYPE核对,再查键命名和写入方。GET空是不存在;TTL=-2也表示不存在;TTL=-1表示当前存在且未设置到期时间,之后仍可能被删除、覆盖或按内存策略淘汰。尤其要排查普通SET覆盖清除了旧TTL。不要遇到WRONGTYPE就DEL修复,这可能删掉另一个业务拥有的数据。
docker exec showdocs-m4-redis-main redis-cli -e HSET m4:wrong field value
docker exec showdocs-m4-redis-main redis-cli -e GET m4:wrong
docker exec showdocs-m4-redis-main redis-cli -e TYPE m4:wrong
docker exec showdocs-m4-redis-main redis-cli -e HGET m4:wrong field
docker exec showdocs-m4-redis-main redis-cli -e DEL m4:wrong预期第二条非零退出并包含WRONGTYPE,第三条hash,第四条value,说明失败未清空原数据。只有确认是自己刚建的实验键才删除。校验器包含同类断言,只有读者另行执行时才连接服务,本轮未运行。
慢查询与延迟不是一回事
SLOWLOG记录服务器执行命令时间,不包含与客户端通信的网络I/O,也不等价于用户端总延迟。客户端慢而SLOWLOG空时应继续检查连接池等待、网络、响应大小与调度;不要据此认定Redis没有问题。INFO commandstats可观察累计调用和时间,计数是运行以来累积,比较采样差值更有用。
docker exec showdocs-m4-redis-main redis-cli -e INFO commandstats
docker exec showdocs-m4-redis-main redis-cli -e SLOWLOG LEN
docker exec showdocs-m4-redis-main redis-cli -e SLOWLOG GET 5
docker exec showdocs-m4-redis-main redis-cli -e LATENCY LATEST空慢日志或空延迟事件均是合法结果,不强造生产负载让它出现。检查slowlog阈值与latency-monitor配置;不要为了教学在共享生产环境调阈值或执行DEBUG SLEEP。大键全量读取、KEYS、长Lua循环、fork与磁盘压力都可能影响延迟。先界定慢在哪里,再限定查询范围与批量。MONITOR会输出所有命令,既增加开销又可能暴露参数,只能在受控短时排查中使用,不能当常驻监控。
OOM、驱逐与持久化失败
docker exec showdocs-m4-redis-main redis-cli -e INFO memory
docker exec showdocs-m4-redis-main redis-cli -e INFO stats
docker exec showdocs-m4-redis-main redis-cli -e INFO persistence
docker exec showdocs-m4-redis-main redis-cli -e CONFIG GET maxmemory maxmemory-policy看used_memory与maxmemory、evicted_keys和expired_keys差值;evicted是内存淘汰,expired是到期,不可混同。maxmemory=0通常表示没有该配置级别上限,不表示容器/宿主无限内存。OOM错误应检查策略与真实容量,不应无限重试。MEMORY USAGE只估算指定键占用;扫描全实例也有成本。
持久化看rdb_last_bgsave_status、aof_last_write_status和加载状态,结合容器日志与磁盘空间。MISCONF可能是配置要求快照失败时拒绝写入;不能直接关闭保护消除报错,先修权限、磁盘与失败原因。docker restart能恢复某些进程故障,却不能证明根因解决,也可能扩大服务中断。
Lua出错不回滚:本课程测试故意SET一个非整数后INCR,脚本返回错误但SET保留。限额脚本通过写前校验降低风险,仍没有数据库事务回滚或通用分布式锁保证。锁的租约过期、暂停后旧持有者继续执行等需要外部资源的fencing等约束;此批没有实现完整锁协议,旧锁资料保持独立入口。
复习题与参考解法
下面是完整的并发错误反例,先让20个调用者读到相同旧值,再各自SET,实现确定性的丢更新。依赖与配置同限额CLI。运行 node examples/redis/race-negative.mjs,预期输出expected=20、actual=1并以1退出;这是预期失败,不能改断言为1来“修好”。改用单条INCR,或运行已提供的Lua限额并发测试,才能满足对应不变量。
// Deliberately incorrect read/modify/write: this program MUST exit nonzero.
import assert from 'node:assert/strict';
import {randomUUID} from 'node:crypto';
import {command} from './quota.mjs';
const key=`m4:race:{${randomUUID()}}`;
try {
await command('SET',key,0,'PX',30000);
// A barrier makes the competing reads observe the same old value.
const old=await Promise.all(Array.from({length:20},()=>command('GET',key)));
await Promise.all(old.map(value=>command('SET',key,Number(value)+1,'KEEPTTL')));
const actual=Number(await command('GET',key));
console.log(JSON.stringify({expected:20,actual}));
assert.equal(actual,20,'lost updates: split GET/SET is not atomic');
} finally {await command('DEL',key);}提示:先分解端到端时间;把并发原子性、持久性、恢复验证作为不同维度。
- PING快但读取一个巨大Hash慢,可能在哪些环节?
- 为什么只增加客户端超时不能解决长Lua?
- 重启后限额减少,是否说明Lua并发测试错了?
- 备份文件存在能否勾选恢复已验证?
参考解法
- 数据遍历、序列化、网络传输、客户端解析或连接排队,按命令执行时间与端到端时间分开测;考虑字段读取或增量读取。2. Lua仍占用服务器执行时段,其他请求继续排队,应缩小脚本工作量。3. 并发原子性与持久性是两个维度,检查AOF同步、驱逐、恢复和故障切换;不能用内存缓存保证永久扣款。4. 不行,必须独立导入、启动、查询且确认时间范围和过期行为;本课程恢复验证实际断言标记值。
把旧 Redis 面试口诀改成有前提的回答
Redis 面试固定 QPS 单线程五类型有哪些边界?旧提纲把内存、简单数据结构和多路 I/O 复用作为性能线索,但固定读写 QPS 不能替代负载、命令复杂度、值大小、网络与版本说明。单线程也不能推出没有后台线程或任意操作都无阻塞;旧 VM 解释和“已知最快”结论撤回,不作为容量承诺。
Redis 支持复制就能自动切换零丢失吗?String 适合字节串和计数,Hash 表示字段对象,List 表示两端序列,Set 表示去重关系,Sorted Set 用 score 排序;这是五种基础结构,不是 Redis 仅有五种能力。内存容量、复制与持久化分别约束资源、数据传播和故障恢复,不能用“支持复制”推导自动切换或零丢失,也不能从无 Sentinel 的单主从说明推导所有部署均须手工改 IP。
事务要区分单条命令原子性、MULTI/EXEC 连续执行与 Lua 脚本边界;执行期错误不自动回滚全部成功写入。缓存回源是否变快必须比较相同请求和数据分布;Redis 故障时数据库能承受的流量也需另行测量。旧 BSD 许可、固定 QPS、VM、仅五类型和一概无自动容错的表述作为历史结论撤回;原图未做图像审阅,不把图作为证明。
Redis 连接失败
区分网络拒绝、TLS 不匹配和认证错误。
确认实例地址、端口及是否要求 TLS。密码使用交互式输入或安全的凭据机制,避免出现在命令历史中。
排查步骤
1. 探测实例并交互式认证
redis-cli -h <host> -p 6379 --askpass PING预期结果: 收到 PONG 表示连接与认证成功;记录其他错误原文。
2. 若服务要求 TLS,核对安全连接
redis-cli -h <host> -p <tls-port> --tls --cacert <ca.pem> --askpass PING预期结果: 证书受信且收到 PONG;普通端口与 TLS 端口不能混用。
3. 连接后确认目标实例
redis-cli -h <host> -p 6379 --askpass INFO server预期结果: 版本和实例信息来自预期服务器;排查时不要输出包含机密的完整配置。
常见报错
Connection refused
原因: 目标地址端口无可达监听,或服务未启动。
处理方式: 核对地址、端口、监听和防火墙,再检查 Redis 服务状态。
NOAUTH / WRONGPASS
原因: 服务要求认证或提供的凭据不匹配。
处理方式: 核对 ACL 用户、密码和客户端认证方式;不要关闭认证来绕过错误。
相关文档
官方资料
继续阅读
- 数据与原子性
- 数据与原子性
- 数据与原子性