Day31-16|10.6.5 大型网站系统架构演化实例(五):数据库读写分离

一、教材定位

大型网站在应用集群和缓存之后,数据库往往成为读请求和写请求共同争用的关键资源。读写分离通过将写请求发送到主库、将部分读请求发送到一个或多个副本,提升读吞吐并减轻主库压力,是网站架构演化中的常见阶段。

读写分离并不是简单地把查询连接换成另一个地址。数据库复制存在延迟、故障和数据路由问题,读请求可能读到旧数据;事务内写后读、跨表查询、分页排序和报表读取都需要明确一致性策略。考试答题应覆盖复制方式、延迟监控、读路由、强制主库、故障切换、回滚和业务场景,而不能只写“主库负责写,从库负责读”。

二、为什么需要读写分离

1. 读写负载不均

许多网站读请求远多于写请求,商品详情、内容、搜索和订单查询会持续消耗数据库资源。主库既要处理写入、锁和日志,又要服务大量查询,容易达到 CPU、连接、缓存和磁盘瓶颈。

2. 扩展读能力

增加只读副本可以分担查询流量,并根据业务区域和查询类型路由。读扩展通常比立即拆分数据库或引入复杂分片风险更低,但前提是业务能够接受副本延迟和有限一致性。

3. 隔离查询类型

在线交易查询、后台报表、数据分析和全文搜索的资源特征不同。可以将报表和分析路由到专用副本或数据仓库,避免影响主交易库。

三、复制和读写分离结构

典型结构是应用通过数据访问层或代理连接数据库集群,写请求发送到主库,读请求根据策略发送到副本。副本通过日志、流复制或其他机制同步主库变更。架构还需要故障检测、主备切换、连接刷新、路由更新和数据校验。

复制可以是同步、半同步或异步。同步复制一致性较强但写延迟和可用性成本较高;异步复制吞吐较好但存在数据延迟和丢失窗口;半同步在二者之间折中。选择要结合业务的可接受数据丢失、写延迟和故障恢复目标。

四、读写路由策略

1. 按操作类型路由

INSERT、UPDATE和DELETE发送主库,普通查询发送副本。实际中要识别存储过程、事务、隐式写操作和读后写,不能只按 SQL 关键字判断。

2. 按业务场景路由

商品浏览、内容和历史订单查询可以读副本;支付状态、刚刚提交的订单、库存可用量和权限变化后查询可能强制读主库。业务路由比简单的读写分类更准确。

3. 按会话和一致性路由

写入后的一段时间内,同一用户或请求链路强制读主库,待副本追平后再恢复读副本。可以使用主库位点、版本号或会话粘滞判断副本是否足够新。

4. 按区域路由

将用户路由到就近副本降低延迟,但跨区域复制可能有更大延迟。跨区故障时要有备用路由和一致性降级策略。

五、复制延迟与一致性问题

1. 写后读不一致

用户刚提交订单,随后查询却读到旧状态。这是最常见的读写分离问题。可在写入后的会话窗口读主库,或携带写入版本,只有副本达到该版本才允许读取。

2. 跨副本读不一致

连续两次查询被路由到不同副本,结果可能前后不一致。可使用会话粘滞、最低位点、同一请求固定副本或对关键查询读主库。

3. 延迟积累

副本 CPU、网络、磁盘或大事务会导致复制延迟持续增加。必须监控延迟、日志积压、复制状态、数据位点和副本健康,超过阈值时暂停接收读流量或扩容。

4. 数据丢失窗口

异步复制下主库故障可能有未复制事务。对订单、支付和账务要明确可接受范围,采用同步或半同步、日志保留、故障后对账和补偿,不能默认副本永远完整。

六、读写分离的事务处理

1. 事务内读写

同一事务中的查询必须看到自己的写入,通常应固定在主库或同一事务连接。把事务中的查询随机路由到副本会破坏隔离和业务逻辑。

2. 跨应用事务

多个应用分别拥有数据库后,跨库事务更加复杂。要减少跨库强事务,采用领域边界、事件、幂等、补偿和对账;对必须一致的核心操作,可以集中在同一事务边界或采用专门协调机制。

3. 报表和分析

