FunctionGraph 突发扩容打 RDS:事务收尾、连接数和主备切换要一起算

举报
yd_255586444 发表于 2026/10/31 11:17:01 2026/10/31
【摘要】 函数平台的扩容是按请求来的,数据库的连接数是按实例算的,这两个数不对齐,是 FunctionGraph 连 RDS for MySQL 最常见的事故来源:200 个实例同时起来,每个实例的连接池开 5 条,事务期间又被数据库代理 1 比 1 钉住后端连接,RDS 侧瞬间多出 1000 条连接。连接数只是第一层,下面三件事——事务在实例冻结时怎么收尾、提交结果未知时怎么判、主备切换时旧连接怎么...

函数平台的扩容是按请求来的,数据库的连接数是按实例算的,这两个数不对齐,是 FunctionGraph 连 RDS for MySQL 最常见的事故来源:200 个实例同时起来,每个实例的连接池开 5 条,事务期间又被数据库代理 1 比 1 钉住后端连接,RDS 侧瞬间多出 1000 条连接。连接数只是第一层,下面三件事——事务在实例冻结时怎么收尾、提交结果未知时怎么判、主备切换时旧连接怎么淘汰——不一起算,压测时全是绿的,出事时全是红的。

事务不能活过单次调用:冻结不回滚,真死才回滚

先分清实例的三种状态,收尾策略完全不同。

冻结只是进程暂停调度,TCP 连接不断,未提交事务和它持有的行锁原样挂在 RDS 上,没有任何人会去回滚它。冻结一般发生在请求返回之后,所以纪律只有一条:任何事务必须在处理函数返回之前 commit 或 rollback,冻结永远发生在事务之外。顺带一条:不要在响应之后用后台线程续跑事务,它会被冻在事务中间,解冻后还可能拿着过期会话继续执行,比实例直接死了更麻烦。

实例被回收、执行超时被强杀,反而是最干净的结局:进程消亡,套接字关闭,FIN 到达代理,代理关闭后端连接,MySQL 在连接断开时回滚未提交事务、释放行锁,秒级且确定。

最坏的是没死透:网络分区或沙箱被硬停,套接字没关,RDS 侧只看到一条闲在事务中的连接。这时回收靠一条超时链:代理的空闲连接超时先收,其次才轮到 RDS 的 wait_timeout——默认值是 28800 秒,对这个场景等于没有,必须改小到 600 到 1800 秒量级。这里还有个常见误解要纠正:innodb_lock_wait_timeout 管的是等锁的人等多久放弃,它不回收持锁的人,只能让被孤儿事务堵住的后来者快速失败,锁本身还得靠超时链和巡检释放。

函数侧再补两手:语句级 socket 超时,让卡住的语句在实例还活着时报错并走显式 rollback;再加一个定时巡检,扫 innodb_trx,把开启超过阈值的事务结合 processlist 确认后 KILL,最坏持锁时间就能从小时级压回分钟内。

SELECT trx_id, trx_started, trx_mysql_thread_id
FROM information_schema.innodb_trx
WHERE trx_started < NOW() - INTERVAL 120 SECOND;

过代理之后,连接数要按事务期占用算

多语句事务过不了代理的会话保持这一步:事务一旦开始,或者出现临时表、用户变量、预处理语句,代理必须把这条前端连接钉到同一条后端连接,后续语句才在同一个会话里。钉住的代价是事务期间前后端连接 1 比 1 占用,所以事务必须短,事务里绝不能等外部调用——等一次 HTTP 接口的 3 秒,就是一条后端连接被白白占住 3 秒。

连接数预算直接按最坏情况算:

后端连接峰值 ≈ 函数最大实例数 × 单实例池大小
200 实例 × 5 条 = 1000 条,还没算其他应用和运维连接

单实例池压到 1 到 5 条,靠函数的并发上限去截实例数,比在 RDS 侧盲目调 max_connections 靠谱:max_connections 调大了,连接背后的内存和上下文切换照样会把实例拖慢。

残留状态的高发地不在代理,在实例自己的连接池:同一实例内,上一个请求异常退出、连接没回滚就还池,下一个请求借到它,就能看见上一个事务、临时表和用户变量。防御是异常路径显式 rollback 再还池,业务上不用临时表、用户变量这类会话级状态,既防污染,也减少不必要的会话钉住。

COMMIT 回执丢了:唯一约束加撞键回查

提交结果未知是最容易被写错的一段:COMMIT 在服务端已经成功,但回执在实例死亡或超时中丢了,客户端不知道成没成。这时先纠正一个前提——查库存、扣库存、写订单如果本来就在同一个事务里,断了就整体回滚,不存在扣了没写的中间状态;会重复扣减,只发生在三步被拆成多个事务的设计,那要先改回一个事务。

剩下的未知窗口,靠三件套:同事务、业务键唯一约束、撞键之后回查。重放撞上唯一键,按业务键把订单查出来核对关键字段,是同一笔就当已成功返回;查不到,说明上一遍确实回滚了,完整重跑。扣减语句本身再写成带下限的:

