动态资源调度优化:11年分布式事务专家的全链路复盘
|
去年元旦那会儿,产品突然说要搞个“动态资源调度优化”,说是要解决跨机房事务阻塞的问题。我在会议室的白板上画了半小时的时序图,键盘上那个掉键的“F”键硌得我手疼——这破键盘用了三年,每次写TCC模式的时候都得盯着拼写。产品经理端着咖啡站我背后看了十分钟,突然说:“要不把资源池拆成微服务?这样调度更快。”我差点把马克笔戳进白板,心想你懂个屁的分布式事务,微服务拆完,补偿链得绕地球三圈。 问题出在资源锁的粒度上。原来的方案是按机房锁资源,结果A机房的订单服务要锁库存,B机房的支付服务也要锁,俩锁一撞,事务直接挂。测试机是四台2018年的i7老机器,跑JMeter的时候风扇转得像直升机,性能面板上“事务等待”的曲线直接飙到90%。我盯着控制台那行“Lock timeout”的报错,突然想起三年前在另一个项目里,后端非要把Redis的锁改成Zookeeper,结果死锁了两天——历史总是重复,只是这次换了个姿势。 改的第一版是按服务维度拆锁。比如订单服务锁订单资源,支付服务锁支付资源,理论上能减少冲突。结果上线第一天,监控大屏上“事务回滚率”从5%涨到18%。查日志发现,支付服务锁完资源后,订单服务还在等,等超时了直接回滚,支付服务那边却已经提交了——典型的“脑裂”。我在工位上骂了句“这他妈也能行”,竖着的显示器上,测试用例的红叉叉得我眼睛疼。外卖到了没人去拿,凉了的炒饭在会议室桌上放了半小时,最后被保洁阿姨收走了。 第二版加了“锁超时自动降级”。如果A服务锁资源超时,就先把自己的事务标记为“可回滚”,让B服务先执行。听起来美好,实际跑起来更糟——支付服务降级后,订单服务回滚了,但支付服务那边因为网络延迟,没收到回滚通知,钱扣了,货没发。客服电话被打爆,产品经理在群里发了个“捂脸”的表情,说“要不咱们回滚吧”。我盯着构建日志里那行“Rollback successful”,突然觉得这活儿干得像在玩俄罗斯轮盘赌——你不知道哪颗子弹会要命。
文章配图,仅供参考 真正解决问题的是第三版:把资源锁和事务状态绑在一起。每个资源锁里存个“事务ID”,A服务锁资源时,先查事务状态表,如果状态是“进行中”,就等;如果是“已提交”或“已回滚”,就直接拿锁。后端改了三处代码:一处加事务状态查询,一处改锁的释放逻辑,一处加超时重试。测试机跑的时候,性能面板上“事务等待”从90%降到12%,首屏加载时间从“转圈5秒”变成“大概2秒”——虽然没具体测,但感觉快多了。控制台那行“Lock timeout”的报错,终于从每分钟10条变成每天1条。最气人的是,产品经理后来在复盘会上说:“你看,动态调度就是要敢试错,第一版虽然有问题,但给我们指明了方向。”我差点把笔记本摔桌子上——方向个屁,第一版的问题明明是因为你不懂锁的粒度,第二版是因为你不懂事务的隔离性,第三版才是我们熬了三个通宵改出来的。键盘上那个掉键的“F”键还在,我每次敲“finally”的时候都得低头看,生怕拼错。 现在回头看,动态资源调度的核心不是“动态”,是“怎么不动态的时候也能稳”。那些说“微服务解耦一切”的,要么没写过分布式事务,要么没遇到过跨机房锁冲突。下次再有人提“拆微服务”,我就把三年前那个Zookeeper死锁的日志甩他脸上——历史不会重复,但会押韵。 下一步打算把事务状态表改成分布式缓存,减少数据库压力。不过后端说缓存的一致性更难保证,得再测。键盘还是那个破键盘,外卖还是经常凉,但至少控制台的红叉叉少了——这大概就是程序员的小确幸吧。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