报表查询可能扫描大量数据,不能简单路由到普通只读副本。应使用专用副本、物化视图、数据仓库或离线处理,并明确数据延迟和报表口径。

七、故障切换和恢复

1. 主库故障

检测主库不可用后,需要选主、提升副本、更新路由、刷新连接池并验证数据。切换期间要防止双主写入和脑裂,使用租约、仲裁和唯一领导者机制。

2. 副本故障

副本异常时应停止分配读请求,不能因为健康检查延迟而继续返回错误或旧数据。副本恢复后先追平日志、校验数据,再逐步恢复流量。

3. 回切和重建

主库恢复后不能立即切回,要确认数据分叉、写入、复制和业务状态。必要时重建副本,执行全量校验和增量追平。回切同样需要灰度和观测。

八、读写分离的安全与运维

主库和副本应采用不同权限:只读账号不能执行写操作,应用不能任意切换数据库地址。路由、切换和权限变化要审计。敏感数据复制到副本后仍需遵守访问、加密和区域合规要求。

运维指标包括主库写延迟、事务提交、连接数、慢查询、锁等待、副本延迟、日志积压、读流量分布、错误率、切换时间和数据差异。监控不仅要看数据库状态,还要关联业务成功率和用户读到的数据。

九、案例分析:订单系统实施读写分离

某电商平台订单查询占据数据库大部分请求,项目计划把所有SELECT路由到副本。上线后出现用户下单成功但订单列表暂时为空、支付状态反复变化、后台报表拖慢线上查询等问题。

原因是路由规则没有区分业务场景,也没有处理写后读和事务内查询。改造时先把商品、历史订单和非实时内容路由到副本;订单创建、支付回调、库存扣减后的关键查询固定主库;用户写入后携带会话版本,在副本未追平前自动读主库。报表迁移到专用只读副本或离线数据集市,避免与在线查询争用资源。

同时监控复制延迟和读流量,副本超过阈值自动摘除;主库故障时按预案选主、更新连接、冻结高风险写入并执行业务对账。测试覆盖主库切换、副本延迟、写后读、事务内查询、重复回调、报表高峰和网络分区。通过订单最终一致率、查询延迟、主库压力、复制延迟和切换恢复时间验证效果。

十、综合知识练习

题1

读写分离最常见的一致性问题是什么?

参考答案:写入主库后立即从副本读取可能得到旧数据,即写后读不一致;连续请求被不同副本处理也可能读到不同结果。可通过关键场景读主库、会话粘滞、位点等待和版本校验缓解。

题2

副本延迟升高时,为什么不能继续按原策略分配读流量?

参考答案:副本可能返回过旧数据,影响订单、支付和库存等业务,且延迟持续时会积累日志和资源压力。应监控、摘除异常副本、调整路由、限流或读主库,并分析恢复和容量原因。

题3

主库故障切换时最需要防范什么架构风险?

参考答案:脑裂和双主写入、未复制数据丢失、路由和连接未刷新、切换后业务状态不一致以及恢复回切造成数据分叉。需要仲裁、租约、数据校验、冻结或限制写入、对账和灰度回切。

十一、论文写作素材

论文中可以这样描述数据库读写分离:

“针对网站读请求占用主库资源的问题,我在评估一致性和业务场景后实施读写分离。写请求、事务内查询以及订单、支付、库存等关键读请求固定主库,商品、历史查询和报表分别路由到合适的只读副本或离线数据集市;通过会话版本、位点检查和短时主库粘滞处理写后读一致性。系统持续监控副本延迟、日志积压、连接、慢查询和业务对账,异常副本自动摘除。主库切换采用仲裁、租约和灰度回切,结合数据校验和补偿,确保扩展读能力不破坏核心业务正确性。”

十二、今日复盘

  1. 读写分离通过副本分担查询压力,但会引入复制延迟和一致性问题。
  2. 路由应按业务场景、事务、会话和副本状态决定,不能简单按 SQL 分类。
  3. 写后读、跨副本读、报表、事务和数据丢失窗口必须单独设计。
  4. 主库和副本都需要健康检查、故障摘除、切换、回切和数据校验。
  5. 读写分离要结合权限、审计、延迟监控和业务对账。
  6. 案例答题要写清路由策略、一致性处理、故障切换和验证指标。