一次线上故障的复盘:连接池打满之后,我们改了三处地方
【摘要】 二号每日文章草稿:一次线上故障的复盘:连接池打满之后,我们改了三处地方线上出问题那天,现象很直接:接口大面积超时,错误日志里全是等待连接超时。查下来不是数据库挂了,是应用的连接池被打满了。 当时的数据应用实例:4 台,每台连接池上限 50,总共 200 个连接。正常情况下,每台同时在用的连接大概 20 个左右。故障高峰:某段慢查询把单次查询时间从平均 30 毫秒拖到了 2 秒以上,连接迟迟...
二号每日文章草稿:一次线上故障的复盘:连接池打满之后,我们改了三处地方
线上出问题那天,现象很直接:接口大面积超时,错误日志里全是等待连接超时。查下来不是数据库挂了,是应用的连接池被打满了。
当时的数据
- 应用实例:4 台,每台连接池上限 50,总共 200 个连接。
- 正常情况下,每台同时在用的连接大概 20 个左右。
- 故障高峰:某段慢查询把单次查询时间从平均 30 毫秒拖到了 2 秒以上,连接迟迟不还,4 台实例的在用连接很快顶到了上限。
- 后续请求排队等连接,等待超过 5 秒就超时,超时又触发上游重试,重试又来抢连接,越抢越满。
这个链条里,慢查询是起因,连接池打满是放大器,重试是第二次放大。
改的第一处:把慢查询找出来,先止血
我们先把慢查询日志打开,按耗时排序,前 10 条里有 3 条是同一张表的大范围扫描。表里有 800 万行,查询条件里有个字段没建索引,执行计划是全表扫描。
处理办法很具体:给那个字段加了索引,查询从 2 秒以上降到 40 毫秒以内。加索引是在业务低峰做的,执行时间 3 分钟,期间没有锁表导致写入阻塞。
止血之后,在用连接从满格回落到每台 25 个左右。
改的第二处:连接池参数按实测改,不按默认值
原来每台池子 50 个,等待超时 5 秒,最大空闲时间 10 分钟。改完之后:
- 每台池子上限从 50 降到 30。不是越大越好,池子越大,数据库那边同时在线的连接越多,数据库本身的线程切换和锁竞争也会变大。实测 30 个够用,峰值在用连接在 28 个以下。
- 等待超时从 5 秒降到 1 秒。等 5 秒再超时,用户早就走了,还白白占着排队位置。1 秒超时后快速失败,至少让调用方能立刻走降级,而不是在队列里排 5 秒。
- 空闲回收从 10 分钟改到 2 分钟,空闲连接及时还回去,数据库那边的空闲连接数从 180 个降到 60 个左右。
改完压测:模拟慢查询重现,单台在用连接顶到 30 后,后续请求 1 秒内快速失败,不再出现 5 秒排队超时堆积。
改的第三处:重试加熔断,不让重试把池子打满第二次
原来上游重试是无差别的:超时就重试,最多 3 次。故障时重试流量是正常流量的 2.8 倍。
改成:
- 只有读请求可以重试,写请求不重试,避免重复写入。
- 重试最多 1 次,且重试间隔 200 毫秒,不立即重试。
- 连续 10 次超时后,熔断 30 秒,这 30 秒内直接走降级返回,不再发请求打数据库。
熔断这 30 秒里,降级返回的是缓存里的上一份结果,页面能打开,只是数据不是最新。故障演练里,熔断触发后,数据库在用连接在 10 秒内从 120 个降到 40 个以下。
复盘时记下的三条
- 连接池打满很少是池子本身的问题,先看是谁占着连接不还,慢查询日志比池子参数更重要。
- 池子上限不是越大越好,要按数据库能承受的并发和实测峰值定。
- 重试必须有限制,没有熔断的重试在故障时会把一次小故障放大成全站故障。
这三处改完之后,同一类慢查询又出现过一次,在用连接顶到 28 个就停住了,没有再打满,也没有触发大面积超时。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)