当前位置:首页 > 问答 > 正文

Redis重试次数别忘了控制,抓紧机会别无限试错才行

行,咱们就聊聊这个“Redis重错次数”的事儿,这事儿说起来不大,但要是没弄好,真能惹出大麻烦,你别看Redis平时响应快得像闪电,就觉得它坚不可摧,它也是个服务,是服务就可能出毛病——网络抽风了、服务器内存不够了、Redis自己扛不住压力挂掉了,这都是常有的事。

(来源:日常后端开发中的常见问题)

当你的程序去连Redis,或者向Redis发命令发现没反应的时候,第一反应肯定是“重试一下试试”,这个想法没错,一次不成,万一第二次就成了呢?但坑就坑在这个“万一”上,如果你不加控制,让程序傻乎乎地一直重试,那可就坏了菜了。

想象一下这个场景:半夜里,Redis因为某种原因彻底宕机了,你的一个核心服务,它有个功能严重依赖Redis,这个服务发现Redis连不上了,它没多想,本着“坚持不懈”的精神,每隔100毫秒就尝试重连一次,发一次命令,结果呢?Redis那边死气沉沉,根本不会回应,但你的服务可没闲着,它每一次重试,都会占用着一个线程(或者一个协程),消耗着系统的CPU和内存资源,它还在痴痴地等待回应,这个等待也是有超时时间的,比如设了2秒,这意味着什么?意味着短时间内,你的服务会创建出成千上万个“等待Redis回应”的任务,这些任务迟迟等不到结果,又不断有新的重试任务加进来。(来源:对系统资源消耗的推演)

很快,你的应用服务器自己就先撑不住了,内存被这些等待的任务占满,CPU忙于调度这些无用的任务,导致其他不依赖Redis的正常业务请求也处理不了了,这就叫“雪崩效应”——本来只是一个下游依赖(Redis)故障,但由于没有做好隔离和容错,导致故障向上蔓延,最终让你的整个应用都瘫痪了,这就好比家里一个水龙头坏了,你没及时关总闸,反而拼命想去修,结果导致整个屋子都被水淹了,Redis没用好,反而把自己整个系统都拖垮了,这可就太不划算了。

那该怎么避免这种“自杀式”的重试呢?核心思想就一句话:抓紧机会,但别无限试错。 你得给重试设个“底线”。

最直接的就是限制重试次数,别没完没了地试,连续试个3次、5次,最多不超过10次,如果这么几次都失败了,基本上就可以断定不是偶然的网络抖动,而是Redis那边出了真问题,这时候,就应该果断放弃,把这次操作标记为失败,然后走失败处理的逻辑,如果是查询数据,可以返回一个空值或者默认值;如果是写入操作,可以先把数据记录到本地日志或者一个临时队列里,等Redis恢复了再同步过去。

光限制次数还不够聪明,因为如果你连续快速重试,每次失败的原因可能都一样(比如网络还没恢复),所以第二个要点是重试要有间隔,并且最好间隔时间逐渐变长,这种方法有个挺形象的名字,叫“指数退避”,第一次失败后等1秒再试,第二次失败后等2秒再试,第三次等4秒……这样间隔时间指数级增加,这既给了系统一定的恢复时间,避免了在问题瞬间被解决时错过机会,又防止了在长时间故障时过于频繁的无用功。(来源:分布式系统常见的容错设计模式)

第三个很重要的点是要能区分不同的错误类型,不是所有错误都值得重试,如果你的命令本身语法就有问题,Redis返回了一个错误,那你重试一万遍也还是错,这种错误应该立即失败,而不是进入重试循环,只有对于那些可能 transient(临时性)的故障,比如网络超时、连接拒绝等,才值得去重试。

别忘了设置一个全局的超时时间,哪怕你重试了N次,每次都有间隔,但从第一次尝试开始,整个操作不应该无限制地等下去,你可以设定整个重试过程最多持续30秒,超过30秒无论重试到第几次,都强制失败,这能保证你的服务响应不会变得不可接受地慢。

说白了,控制重试次数和策略,是一种“优雅的失败”的艺术,它承认分布式环境中失败是常态,但要求系统在失败面前要有“自知之明”,知道什么时候该坚持,什么时候该放弃,这不仅仅适用于Redis,对于任何外部依赖,比如数据库、第三方API调用,都是一个道理。(来源:微服务架构下的通用容错理念)

你别觉得这是小题大做,线上系统很多莫名其妙的“全站瘫痪”,追根溯源,往往就是某个角落里的一个无限重试逻辑引发的血案,花点时间,给你的Redis客户端配置一个合理的重试策略,就像是给系统买了一份“意外险”,平时感觉不到它的存在,真出了事,它能帮你把损失降到最低,让你能抓紧真正解决问题的机会,而不是在无限试错的循环里把自己给绕进去。

Redis重试次数别忘了控制,抓紧机会别无限试错才行