一、先搞懂啥是只读副本写延迟

很多做系统的朋友应该都遇到过这种情况:线上业务跑着跑着,突然发现读出来的数据比实际存的晚了好几秒,甚至十几秒,业务上要么用户看自己的订单更新半天不显示,要么后台统计数据和实际对不上。这种情况大多和只读副本的写延迟有关。

咱们先拿最常见的电商订单系统举例子,假设你有一个主数据库专门负责写订单(比如用户下单、修改地址、确认收货这些操作都往主库写),然后为了不让主库压力太大,又搭了好几个只读副本库,专门给前端页面展示订单、后台导出订单、统计销量这些场景用。这时候问题就来了:主库刚写完一个订单,只读副本库得等主库把这个更新的操作同步过来,才能读到新的订单数据,这个“主库写完到副本能读到的时间差”,就是咱们说的写延迟。如果这个延迟突然变得特别大,比如平时只有几十毫秒,现在突然涨到好几秒甚至几十秒,那就是延迟激增了。

二、为啥会出现延迟激增?核心在同步链路和网络

延迟激增不是凭空来的,大多是两个环节出了问题:一个是主库到副本的同步链路本身有问题,另一个是中间的网络突然抽风。咱们一个个拆。

2.1 同步链路的“天生短板”

同步链路其实就是主库把自己的更新操作,按顺序传给副本的通道,这个通道的设计逻辑直接影响延迟的下限。比如很多数据库的同步是“主库先把更新记到自己的日志里,然后再把日志发给副本,副本拿到日志后再执行一遍,最后标记自己已经同步到这个位置”。这个过程里,每一步都不能出错,只要某一步卡壳,延迟就会往上窜。

举个具体的场景:假设主库在1秒内收到了1000个订单的写请求,每个订单的更新都要记日志、传日志、副本执行日志。如果主库的日志生成速度太快,而副本的执行速度太慢(比如副本的服务器配置比主库差,CPU被其他任务占满了),那副本就会攒一堆没处理的日志,延迟自然就上去了。

2.2 网络抖动的“突发影响”

网络是同步链路的“血管”,血管一堵,血就送不过去。网络抖动不是说网络完全断了,而是网络的稳定性突然变差,比如丢包率从平时的0.1%涨到了10%,或者延迟从平时的1毫秒涨到了几十毫秒。这时候主库发给副本的日志包,要么传一半丢了要重传,要么传的特别慢,副本拿到日志的时间就会大幅增加,延迟也就跟着爆了。

比如某电商大促当天,主库和副本所在的机房之间的网络突然因为线路维修出现抖动,主库每秒传100M的日志,结果因为丢包,实际每秒只能传10M,剩下的90M都在排队,延迟一下子从几十毫秒涨到了10秒以上,导致前端页面的订单更新慢了十几秒,用户投诉瞬间涨了3倍。

三、OceanBase的同步链路和副本一致性保障

OceanBase是现在很多企业在用的分布式数据库,它的同步机制和传统的主从同步有点不一样,咱们得搞懂它的逻辑,才能知道怎么解决延迟激增的问题。

3.1 OceanBase的同步链路是啥样的?

OceanBase的同步链路核心是“提交日志(Commit Log,简称clog)”,整个同步过程可以分成三步: 第一步,主节点(就是负责写的那个节点)收到写请求后,先把这个请求的操作内容写到自己的clog里; 第二步,主节点把自己的clog同步给所有的副本节点(包括只读副本); 第三步,当主节点确认超过一半的副本都已经把这个clog存好后,才会给客户端返回“写成功”的响应。

这里有个关键点:OceanBase的只读副本也会参与clog的同步,也就是说,只要主库的写请求成功了,这个写请求的clog已经同步到只读副本的存储里了,只是只读副本还没来得及把这个clog里的操作执行完,所以暂时读不到新数据。

3.2 OceanBase的副本一致性保障机制

为了保证数据不丢、不混乱,OceanBase有两个核心的一致性保障机制: 第一个是“多数派确认”,刚才说的,主库的写请求必须等超过一半的副本都存好clog才会返回成功,这样就算少数节点出问题,数据也不会丢; 第二个是“有序执行”,副本拿到clog后,必须严格按照主库生成clog的顺序来执行,不能乱序,不然就会出现数据不一致的情况。

比如主库先写了“订单状态从待支付改成已支付”(clog序号100),然后又写了“订单状态从已支付改成已发货”(clog序号101),副本必须先执行100再执行101,如果反过来,就会出现订单状态从待支付直接改成已发货的错误。

四、延迟激增的排查和解决方法

遇到延迟激增,咱们不能瞎调,得按步骤来,先定位原因,再针对性解决。

4.1 第一步:先确认是不是真的延迟激增

很多时候大家会误判,比如以为是延迟激增,其实是自己的业务代码有问题。所以第一步要先拿到准确的延迟数据。

以OceanBase为例,咱们可以用它自带的命令行工具obclient来查只读副本的延迟,具体命令如下:

# 登录OceanBase的命令行工具,替换成自己的用户名、密码、地址、端口、集群名
obclient -u root@test#cluster1 -p your_password -h 127.0.0.1 -P 2883 -c

# 登录后执行下面的SQL,查看只读副本的同步延迟(单位是微秒,1秒=1000000微秒)
SELECT
  TENANT_ID,
  SVR_IP,
  SVR_PORT,
  REPLICA_TYPE,
  SYNC_STATUS,
  LAG / 1000000 AS LAG_SECOND  # 把微秒转成秒,方便看
FROM
  DBA_OB_REPLICA_STATUS;

如果查出来的LAG_SECOND突然比平时的基准值(比如平时是0.05秒,现在是5秒)大很多,那才是真的延迟激增。

