十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

三地五中心与单元化实战:LDC、路由分片及容灾切换标准设计

三地五中心与单元化实战:LDC、路由分片及容灾切换标准设计 1. 三地五中心与LDC先把概念对齐别一上来就画架构图三地五中心、LDC、逻辑数据中心、单元化、容灾这几个词经常被混在一起讲很多人一上来就去画那张五边形的部署图结果讲了半天还是不知道请求到底走了哪条链路。我自己的经验是先把概念层级理清楚后面讨论单元拆分、容灾切换标准设计方案时才不会各说各话。简单讲LDCLogical Data Center逻辑数据中心是站在业务视角看的一套完整服务能力它可能由多个物理机房拼出来单元化是把这套能力按某个维度切成若干份每份都能独立对外服务容灾则是当某一份甚至某几份不可用时系统还能按预期继续跑。这三者是一条链上的东西不是三个并列的选项。很多人对LDC的理解停留在给机房改个名这其实偏了。物理机房关心的是机柜、电力、制冷、交换机LDC关心的是这套业务在这个逻辑单元里能不能自洽地完成一笔交易。一个LDC里可能包含计算、存储、缓存、消息、数据库等完整栈也可能只是其中一部分。区分物理和逻辑的意义在于扩容时你可以往一个LDC里加机器而不用动业务路由容灾时你可以整块切掉一个LDC而不用逐个服务去关。理解这一点后面所有的拆分和切换动作才有落脚点。1.1 LDC是什么为什么机房数量不等于逻辑数据中心我见过不少团队把我们有两个机房直接等同于我们有两个LDC结果真出事的时候发现两个机房里的服务互相强依赖一个挂了另一个也跟着挂。这就是典型的物理上分开了、逻辑上还缠在一起。判断是不是真正的LDC有一个很土但很有效的标准把这个LDC整体断网剩下的部分能不能独立完成核心交易如果不能那它顶多算个机房不算LDC。LDC的边界通常按业务域来划比如交易域、账务域、用户域各自成域也可能按用户群体来划比如一批用户固定落在某个单元。这里的关键是自洽两个字。一个LDC内部要能闭环处理它承接的那部分流量包括读、写、鉴权、风控这些环节。只有当闭环成立容灾切换才有意义——否则你切过去请求还是得回来找原机房的数据库等于没切。注意不要为了凑LDC数量而强行拆分。逻辑单元的划分要能对应到明确的流量归属规则上否则运维和开发都会被绕晕。LDC和单元化经常被放在一起讲其实两者是抽象和实例的关系。LDC是逻辑上的一套能力单元是这套能力的一个可独立运行的实例。一个LDC可以对应一个单元也可以对应多个单元取决于你打算把故障半径切到多小。理解这层关系后面讲三地五中心时就不会把中心和单元这两个词混着用了。1.2 三地五中心的三和五分别约束了什么先说三地它约束的是地理隔离级别。三个城市意味着任意两个城市之间不会同时受同一个区域性故障影响比如大范围电力问题、区域网络抖动。地理隔离不是越多越好每多一个地点数据同步的延迟和运维复杂度都会上一个台阶。三个地点通常已经能覆盖绝大多数场景再多边际收益就有限了成本却涨得很快。再说五中心它约束的是机房数量在一个城市内的分布。常见的是同城两个中心加异地若干中心这种组合。五这个数字本身没什么神秘关键看你怎么分配。有的方案是两城各两中心、第三城一中心有的是一城三中心、另两城各一中心。不同分配直接决定了同城双活、异地多活能做到什么程度也直接决定了切换时能保住多少容量。我用一个表格把常见的五中心功能定位摊开讲这样更直观中心编号所在城市角色定位承接流量容灾作用中心1城市A主机房、主单元全量写流量的一部分城市A故障时由中心3/4接管中心2城市A同城热备、只读部分写读流量、部分写与中心1同城互备延迟极低中心3城市B异地双活单元按用户分片的写流量承担城市A切换过来的流量中心4城市B异地只读应急写读流量、应急写城市B内部互备中心5城市C异地冷备/仲裁极少流量、仲裁节点提供第三地仲裁与数据兜底这张表不是标准答案只是想说明一件事五中心不是五个对等的机房而是有主次、有分工的。你把这层分工想清楚容灾切换标准设计方案才有地方落。1.3 单元化到底解决了什么问题把故障半径关进笼子单元化最核心的价值是把一个组件挂了导致全站不可用变成一个单元挂了只影响这个单元的用户。听起来简单做起来要动的东西很多路由要改、数据要分片、配置要隔离、调用要收敛。但方向很清楚——让每个单元尽量自洽减少跨单元依赖。我以前参与过一个系统的改造改造前一个缓存集群抖动就能让全站交易卡住改造后同样的问题只影响了八分之一的用户其余用户完全无感。这不是因为缓存变强了而是因为每个单元用自己的缓存故障被关在笼子里了。这就是单元化的直接收益也是为什么三地五中心这种架构值得投入的原因。单元化还带来一个隐性好处灰度发布和容量调度变得容易。想升级新版本先在一个单元灰度观察没问题再推其他单元想压测拿一个单元单独压。这些在单体架构里很难做到因为所有用户共享同一套资源。当然代价也明显运维复杂度上升数据一致性变难这些后面都会展开。2. 容量拆分与路由设计单元化的骨架怎么搭概念讲清楚之后就该动骨架了。单元化的骨架就两件事怎么拆、怎么找。拆是指把用户或数据分配到不同单元找是指请求来了怎么知道该去哪个单元。这两件事没定下来后面写再多同步逻辑都是空中楼阁。我在实际项目里踩过最大的坑就是拆的时候没想清楚路由维度结果数据分完了发现路由标识带不上来只能返工。所以这一章先把拆和找讲透。2.1 单元粒度拆得太细和太粗都会要命单元粒度没有一个万能答案但有几个约束必须考虑。第一是数据量一个单元的数据量要能在一台或一组机器上扛住且留出增长空间第二是流量单元要能承接它那份流量不能出现某个单元特别热的情况第三是运维成本单元越多配置、监控、发布的工作量成倍增长。太粗的问题是好几个业务挤在一个单元里故障半径大切换时一动就是一大片。太细的问题是跨单元调用变多本来一次本地调用变成网络调用延迟和故障点都增加。我一般的做法是先按用户ID做哈希分片分到一个能整除、便于扩容的数量级比如先分8个或16个单元再根据增长情况决定要不要细分。这里有个经验分片数最好取2的幂次扩容时可以用翻倍的方式重新映射减少数据迁移量。如果你用质数分片扩容时几乎要全量搬迁代价很大。这个细节在容量规划阶段就要定下来等上线再改会非常痛苦。2.2 五个中心的功能定位与流量配比上一章给了五中心的分工表这一节讲流量配比怎么定。核心原则是写流量尽量集中、读流量尽量分散。写集中是因为跨中心写的数据同步代价高能少跨就少跨读分散是因为读流量大、对延迟敏感放到本地能显著降低延迟。具体来说主单元承担主要的写流量同城热备承担全部读加少量写异地单元承担分给它的那部分用户的读写。这样设计的好处是同城切换几乎无感因为同城延迟低、数据同步快异地切换会有短暂的延迟上升但容量是够的。流量类型主中心同城热备异地双活异地冷备读流量承接本单元承接全量读承接本单元基本不承接写流量主写应急写分片写不承接切换后容量由热备补足独立承接独立承接仅做兜底流量配比不是拍脑袋定的要结合历史峰值算。比如你的读峰值是每秒多少万一个单元能扛多少剩下的要靠几个单元分摊这些都要算清楚。算不清就盲目上五中心切换时容量不够比不切换还糟。2.3 路由层一次请求怎么找到它该去的单元路由是单元化的神经中枢。一次请求进来系统要能快速判断它属于哪个单元然后把它送到对应的资源上。判断依据通常是路由标识也就是那个能把请求唯一映射到单元的字段最常见的是用户ID也可能是商户ID、设备ID。路由信息要在链路上一路传递从接入层到网关到业务服务到数据访问层每一层都要能读到。传递方式有两种一是放在请求头里二是放在上下文里。请求头适合跨进程传递上下文适合同进程内。实际项目里通常两个都用头信息跨服务传上下文在同服务内复用。提示路由标识一旦丢了请求就可能被送到错误的单元读到脏数据甚至写错地方。所以每一层都要有校验发现标识缺失就快速失败不要让它带着默认值往下走。路由层还要处理单元内找不到的情况。比如某个用户的数据还没同步到这个单元请求来了怎么办通常的做法是回源到该数据的主单元读一次或者等待同步完成。这块策略要提前定否则切换时会出现大量读失败。2.4 数据分片与跨单元收敛数据分片是单元化的另一半。用户分到哪个单元数据就要落在哪个单元的主库上。分片规则要单一、明确最好只有一套不要这个业务按用户ID分、那个业务按商户ID分最后映射关系乱成一团。跨单元调用要尽量收敛。理想状态下一个请求只在一个单元内完成不跨单元。现实中总有些全局数据比如配置、字典、序列号这些可以集中放但要走缓存和异步不要每次都同步调用。我见过一个系统每次下单都要同步调用一个全局的序列号服务结果序列号服务一抖全站下单都卡住。这就是典型的没收敛跨单元依赖。收敛的思路是能本地的本地能缓存的缓存能异步的异步。剩下的真跨单元的做成可降级的。这样即使跨单元那条链路出问题主流程还能跑。3. 落地实操从路由标识到数据同步的关键环节骨架搭好之后落地环节才是真正考验人的地方。这一章讲我在实际项目里怎么把路由标识带下去、怎么保证单元内闭环、多活数据怎么同步最后给一段可以直接抄的配置示例。每个环节我都会说清楚为什么这么做而不是只给结论。因为单元化这件事照抄别人方案很容易水土不服理解了原理才能改出适合自己的。3.1 接入层路由标识的传递接入层是路由的起点。用户请求进来最先能拿到用户身份的地方通常在网关。网关解析出用户ID算出它属于哪个单元然后把单元信息写进请求头往下传。这个动作要尽量早越早后续越省事。传递的时候要注意几点。第一头信息要有明确的命名规范比如统一用某个前缀避免和业务头冲突。第二要做防篡改单元信息不能由客户端随便传必须由网关生成。第三要在日志里带上单元信息排查问题时能快速定位请求去了哪个单元。# 请求头示例单元信息由网关注入 X-Unit-Id: unit-03 X-Route-Key: user-88213 X-Route-Source: gateway这三个头各有用处Unit-Id 告诉下游去哪个单元Route-Key 保留原始路由标识方便追溯Route-Source 标记是谁写的出问题时能定位是网关算错了还是被人改了。3.2 单元内闭环哪些调用必须收敛单元内闭环的核心是本单元的事本单元办。这句话说起来简单做起来要把调用关系一条条捋。我一般会画一张调用关系图把每个服务的下游列出来然后逐个判断这个下游是不是必须跨单元。必须收敛的典型有用户信息、账户余额、订单主数据。这些数据本身是分片的天然就该在单元内。可以跨单元的有全局配置、公共字典、风控规则这些数据不常变可以用缓存兜住。可以异步的有通知、统计、日志这些不阻塞主流程异步过去就行。判断标准其实就一条这个调用失败了主流程还能不能走完能就放宽不能就必须收敛或者做成可降级。按这个标准过一遍你会发现真正必须同步跨单元的没几个大部分都能优化掉。3.3 数据同步多活下的最终一致与冲突处理多活的数据同步是最难的部分。同城之间延迟低可以用强同步或者半同步异地之间延迟高通常只能异步。异步就意味着会有一段时间的延迟这段时间内不同单元的数据可能不一致这就是最终一致要处理的问题。处理方式我常用两种。一种是写的时候带上版本号或者时间戳读的时候发现版本不一致就走主单元的强一致读。另一种是按业务规则指定主单元比如某个用户的写永远走它所属的主单元读可以在本地读延迟容忍范围内不阻塞。两种各有适用场景前者适合对一致性要求高的后者适合对延迟敏感的。冲突处理要提前定规则不能等冲突了再想。常见的规则有最后写入者赢、主单元优先、按版本号高者优先。规则要简单且可预测复杂的冲突解决逻辑往往带来更多问题。注意数据同步的延迟指标要监控起来并设置告警阈值。延迟超过阈值就意味着一次读可能读到旧数据业务上要有兜底。3.4 一段可直接抄的路由配置示例下面这段是我在某次项目里用过的路由配置思路做了脱敏和简化核心逻辑可以直接参考。它是配置中心的规则文件网关和服务都读它来算路由。route: key: userId algorithm: hash_mod unit_count: 8 units: - id: unit-00 city: cityA role: primary - id: unit-01 city: cityA role: primary - id: unit-02 city: cityB role: replica - id: unit-03 city: cityB role: replica fallback: read: nearest_unit write: fail_fast switchover: enabled: true target_unit: unit-02 trigger: manual这份配置里key决定路由维度algorithm决定分片算法unit_count决定分几片units描述每个单元的位置和角色fallback定义降级策略switchover定义切换开关。用配置文件而不是硬编码好处是切换时只改配置服务不用重新发版。这一点在实战里非常重要因为容灾切换争分夺秒重新发版根本来不及。4. 容灾切换标准设计方案指标、流程与演练容灾切换标准设计方案这个说法本质上是把出事之后怎么办变成一套可执行、可验证、可回滚的流程。我在不同团队看过好几套方案好的方案都有共同点指标明确、流程清晰、演练常态化。差的方案也有共同点指标含糊、流程靠人临场发挥、演练基本没有。这一章就按指标、流程、演练三块讲最后给一张切换决策表方便你在自己项目里对照。4.1 RTO/RPO怎么定谁来定RTO是恢复时间目标RPO是恢复点目标也就是能容忍丢多少数据。这两个指标不是技术团队自己拍脑袋定的要业务方一起参与。因为RTO/RPO直接对应成本要求越高投入越大。比如RPO为零意味着任何一条已确认的数据都不能丢那同步机制就得做得很重延迟和成本都会上升。我一般的做法是分业务定级。核心交易类要求RTO短、RPO接近零查询类可以放宽RPO允许几分钟的延迟。分级定好之后不同级别对应不同的技术方案和演练频率。业务等级RTO目标RPO目标同步方式演练频率核心交易分钟级接近零半同步仲裁每季度重要业务十分钟级秒级异步补偿每半年一般业务小时级分钟级定时同步每年这张表是参考具体数值要结合你的业务和成本承受力来定。关键是定完之后要有人认账不能技术定完业务不认出事又怪技术。4.2 切换流程决策、执行、验证、回滚四步切换流程我总结为四步决策、执行、验证、回滚。每一步都要有人负责、有据可依。决策环节最难因为它往往发生在压力很大的时候。我的建议是把决策条件量化比如主单元不可用超过X分钟且无法在Y分钟内恢复才触发切换避免一有抖动就切切来切去反而更乱。决策还要有明确授权谁能拍板必须提前定好。执行环节要靠工具不要靠人手点。切换脚本、配置推送、流量调度这些都要预先准备好并测试过。我见过切换时现敲命令的手一抖输错参数故障范围反而扩大了。验证环节要能快速确认切过去之后服务是否正常。核对项包括核心接口成功率、数据一致性、关键业务指标。验证不通过就进入回滚。回滚环节和切换同样重要甚至更重要。因为切换可能失败回滚不及时会把故障时间拉长。回滚方案要和切换方案一起设计不能只设计切不设计回。4.3 演练不演练的容灾等于没有容灾这句话我在很多场合说过没有演练过的容灾方案只能算文档不能算能力。演练的价值在于暴露问题而问题往往藏在你想不到的地方。我参与过的一次演练切换流程本身没问题结果卡在了DNS缓存上客户端还在往老地址发请求。这种问题不演练根本发现不了。演练要分层次先单点演练比如切一个单元再全链路演练切整个中心最后做真实故障注入模拟断电、断网、依赖服务不可用。每次演练都要有记录、有复盘、有改进项并且改进项要闭环跟踪。演练还要注意控制影响面别在生产高峰期做。可以在低峰期做或者先在预发环境演练流程再到生产做小范围验证。4.4 切换决策表把决策条件做成表格值班时对着看能减少临场犹豫。下面这张表是我常用的结构。故障场景判断依据是否切换切换动作负责角色单机房网络抖动持续小于1分钟自愈否观察告警值班工程师单机房不可用超过阈值无法恢复是切至同城热备值班负责人同城两中心故障同城均不可用是切至异地单元应急指挥数据同步延迟过高延迟超阈值视情况降级为强一致读值班工程师切换后验证失败核心指标未恢复是执行回滚应急指挥表格不是万能的但能让决策有据可依减少这事到底该不该切的争论。争论的时间往往就是故障扩大的时间。5. 常见问题与排查技巧实录这一章讲实际问题。单元化和容灾的坑文档里通常不会写只有真正做过的人才知道。我把这几年遇到的典型问题整理出来配上排查思路和解决方法希望能帮你少走点弯路。问题分两类一类是改造期的问题一类是切换演练时暴露的问题。5.1 改造期最容易翻车的几个点改造期第一个坑是边改边跑没做双跑。直接从单单元切到多单元风险很高因为你想不到所有依赖。我的建议是双跑一段时间让新单元和旧逻辑并行对比结果确认一致后再切。双跑会多花资源但比出事后再回来强。第二个坑是配置没隔离。单元化了配置还共用一份改一个单元的配置影响全部等于白拆。配置要按单元隔离至少核心参数要隔离。第三个坑是日志和监控没带单元标识。出问题时根本不知道是哪个单元的问题排查时间翻倍。改造初期就要把单元标识打进日志和监控指标后面省很多事。5.2 切换演练里暴露的典型问题第一个典型问题是客户端的连接池还连着老单元。切换后老单元不可用大量请求卡在等待超时上。解决办法是让客户端支持动态刷新节点列表切换时主动通知或者等待健康检查摘除并设置合理的超时时间。第二个典型问题是分布式事务跨单元失败。原来在一个单元里的事务拆开后成了跨单元事务切换时一半成功一半失败。解决办法是尽量避免跨单元事务用最终一致加补偿替代补偿逻辑要能重入。第三个典型问题是缓存没同步失效。切换后数据在新单元但缓存里还是老数据出现数据错乱。解决办法是切换时主动清理相关缓存或者用版本号让旧缓存自然失效。5.3 问题速查表把上面这些问题整理成表方便快速查阅问题现象可能原因排查方向解决思路请求去了错误单元路由标识丢失或算错检查头信息传递链路补校验、快速失败切换后大量超时客户端未刷新节点看连接池状态动态刷新健康检查数据读写不一致同步延迟或缓存未失效比对主从版本强一致读缓存清理跨单元事务半成功事务未拆解查事务日志最终一致补偿换中心后容量不足流量预估偏低看实际QPS分布重新评估并扩容这张表可以直接贴在值班手册里出问题时对着查比临时讨论快得多。6. 一些实操心得最后分享几个我在实际做三地五中心和单元化过程中总结的个人体会不一定对所有人适用但都是我踩过坑之后才想明白的。第一单元化的收益是渐进的不要指望一次改造就达到理想状态。我参与的项目分了三个阶段先做路由和分片再做数据同步最后做容灾切换。每个阶段都能上线、能验证、能回退这样风险可控。想一口吃成胖子往往中途就崩了。第二切换的成败很大程度取决于平时的准备而不是切换当天的操作。平时把配置、脚本、告警、演练都做扎实切换时反而很平淡。我经历过一次很顺利的切换事后复盘发现顺利的原因就是之前演练了七次每个环节都熟。第三别追求零切换时间。物理上做不到追求这个目标会逼着团队做很多不切实际的优化。合理的做法是明确RTO在RTO内完成切换并保证数据不丢这已经是很好的结果了。剩下的时间应该花在减少切换的必要性上比如提升单单元的稳定性。第四文档要和代码一起维护。单元化和容灾涉及的东西太多人一走知识就断了。我在项目里坚持把路由规则、切换流程、演练记录都写进文档并且每次变更都更新。这件事看起来笨但关键时刻能救命。如果你正准备做单元化或者三地五中心我的建议是先小范围验证把路由和数据分片跑通再逐步扩展到容灾切换。这个过程会比较长但每一步都走得踏实最后得到的能力才靠得住。
返回列表