前两天帮朋友排查线上问题,发现他们的 Redis 集群疯狂报错,响应时间从毫秒级直接飙到秒级,数据库连接数爆满,应用层也跟着雪崩。他一脸懵地问我:“Redis 不是号称单机 10 万 QPS 吗?我这才几千请求怎么就不行了?”我看了看代码,哭笑不得——这家伙每次请求都新建 Redis 连接,用完也不关闭,连接池形同虚设。这其实不是 Redis 本身的问题,而是对 Redis 的访问姿势出了问题。

很多人把 Redis 当成黑箱,觉得装上就能用,访问就是 “set/get” 两下完事。但真相是,Redis 的访问模式直接决定了它的性能和稳定性。你是在用连接池还是每次新建连接?网络延迟有没有被压到极致?读写比例和数据结构选型对不对?这些细节就像开车时的油门和刹车,踩不对地方,再好的发动机也会废。我见过太多项目,Redis 本身没毛病,代码写得稀烂,锅全甩给数据库。
连接池是 Redis 访问的第一道坎。生产环境里,每次新建 TCP 连接消耗巨大,三次握手加 TLS 加密,一次连接就要几十毫秒。如果并发稍微上来,连接还没来得及复用,请求就排队等着建连接,CPU 和内存全耗在握手上了。正确做法是使用连接池,比如 JedisPool 或 Lettuce,把连接数控制在合理范围,例如 200 左右。但连接池也不是越大越好,池子太大,线程竞争锁会让性能反而下降。我之前调过一个项目,连接池设了 1000 个,结果请求一多,线程全在抢连接,吞吐量直接腰斩。所以连接数要跟应用线程数和 Redis 的 CPU 核数匹配,通常两到三倍 CPU 核心数就够用。
网络延迟是另一个隐形杀手。Redis 在本地和在远程,性能差距可能达到一个数量级。假设 Redis 在另一台机器上,网络 RTT 为 1 毫秒,那么一次请求至少要 1 毫秒,1 秒只能处理约 1000 个请求。即使 Redis 本身只需要 0.001 毫秒,网络延迟也把性能锁死了。因此线上部署时,Redis 与应用服务器最好在同机房同交换机,或者使用 Unix socket 直连。如果必须跨机房,就要用 pipeline 批量发送命令,把多次网络往返合并成一次。比如要查询一千个 key,逐个 get 需要 1000 次往返,用 pipeline 一次搞定,性能提升几百倍。我见过有人用 for 循环挨个查 Redis,耗时几分钟,换成 pipeline 后秒级完成。
数据结构的选型也经常被忽略。很多人习惯用 String 存一切,比如把用户信息序列化成 JSON 丢进去,取出来再反序列化。这很浪费,因为每次操作都要序列化和反序列化,CPU 开销大。更合适的做法是用 Hash 存储对象字段,每个字段单独操作或批量读取。比如用户信息有 name、age、email,用 Hash 存,更新年龄时只改一个字段,不用把整个 JSON 拉下来再写回去。同样,List 适合做消息队列,Set 适合做去重,ZSet 适合做排行榜。选错结构,轻则浪费内存,重则操作复杂度飙升。比如用 List 做去重,每次插入前都要遍历检查,复杂度 O(n),换成 Set 直接是 O(1)。
访问频率和缓存策略也是门学问。Redis 是内存数据库,数据全在内存里,访问速度极快,但内存成本高,容量有限。如果热点数据没有被缓存,或者缓存没有淘汰策略,Redis 很快就会被不常用的数据塞满。常见做法是设置过期时间,TTL 不要太长,例如 10 分钟。同时使用 LRU 或 LFU 淘汰策略,把最不常用的数据踢出去。但有个坑:如果缓存雪崩,大量 key 同时过期,请求全打到数据库上,数据库会挂掉。所以要加随机过期时间,在基础 TTL 上加减几秒,让过期时间分散。或者使用二级缓存,Redis 挂了还有本地缓存顶着。
高并发场景下,Redis 的单线程模型既是优势也是软肋。Redis 单线程处理命令,所有命令顺序执行,不会出现并发写冲突。但这也意味着如果一个命令执行时间过长,会阻塞后续所有命令。比如使用 KEYS 查询所有匹配的 key,若 key 数量上百万,Redis 会卡死几秒,其他请求只能排队等着。因此线上禁止使用 KEYS,改用 SCAN 游标遍历。同样,一次写入大量数据时,用 pipeline 分批发送,别用一条命令塞几万条数据。还有,避免使用慢查询命令,例如 SMEMBERS 取大集合所有元素,可以用 SSCAN 替代。
监控和限流也不能少。Redis 虽快,但扛不住无限请求。如果某个接口突然被刷,Redis 连接数和 CPU 会瞬间飙满,影响其他服务。所以要加限流,比如用 Redis 自身的 INCR 加过期时间,或者在 Nginx 层做限流。同时监控 Redis 的慢查询日志,定期检查哪些命令执行时间超过阈值,例如 100 毫秒以上。我一般会设置慢查询日志长度为 200 条,定期拉取分析,把慢操作优化掉。另外,内存使用率也要盯紧,超过 80% 就要考虑扩容或清理数据,否则 Redis 可能 OOM 被杀。
Redis 访问不是写完代码就完事的。线上环境里,网络抖动、资源竞争、代码 bug 都会让 Redis 成为瓶颈。我见过一个项目,连接池配置完美,但业务代码里有个死循环,每秒生成几万个 Redis 请求,直接把连接池打爆。还有一次,同事把 Redis 当关系数据库用,所有数据都不设过期,结果半年后内存占用 200 GB,机器直接卡死。所以 Redis 访问要当成系统工程来对待:从连接管理、网络优化、数据结构选型、过期策略,到监控和限流,每一步都要想清楚。别以为 Redis 是万能药,它只是一个工具,用好了是加速器,用不好就是导火索。