4.2 第二步:排查同步链路的问题

如果确认是延迟激增,先看同步链路本身的问题,主要查两个点: 第一个是主库的clog生成速度是不是太快,比如主库的写请求突然暴涨,导致clog生成速度超过了副本的处理能力; 第二个是副本的处理速度是不是太慢,比如副本的CPU、内存、磁盘IO被占满了,导致没法及时执行clog。

咱们可以用下面的SQL查主库的写请求量和副本的处理速度:

-- 查主库1分钟内的写请求量(单位:次)
SELECT
  COUNT(*) AS WRITE_COUNT
FROM
  GV$OB_SQL_AUDIT
WHERE
  EXEC_TYPE = 'WRITE'  -- 只统计写操作
  AND TIME >= NOW() - INTERVAL 1 MINUTE;  -- 统计最近1分钟

-- 查副本的clog处理进度,看是不是攒了很多没处理的clog
SELECT
  SVR_IP,
  SVR_PORT,
  (MAX_CLG_ID - APPLIED_CLG_ID) AS PENDING_CLOG_COUNT  -- 没处理的clog数量
FROM
  DBA_OB_REPLICA_STATUS;

如果PENDING_CLOG_COUNT突然从平时的个位数涨到了几千甚至几万,那就是副本处理速度跟不上主库的生成速度。

解决办法也很直接:如果是主库写请求暴涨,那可以考虑给主库扩容,或者优化业务代码减少不必要的写操作;如果是副本配置太低,那可以给副本升级CPU、内存、磁盘,或者加更多的只读副本分担压力。

4.3 第三步:排查网络的问题

如果同步链路本身没问题,那就要查网络了。咱们可以用两个工具来测网络的稳定性: 第一个是ping命令,测网络的连通性和延迟; 第二个是tc命令,模拟网络抖动来定位问题。

先看ping命令的用法:

# 测主库到副本的网络延迟,持续测100次,看有没有丢包或者延迟突增
ping -c 100 192.168.1.10  # 把192.168.1.10换成副本的IP

如果ping的结果里有很多丢包,或者延迟从平时的1毫秒涨到了几十毫秒,那就是网络的问题。

如果ping暂时没测出来,咱们可以用tc命令模拟网络抖动,来验证是不是网络导致的延迟激增:

# 先查主库的网卡名,比如eth0
ip addr

# 给主库的网卡eth0添加网络抖动规则,模拟10%的丢包率
tc qdisc add dev eth0 root netem loss 10%

# 然后再查只读副本的延迟,如果延迟真的涨了,说明网络是诱因
# 测试完记得删掉规则,恢复正常网络
tc qdisc del dev eth0 root netem

如果模拟网络抖动后延迟激增,那就要联系运维调整网络,比如换线路、优化路由、升级带宽。

4.4 第四步:调整OceanBase的同步配置

如果同步链路和网络都没问题,那可以调整OceanBase的同步配置,来降低延迟。比如可以把只读副本的“执行优先级”调高,让副本优先处理同步的clog,而不是处理其他的读请求。

调整配置的命令如下:

-- 给只读副本的同步任务调高优先级,优先级范围是1-10,数字越大优先级越高
ALTER SYSTEM SET REPLICA_EXEC_PRIORITY = 10 TENANT = test;

不过这个配置要谨慎调整,调高优先级可能会影响副本的读请求处理速度,所以要根据业务的实际情况来权衡。

五、应用场景、优缺点和注意事项

5.1 应用场景

只读副本加同步链路的架构,主要用在这几个场景:

  1. 读写分离:把读请求分散到多个副本,减轻主库的压力,适合读多写少的业务,比如电商订单系统、内容平台的文章展示;
  2. 数据备份:副本可以作为主库的备份,万一主库出问题,可以快速切换到副本,适合对数据可用性要求高的业务;
  3. 离线分析:把复杂的统计、分析任务放到副本上,不影响主库的正常业务,适合需要定期做报表的业务。

5.2 技术优缺点

优点

  1. 提升系统可用性:主库出问题可以切换到副本,数据不会丢;
  2. 降低主库压力:读请求分散到多个副本,主库可以专注处理写请求;
  3. 成本可控:不需要为了主库的压力升级更高配置的服务器,加副本更划算。

缺点

  1. 存在写延迟:主库写完到副本能读到的时间差,对实时性要求高的业务不友好;
  2. 同步链路的复杂度:要维护主库和多个副本的同步,出问题后排查难度大;
  3. 网络依赖大:同步链路的稳定性完全依赖网络,网络出问题就会导致延迟激增。

5.3 注意事项

  1. 基准值监控:平时要记录正常情况下的延迟基准值,这样出问题时能快速判断是不是真的延迟激增;
  2. 配置匹配:副本的配置不能比主库差太多,不然副本的处理速度跟不上主库的生成速度;
  3. 网络冗余:主库和副本之间的网络要有冗余,比如同时用两条线路,一条出问题可以自动切换;
  4. 业务适配:对实时性要求高的业务(比如实时聊天、实时库存),尽量不要用只读副本,直接读主库,或者用OceanBase的强一致读功能。

六、文章总结

只读副本的写延迟激增,本质上是主库到副本的同步链路和网络两个环节出了问题。咱们要先搞懂同步链路的逻辑,再按步骤排查:先确认是不是真的延迟激增,再排查同步链路的处理速度,然后排查网络的稳定性,最后针对性解决问题。

OceanBase的同步机制和一致性保障,为咱们提供了排查和解决问题的基础,只要咱们掌握了它的核心逻辑,遇到延迟激增时就不会慌。同时,咱们也要根据自己的业务场景,合理使用只读副本,权衡好延迟和性能的关系,让系统更稳定。