一场办公室断网带来的连锁反应
上周三下午,公司突然断网半小时。等网络恢复后,财务发现订单系统里有几笔重复扣款,客服那边也接到不少客户投诉说支付成功却没收到确认短信。排查下来,问题出在数据库——那段时间正好发生了网络分区,导致主从库数据不一致,写操作在不同节点上产生了冲突。
什么是网络分区?
简单说,就是网络断了,原本连在一起的服务器被割成几块孤岛。比如你家Wi-Fi断了,手机连不上路由器,设备之间没法通信。在数据库集群里,这种情况更严重。假设你用的是MySQL主从架构,主库在北京,从库在上海,中间网络因为运营商故障断了,两边就各自为政,谁也看不到对方。
数据可能就此分道扬镳
网络一断,主库还在接写请求,用户下单、付款、改地址都在记录。但从库收不到同步日志,只能靠自己猜。有的系统会启用“可读写从库”模式,允许从库也接写操作。这时候两个库都在写,等网络恢复再合并,就像两个人同时编辑同一份文档,最后谁的内容保留?冲突几乎不可避免。
常见的场景是库存超卖。比如商品只剩1件,北京用户下单锁库存,上海用户也同时下单。主库记了“已售”,但从库没收到通知,也记了“已售”。网络恢复后,系统发现卖出去两件,实际只有一件,客户等着收货,仓库却发不出。
CAP理论不是纸上谈兵
分布式系统绕不开CAP:一致性(Consistency)、可用性(Availability)、分区容忍性(Partition tolerance),三者只能选两个。网络分区发生时,系统必须做选择:要么停服务保一致(比如暂停下单),要么继续服务但接受数据可能错乱。
电商大促时通常选AP,宁可短暂数据不准也不能让交易停下;银行系统则倾向CP,宁愿交易失败也不能出现余额算错。这个取舍直接影响用户体验和业务风险。
代码里的应对策略
应用层得有点自我保护意识。比如写数据库时加上版本号或时间戳,避免覆盖别人刚更新的数据:
UPDATE orders SET status = 'paid', version = version + 1 WHERE id = 1001 AND version = 2;这样如果同一行被两个节点同时修改,只有一个能成功。失败的一方得重新读最新数据再试。虽然麻烦点,但比事后对账强。
监控和预案比技术更重要
很多团队花大力气搞高可用架构,却忘了设网络分区告警。ZooKeeper、etcd这些协调服务能帮判断“我是不是被孤立了”,一旦发现自己失联,就该主动降级——比如从库发现连不上主库,就别再接写请求,免得制造更多麻烦。
定期演练也很关键。模拟断网几分钟,看数据库怎么反应,日志有没有记录异常,恢复后数据能不能自动修复。真出事时,这些经验能少踩一半坑。
别指望技术能解决所有问题
再好的架构也挡不住光缆被挖断。重要业务得有兜底方案:比如订单系统在网络恢复后自动跑一遍对账脚本,把异常订单挑出来人工处理。用户端提示“系统正在同步,请稍后查看结果”,比直接显示错误更友好。
技术可以降低风险,但消除不了不确定性。认清这一点,反而能让系统设计更踏实。