阿里妹导读
背景
故障视角
数据库相关
故障案例1:某次压测对 DB 产生瞬时 60w+ QPS 的压力,期间同一条数据(更新 count 字段)在事务里大量热点更新导致了行锁争抢产生慢 SQL。
故障案例2:几个大用户高并发操作,其中涉及单条热点数据在事务里的更新,排查发现单次更新耗时高达5-6秒,积压的线程引起 Dubbo 对外服务线程池堆积,最终线程池满导致无法对外服务。
故障案例:大表添加字段未采用 Online DDL,在最后阶段会对表加 Metadata Lock 原子锁,使得大量相关 SQL 锁等待产生慢 SQL,进而快速打满应用线程池。
故障案例:凌晨 3 点多突然收到报警数据库 CPU 100%,排查发现某查询 SQL 走了主键索引触发了全表扫描(SQL 样例为:where a= and b= and c= and d= order by id desc limit 20,当时只有 idx_a_b_e 的联合索引),期间在数据库运维平台手工无差别限流 SQL 有所缓解但很快 CPU 又会飚上来,也尝试了物理删除一些无效数据减少数据量,多管齐下,最后通过临时增加一个 idx_a_b_c_d 新的全字段覆盖的索引止血。
故障案例:某账号(数据量巨大)调用某查询接口分页查询引发慢 SQL 导致数据库连接池满进而导致 Dubbo 线程池满无法对外服务,紧急限流该账号对该接口的调用后恢复。
调用量大
故障案例2:压测未预热,直接一次性并发到压测值导致线程池满,导致数据库有很多事务等待的慢 SQL。
解法:压测应按照一定节奏逐步上量,观察系统负载并及时暂定,而不是开局就决战。
其他
该 Case 不涉及线程池满问题,但笔者觉得有一定的代表性因此也分享下。不管是查询还是删除还是更新数据,不管是代码还是日常的 SQL 订正,建议都增加 Limit 来兜底保护自己,缩小影响面。
技术视角
1、尽量减少远程调用本身的 超时时间 以实现 fast-fail 快速失败。一般是设置 ConnectionTimeout 即握手时间 和 SocketTimeout 即业务执行超时时间。
2、在连接池满了以后,获取新的连接的 超时时间 也需要设置的小一些以实现 fast-fail 快速失败,这个是很容易忽略的一个点。如 Druid 里设置 MaxWait,Http 连接池里设置 ConnectionRequestTimeout。
Dubbo 线程池
如内部运维接口和对外服务的接口做隔离。
Http 连接池
故障案例:某次发布的代码引入了一个 SDK,该 SDK 集成了 HttpClient,但并没有设置 ConnectionTimeout,在某次网络抖动发生时,Http 连接池被迅速打满,进而导致业务线程池满导致服务受损。
故障案例:某 SDK 默认设置的 128,在某次压测中发现客户端耗时较高,但服务端耗时并无波动,排查后怀疑是 DefaultMaxPerRoute 太小导致的阻塞,调大后问题解决。
数据库连接池 Druid
故障案例:凌晨 1 点多收到 API 成功率降低报警,排查发现部分 SQL 执行超时,原因是数据库发生了主备切换,进一步排查发现应用侧对数据库连接池没有设置 SocketTimeout 导致切换前的老的连接不会被超时 Kill 导致相关 SQL 执行超时,直到 900秒系统默认超时后才会断开连接再次重连。
故障案例:在某次变更时由于代码有 bug 导致事务未提交,同时由于事务没设置超时时间,导致大量相关 SQL 超时服务受损。
自定义线程池
Redis连接池
总结
fast-fail 理念
保护好自己:流控/背压
谨慎重试
不在最上层自动重试,在单个节点里重试
令牌桶控制重试的速率
定时、周期性的作业需要打散,分散高峰。这块我们也遇到过类似的故障案例:
故障案例1:某客户端曾经出过一个类似故障:客户端的定时心跳同一秒发送到服务端,导致服务端扛不住,此类情况需适当打散。
你时常焦虑吗?
你时常焦虑吗?一般是在什么场景,工作或生活?我们是否掉入了“别人贩卖的焦虑”(PUA、35岁危机)的陷阱?
点击阅读原文参加话题讨论,截止2024年1月14日24时,我们将会选出 2 名幸运用户和 2 个优质回答分别获得阿里云开发者无线充电器一个。
【阿里云开发者公众号】读者群,是一个专门面向公众号的读者交流空间,你可以探讨技术和实践或参与群活动。欢迎添加微信:argentinaliu (备注读者群)入群。