UPDATE stock SET num = num - 1
WHERE sku_id = ? AND num >= 1;
-- 影响行数为 0 就是没扣成,不要再往下写订单

不建议再叠一层事务外的一次性 token,比如在缓存里 SETNX 一个标记:它的状态和数据库事务天然不一致,标记还在但事务回滚了会误挡重放,标记先过期但事务其实提交了又挡不住,最后还是要回数据库对账。真到了跨库、带外部调用、没法同事务的那天,再上幂等表——但幂等记录必须和业务写在同一个本地事务里落库。死锁 1213 和锁等待超时 1205 则整事务重试,有限次加抖动退避,重试单位是整个事务,不是失败的那条语句。

四个时间参数的对齐顺序

参数打架比参数缺失更常见,给一条链和一组经验值,具体字段名以控制台版本为准:

  • innodb_lock_wait_timeout(5 到 15 秒)要小于客户端 socketTimeout(15 到 30 秒),让服务端先报 1205,客户端拿到确定性失败。反过来客户端先超时、服务端语句还在排队,它之后可能拿到锁并执行成功,就成了幽灵执行,只能靠幂等回查兜底。
  • 函数执行超时要大于整个事务的重试预算,也就是重试次数乘以单次超时再加退避总和,否则平台会在重试中途杀掉处理函数,未知窗口被人为放大。
  • 连接寿命是一条链:函数侧连接池 maxLifetime(180 到 300 秒)小于代理前端空闲回收,小于代理后端空闲回收,不大于 RDS 的 wait_timeout。谁排前面谁先回收,下游就不会用到已被上游关掉的连接;池活得比代理久,借到的就是代理已关的连接,第一句就报错。
  • 池子本身配借出校验:isValid 加 1 到 2 秒校验超时,idleTimeout 60 到 120 秒。解冻后的实例里可能躺着被中间网络设备悄悄断掉的半开连接,校验能把失败从第一条业务语句提前到借连接阶段。

主备切换多停的那几分钟,通常不在数据库侧

切换执行时,RDS 断开旧主连接、备库升主、端点解析切到新主,数据库侧动作是几十秒量级。业务多停的几分钟,第一嫌疑是应用连接池里的旧连接加上没设 socket 超时:旧主如果是网络分区而不是干净下线,池子里的连接等不来 RST,要等下一次被业务借到、再等到超时,才发现它已经死了。没设超时的池子,一次失败能挂到 TCP 重传超时,那就是分钟级。

治理还是那三件,在切换场景下再说一遍:maxLifetime 设 60 到 180 秒,切换后最迟一个周期全池换新;借出存活校验把失败压缩到秒级并提前到借连接阶段;空闲连接周期检测,让死连接在被业务借到之前就被清掉。走代理端点时这一环大幅缓解:应用到代理的长连接在后端切换时不断,代理在自己一侧摘旧主、建新主,应用池基本无感,只有切换瞬间在途的事务会失败,整事务重试即可。

第二嫌疑是个特例:JVM 的 DNS 缓存。端点自身的 TTL 是秒级到一分钟,操作系统一般也遵守,连接池缓存的是连接不是 DNS——真正难控的是 networkaddress.cache.ttl 被安全策略或基础镜像带成 -1,进程生命周期内永不刷新,只能重启。很多「切换后必须重启才恢复」的事故,查到最后都是这一条。动作只有一个:显式设成 5 到 10,并确认运行时生效值没被安全属性覆盖。

丢写也要提前想好判定办法:异步复制下,旧主宕机时已提交但未同步的尾巴会丢,这是真实存在的。需要怀疑的只有切换窗口内在途的那批写,切换前早就 ACK 且当时复制延迟接近零的不用管。窗口内的写都带全局唯一业务键,恢复后逐个回新主回查:在,就是落地了;不在,用同一业务键幂等重放。金额类再加一本只追加的写流水兜底对账即可,给全部流量上全局写序号是过度工程。

演练只测客户端栈,也能测出大半问题

主备切换的恢复时间,测试环境测不出数据形态,但连接池和 DNS 行为住在客户端栈里:测试环境用同一套应用镜像、同一份 JVM 参数和池配置,对一台测试 RDS 手动触发一次切换,就能测出真实的连接淘汰和 DNS 刷新行为。生产侧跑一个只读探针,同池配置连生产端点,低峰窗口手动切换一次,探针全程只读;要量首个成功写恢复时间,让探针写一张隔离的演练表。

量化只盯三个数:首个成功写恢复时间、错误率回落到基线的时间、池内旧连接被清完的时间。没治理之前,恢复时间约等于 maxLifetime、DNS TTL、TCP 超时三者里的最大值;治理之后,目标是压到切换本身的时长加几十秒。最后再补一刀分区演练:用安全组把测试主库的网络打断,而不是走正常切换流程——真实故障多半不是干净切换,围栏和事后对账能不能兜住,只能这样测出来。

https://www.huaweicloud.com/product/functiongraph.html
https://www.huaweicloud.com/product/rds.html
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。