ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

金融征信服务同城双活落地实践:从99.95%可用性目标到架构改造

金融征信服务同城双活落地实践:从99.95%可用性目标到架构改造 凌晨两点半我盯着监控大屏上那个刚刷新的数字99.95%。这是ADVANCE.AI印尼征信服务CBI同城双活项目上线之后当季度的可用性统计。说实话看到这个数字的瞬间我脑子里不是兴奋而是一堆终于落地了的复杂情绪。这类容灾项目我在金融科技公司不是第一次干但这次在雅加达推同城双活从方案选型到上线切换中间踩过的坑至少能写满一页纸。这篇文章不打算写那种发布稿式的官话就聊聊这套同城双活到底是怎么落地的为什么征信服务非要做到双活、99.95%这个目标是怎么定出来的、双活方案之间怎么权衡、以及设计文档里永远不会写清楚的那些实际教训。如果你也在做金融类系统的容灾改造或者正在纠结要不要上双活这篇应该能给你一些参考。1. 征信服务上同城双活不是技术炫技是被业务逼出来的1.1 征信调用是审批链路上的关键路径先说清楚CBI这套服务在印尼的业务位置。CBI是印尼本地的征信机构ADVANCE.AI通过合作方式对外提供征信数据服务。银行、持牌放贷机构、fintech放贷平台在贷前审批的时候几乎都要调一次征信数据来判断申请人有没有过度负债、有没有违约历史。关键是这次调用通常发生在审批流程的实时路径上。用户在前端提交申请后端系统同步发起征信查询中间没有任何人可以等一等再查。征信服务只要不可用下面所有环节全部卡住。再加上印尼市场信贷需求高、放贷平台多很多客户一天要跑几十万甚至上百万次查询单次查询的可用性直接决定金融机构一整天的业务能不能正常走。所以谈高可用不能只盯着技术指标。征信服务本质上处在信贷链路的最核心位置它挂了不只是接口报错这么简单而是会让客户的审批吞吐量直接归零。对金融机构来说这意味着坏账风险敞口失控、人工介入成本飙升、客户投诉短时间内涌入。想清楚了这一层就知道同城双活这件事不是一个可选项而是业务对架构提出来的硬要求。1.2 单机房的三个绷不住时刻在双活之前这套服务跑在单机房。单机房架构能做到很高的应用层稳定性但物理层的风险是绕不开的。我总结下来有三个绷不住的时刻。第一个是计划内维护。只要是跑在单机房的系统数据库升级、操作系统补丁、网络割接、机房配电检修这些动作都得停服或者降级。每次维护前都要拉长战线做窗口申请、做公告但金融机构的审批链路不会等你。哪怕一个月只维护一次每次半小时全年累积下来的业务影响也不容忽视。第二个是非计划故障。单机房环境下硬件故障、线路中断、空调失效、交换机宕机任何一个环节出问题都可能演变成全站不可用。最麻烦的是恢复时间不确定查告警、定位根因、重新拉起服务、追日志校验数据一套动作下来一小时起步。第三个是区域性风险。雅加达这边并非没有地质活动和电力波动机房选址和供电冗余做得再好也无法把区域性风险归零。单机房本质上把所有鸡蛋放在了一个篮子里而且这个篮子还没人帮你分摊重量。单机房不是不能做到高可用而是要做到99.95%这个量级几乎不可能。因为99.95%不只是给意外故障留预算还要吃掉所有计划内变更消耗的时间。所以双活上线前我们内部讨论的第一件事不是用什么技术而是先承认一个事实单机房的极限已经到顶了必须在架构层面换一条路。2. 99.95%可用性这个数字到底意味着什么2.1 先算清楚一年能坏多久99.95%这个数字乍一看和99.9%差不了多少但把它换算成故障时间预算差别立刻显现。一年的总分钟数是525600分钟。99.95%可用性意味着全年允许的不可用时间是525600乘以0.0005约262.8分钟也就是4.38小时。平均到每个月只有大约21.9分钟的故障预算。这4.38小时是全部预算不是只有突发故障才从中扣除。计划内维护、变更操作、升级回滚、误操作只要导致了业务不可用全部要从这里面扣。这样一来传统的半夜停机发布策略基本作废。哪怕一次维护只花30分钟一年十几次常规变更就把全年预算吃光了更不用提真正的硬件故障。所以当目标定在99.95%之后整个团队的思维方式必须变。以前可以靠尽量少出故障来保证可用性现在必须靠故障发生时业务无感知来保证可用性。前者是碰运气后者是要架构冗余。另外提一句行业里常见的口径陷阱。有些系统对外号称99.99%但它的统计口径里把计划内维护、只影响部分用户的故障都剔除了。最后拿出来的是过滤后的可用性参考意义不大。我们内部定可用性的口径是只要是用户实际感知到的服务不可用不管原因是什么都计入故障时间。只有这种口径算出来的99.95%才是有含金量的。2.2 为什么征信系统的可用性要按基础设施定标对比一下互联网业务和金融征信业务你会发现两者的可用性目标天然不同。一般互联网应用做到99.9%已经相当不错全年允许故障8.76小时。电商平台挂一小时损失的是一部分GMV用户骂两句恢复后还能补回来内容平台挂一小时损失的是广告收入和用户时长但数据本身不会坏。金融征信完全不一样征信查询服务是整个信贷审批链路的前置环节挂掉一小时意味着大量放贷决策被阻塞、大量人工介入、大量用户的借款计划被打乱。更要命的是征信数据的敏感性。征信记录不是流水账每一笔逾期、每一笔还款、每一次查询记录都是需要长期保存的。如果一次故障导致数据丢失或者数据错乱就不是补一个接口能解决的后续的纠错成本会高到难以估量。所以对征信系统来说RPO恢复点目标和RTO恢复时间目标同样重要。可用性99.95%这个目标本质上要求的是这样一套组合RPO趋近于零RTO控制在分钟级整体故障时间全年不超过4.38小时。把征信服务类比成城市供电系统不算夸张。电表跳闸大家都会修但电网不能因为修一下就让整座城市断电半小时。征信系统在信贷体系里的角色就是这种基础设施级别的存在。所以99.95%不是拍脑袋定的而是业务连续性需求倒逼出来的底线。3. 主备、异地、同城三种容灾方案下的一次选型复盘3.1 主备架构的窘境设备没坏业务却断了双活听起来高大上但我们做选型的时候其实先把主备架构认真评估了一遍。传统主备模式是一个主中心承载所有读写流量另一个中心只做数据备份或者最低限度的健康检查。主中心故障时人工或者自动脚本把流量切到备中心。逻辑上挺完备实际操作中问题不少。备中心的数据库需要从主中心同步数据常见的是异步复制。一旦主中心突然宕机异步链路里尚未同步的数据就丢了这就造成RPO大于零。在征信场景里任何一笔查询记录、任何一笔还款更新都不能丢数据丢失就等于信用记录被篡改这个后果没法接受。还有切换时间的问题。主备切换不是点一个按钮就能完成的。数据库要拉起、日志要追平、数据要校验、负载均衡要换指向、DNS要更新这些动作即便自动化程度很高也常常需要几十分钟。更别说如果切换脚本本身有bug或者备库数据状态异常整个恢复过程会无限拉长。主备架构的本质是保证数据能恢复到某个时间点而不是保证业务不中断。对征信这种关键链路来说中断几分钟就是事故主备模式在起跑线上就输了。3.2 异地双活光速和同步复制摆不平既然主备不行那就直接双活。但双活也有两种异地双活和同城双活。异地双活刚被提出来的时候我们内部有过一轮激烈讨论。异地双活的优势很明显两个数据中心离得远可以防止大范围区域性灾害。但它的代价也异常沉重。核心矛盾是光速。光在光纤中的传播速度大约是每毫秒200公里两个机房如果距离超过100公里一次数据往返就要消耗超过1毫秒。数据库同步复制模式下每一次事务提交都需要等待远端机房的确认这1毫秒直接加在关键路径上。查询类接口动辄要几十次数据库往返累积下来的额外延迟就会让用户体验急剧恶化。异步复制倒是能解决延迟问题可异步复制必然带来数据窗口。主库写入成功后数据还没同步到异地机房此时异地机房接管业务一部分已确认的写操作就凭空消失了。征信系统扛不住这个。所以异地双活在金融行业里不是不能做而是通常只作为两地三中心架构中的远端灾备不会承担全量读写切换。我们最后没有选异地双活核心原因就是在征信这个场景里RPO0比物理距离的灾难隔离更优先。3.3 同城双活恰好落在平衡点上同城双活是那个让所有人都能接受的折中方案。先说距离。同城双活一般要求两个机房距离在5到15公里左右。在这个距离下光纤传输延迟大约是每公里5微秒两个机房之间的网络往返延迟可以控制在1毫秒以内。数据库同步复制模式下每一次事务提交额外增加1毫秒左右的等待对征信查询这类低延迟、低并发场景来说影响完全可控。我们实测下来启用同步复制后核心查询接口的平均耗时增加了不到10%P995延迟的变化也在可接受范围内。再说故障域。两个机房虽然都在同一个城市但通过独立供电、独立网络链路、独立机房设施可以做到单机房故障不影响另一个机房。换句话说同城双活能防住绝大多数实际会发生的故障包括机房断电、网络中断、硬件损坏。它能防住的只有整个城市都出现问题这种极端情况而那种情况在雅加达这个场景下发生概率极低也超出了绝大多数业务连续性规划的边界。所以最后的方案定为同城双活两个机房同时承载业务流量任何一个机房故障另一个机房继续服务数据通过同步复制保持强一致RPO趋近于零RTO做到分钟级。这个平衡点找得不容易但回头看它是最符合征信业务特性的选择。4. 从入口到数据同城双活的四层改造清单4.1 入口层流量分配和健康检查别让负载均衡变成新的单点双活架构的第一步是让流量能进到两个机房。听起来简单做起来细节非常多。入口层通常由DNS、GSLB和机房间负载均衡组成。DNS把域名解析到两个机房的入口IPGSLB根据策略把用户流量按比例分发机房的负载均衡器再进一步把请求打到后端应用集群。这个链路里最容易忽略的是健康检查的准确性问题。常规健康检查是探测端口通不通、进程活不活但没考虑到进程活着但业务已经异常的情况。比如数据库连接池耗尽、依赖的下游服务超时这时候应用进程本身是存活的健康检查结果显示正常但流量继续灌进去的结果就是请求大面积超时。我们当时把健康检查升级为业务级探测每个机房维护一个独立的探活接口这个接口会调用最核心的查询链路探测真实RT和错误率。一旦错误率超过阈值或者RT超过预期负载均衡器自动摘除该机房的流量权重。这个细节在整个双活架构里看起来不起眼但实际故障发生时它是决定切换到另一个机房还是带着故障机房继续跑的分水岭。还有一个容易被低估的问题流量分配比例不能永远是50比50。如果两个机房容量不对称就要按权重分配。更重要的是当一侧机房故障另一侧机房要能承载全量流量。这意味着平时每个机房至少要留出50%以上的冗余容量确保切换后不会因为性能瓶颈又引发二次故障。容量评估不能只在架构图里算要用全链路压测实测出来。4.2 应用层把所有有状态的东西往外搬应用层的双活改造核心一句话让应用变成无状态。如果应用实例自己保存了Session、本地缓存、临时文件那么用户请求落在不同机房时这些状态就不一致。最简单的解决方式是让Session存储外置放到统一的分布式缓存或数据库中。用户在任何机房登录后续请求落到另一个机房依然能读到完整的会话上下文。更隐蔽的是幂等性问题。双活架构下当一个机房故障、客户端重试时同一个请求可能被提交到不同的机房。如果接口不设计成幂等的重复提交就可能造成数据重复或者状态错乱。征信场景里的查询接口天然只读幂等性压力不大但涉及数据更新、回写、通知这类操作时幂等键就必须设计好。我当时要求所有写接口接受一个全局唯一的请求号服务端按请求号去重这个改动解决了切换期间一大堆重复请求的隐患。应用层还有一个容易被忽略的点连接池配置。每个机房的应用实例要优先连接本机房的数据库和缓存同时配置跨机房备用地址。很多团队为了方便把所有应用连接都指向同一个机房的数据源一旦这个机房故障另一个机房的应用也连带瘫痪双活的意义直接归零。正确做法是本机房优先、跨机房备份并且连接池的连接数和超时时间要按故障切换后的流量估算不能按正常流量配置。4.3 数据层数据库双活的三种主流路线数据层是整个双活架构里最硬核的部分没有之一。主流路线大致有三种各有各的适用场景。第一种是共享存储加存储复制。两个机房的数据库实例共享一套存储阵列存储层负责数据同步。这个方案的好处是数据库层不需要感知复制细节坏处是存储阵列本身可能成为单点而且跨机房的存储复制对带宽要求极高。传统金融行业用得多但近年来软件定义存储和分布式存储兴起后这个方案已经不那么主流了。第二种是数据库原生同步复制。以MySQL为例开启半同步复制或者组复制主库写入后要等待备库确认才提交从而保证两个机房的数据强一致。这个方案的好处是数据库自身能力就能完成不需要额外引入中间件坏处是网络延迟直接进入写关键路径对网络质量非常敏感。我们在项目里大量使用了这种模式并且在网络抖动触发同步超时时设计了自动降级到异步的开关避免写链路被网络问题拖垮。第三种是基于分布式数据库的多副本多数派提交机制。TiDB、OceanBase这类NewSQL数据库天然支持多副本跨机房部署写入事务需要多数派副本确认在保证强一致的同时也能容忍少数副本故障。这个方案在一致性、可用性和运维复杂度之间取得了很好的平衡尤其适合数据量增长快、未来还需要扩容的场景。征信服务的数据模型复杂、一致性要求极高分布式数据库的多数派机制比传统主从复制更稳这是我们后来倾向采用的路线。不管选哪条路线有一个指标必须盯死同步复制下的RPO必须为零。数据库层面如果出现数据差上层再花哨的容灾设计都是白搭。4.4 网络层专线、心跳和故障隔离网络是双活的地基地基不稳上面全白干。两个机房之间至少需要一条高质量、低延迟的物理链路通常是裸光纤或者波分复用线路。这条链路的带宽不能只按日常业务流量算还要覆盖故障切换时的数据同步洪峰所以要留至少一倍余量。心跳机制的设计也很关键。两个机房需要互相探测对方是否存活才能决定要不要触发切换。但心跳检测不能只走一条物理链路否则链路断了心跳和业务数据一起断根本无法区分是对方机房故障还是链路故障。我们当时做了心跳双链路冗余并通过第三点仲裁来辅助判断避免因为灵机一动的心跳设计导致两个机房同时误判对方故障、触发争抢。这部分必须提前想清楚不然后面出了脑裂问题代价极大。网络层的坑往往不在规划设计里而在物理实践中。我曾经踩过一个特别典型的坑光纤配线架上一根尾纤松动导致链路丢包率升高到5%。从监控上看没有任何告警但数据库同步复制的重传机制被频繁触发整个写链路的延迟被拉高了近10倍。最后是通过逐段打流、逐段测试才定位到物理链路层。所以网络层双活改造完成后一定要做一次完整的物理链路验收包括线路损耗测试、割接演练、端口倒换测试而不是只测通就算完事。5. 数据一致性与脑裂双活最难啃的技术骨头5.1 脑裂两个机房都觉得自己才是主双活架构里最恐怖的事不是机房宕机而是两个机房同时认为自己是主、同时接受写入导致数据分裂。这就是脑裂。脑裂的典型场景是心跳链路断了但两个机房的应用都还在正常运行。机房A检测不到机房B的心跳以为机房B挂了把自己提升为主机房B那边也没收到机房A的心跳同样把自己提升为主。结果两边同时接收写请求同步复制链路却是断的数据各自为政。等心跳恢复两个机房的数据已经产生了分叉这时候想合并都无法合并只能回退到某个历史点丢失一部分数据。要防脑裂常见的机制有三个。一是仲裁节点引入一个位于两机房之外的第三个监控点由它来投票决定哪个机房存活二是Fencing在切换前先确保对端已经被隔离或者说断电绝不能让它继续写入三是租约机制数据库节点定期续租租约过期则自动降级为只读。这些机制不是选一个就完事而是要根据业务场景组合使用。我在这个项目里最大的体会是防脑裂机制宁可多一些冗余也不要在危机时刻赌运气。因为一旦脑裂发生数据一致性就彻底崩溃了后面所有补救手段都事半功倍。5.2 为什么征信数据坚持RPO0征信数据不是普通业务数据。一条逾期记录如果丢失用户可能莫名其妙被银行拒贷一条还款记录如果丢失用户可能背负不存在的负债。这类数据的正确性直接影响信贷决策甚至影响一个人一生的信用评价。所以征信服务的数据同步目标必须严格锁定在RPO0。这意味着主库上的每一笔提交都必须同步到另一个机房之后才能向客户端确认成功。业务上这个要求非常合理但技术上它意味着写请求的RT要额外加上一个跨机房往返的延迟。为了把影响降到最低我们做了几件事对写链路进行批量合并减少跨机房往返次数对同步复制的确认机制做超时优化避免网络抖动导致不必要的等待对读多写少的场景把大部分查询流量分流到本地副本只有写路径才走强一致链路。这样既保住了数据一致性又不至于让用户体验被拖垮。这里需要坦白讲RPO0和性能之间永远存在取舍。我们最终选择的是核心征信数据走强一致同步复制非核心的业务日志、统计类数据可以放宽到秒级延迟的异步同步。分层处理的好处是既守住了征信数据的底线又给整体系统留出了性能空间。5.3 切换时的事务处理和会话保持故障切换那一刻的细节往往决定了整个双活架构是被夸奖还是被骂。最常见的错误是切换时直接把流量切到另一个机房不做任何预处理。假如故障机房还剩下一些未完成的事务这些事务可能残留在数据库中也可能还处于中间状态切换后客户端重试会导致重复提交或数据不一致。所以我们设计的切换流程分三步走第一步切断故障机房的写流量将其降级为只读第二步等待数据追平确认两个机房的数据一致第三步将读写流量切换到健康机房再逐步恢复故障机房的容量。切换期间客户端需要能自动重试但重试必须带上幂等键并且超时时间要覆盖切换窗口。很多客户端的默认超时设置是2到3秒而一次完整的故障切换即使在自动化剧本下也需要几十秒。如果客户端在上线前不做超时参数调整切换瞬间那批请求会立刻触发超时客户端再继续重试又会加剧目标机房的压力。我们上线前专门给客户端的重试策略做了一轮配置适配确保在切换窗口内不会出现重试雪崩。说到底切换的目标不是零感知而是快速恢复加有限影响。用户可能在故障切换时有几秒钟的请求延迟或者偶发超时但只要重试能成功、数据不丢整体体验依然可以接受。把切换设计成少折腾而不是零代价是双活落地过程中最务实的觉悟。6. 落地复盘踩过的坑和切换演练的真实节奏6.1 双活机房选址越近越好是一个常见的错觉同城双活选址第一个跳进脑子里的想法是机房离得越近延迟越低同步复制越稳。实际做下来这个直觉只对了一半。机房距离太近比如小于1公里反而可能落入同一个市政供电回路或者同一个网络汇聚点。一旦这个汇聚点故障两个机房一起断电断网双活彻底失去意义。从故障域隔离角度看两个机房必须做到供电、网络、制冷等基础设施完全独立而不是物理距离近就行。距离太远也有问题。超过20公里后光纤延迟开始变得不可忽视同步复制下的写请求RT明显上升专线成本也跟着上涨。我们综合评估后把选址范围限定在了8到12公里之间。这个距离既能让两个机房拥有独立的故障域又能把跨机房网络延迟控制在1毫秒以内算是一个比较稳妥的区间。不过光看距离还不够。选址还要考察链路走向看两个机房之间的光纤是不是走的同一条物理路由。有些时候直线距离看起来不远但光纤绕行了一大圈实际延迟远超预期。选址阶段最好做一次物理打流测试实测出真实延迟和丢包率再决定要不要在这对机房上做双活。6.2 链路抖动比机房全挂更常见在真实运维里机房全挂是小概率事件更常见的症状是网络质量劣化——丢包率时高时低延迟忽大忽小。这种半死状态比完全故障更折磨人。故障现象是这样的数据库同步复制默认是强一致的主库提交事务要等备库确认。当跨机房链路出现抖动ACK不能及时返回主库的写事务就会一直卡在等待确认状态。同步复制的重传机制开始频繁触发短时间内堆积大量积压请求写链路的RT从几毫秒飙升到几百毫秒甚至秒级。监控系统会报警但你一眼看过去所有机器的CPU、内存、磁盘都是正常的只有跨机房链路那一条线是红的。踩过这个坑之后我们给同步复制链路加了一层熔断降级机制。当网络质量指标连续超过阈值时自动将同步复制切换为异步模式先保住主库写入的可用性同时触发告警要求运维人员判断是否将流量切换到另一机房。异步模式意味着RPO会变为大于零所以这个降级只能作为短期逃生通道绝不能长期运行。整个过程需要自动化和高告警敏感性配合人肉盯是不可靠的。这件事给我的启发是双活的日常大多数时候不是在跟机房全挂搏斗而是在跟链路抖动光模块衰耗交换机单端口故障这些琐碎问题纠缠。只有把这些日常问题处理得足够稳真正的大故障来临时你才有余力应对。6.3 从试运行到全量切换的推进节奏双活上线切流量最忌讳一步到位。我们当时的节奏分成了四个阶段每个阶段都有明确的验证目标。第一阶段是旁路验证。两个机房的架构全部搭好但生产流量仍然只走原机房新机房只做数据同步和基础探活。这一阶段主要验证数据同步的延迟、带宽占用、心跳链路的稳定性把架构层面的问题先暴露出来。第二阶段是部分读流量切换。把10%到20%的查询请求切到新机房持续观测RT、错误率、同步延迟等指标并且做一次全量数据对账确保两个机房的数据完全一致。这个阶段能暴露连接池、缓存、负载均衡等配置问题。第三阶段是读写流量同时切换。先切只读接口再切部分写接口每一步都小步快跑。写接口的切换要特别谨慎必须在切换前后做数据完整性校验并且准备好回滚预案。第四阶段才是全流量切换和自动故障转移开关的开启。这个阶段并不意味着结束反而意味着常态化演练的开始。我们规定每季度至少做一次不提前通知的突袭式切换演练验证从故障产生到业务恢复的完整链路。演练的结果每次都要复盘把切换流程中的每个环节都变成checklist确保下一年度不会再犯同样的错误。最后说一点个人体会。同城双活这类项目上线不是结束而是运维纪律的开始。99.95%这个数字不是写出来的是每次切换演练、每次链路抖动处理、每次变更评审一点点攒出来的。如果你也在做类似的容灾改造我唯一想强调的是把切换当成日常动作而不是应急动作多练几次真到故障那天你才不会慌。
返回列表