1. 先把问题拆开:主从不一致到底卡在复制链路的哪一环
主从数据库数据不一致这个说法太笼统,落到生产环境里,其实至少混着三类完全不同的现象,处理手段也完全不一样。第一类是读到旧数据:写主库成功、业务马上跳转查询,结果从库还没应用这条变更,用户看到"刚提交的数据不见了"。第二类是位点长期追不上:从库延迟从 200ms 一路爬到几分钟,主库的二进制日志(binlog)堆积,磁盘水位告警。第三类是数据实质性分叉:某个时刻之后两边内容永久不一致,即便延迟归零也补不回来,这种往往是大事务、DDL、非确定性函数或人为误操作造成的。
我见过的绝大多数"不一致"投诉,其实属于第一类,本质是延迟抖动而不是数据损坏。用户体感上觉得"数据不一致",工程师一查延迟 30ms,可偏偏业务是先写后读、走同一份缓存键,于是这 30ms 就成了肉眼可见的 bug。所以要谈 RDMA 能不能治这个病,得先承认:RDMA 治的是"链路快不快、稳不稳",治不了"复制逻辑本身有坑"。把这两者混为一谈,最后一定是花钱升级了网络,问题原地不动。
1.1 复制链路上的延迟预算,得先算清楚再谈优化
一条事务从主库提交到从库可查,链路上至少串了六段开销,我把它们列出来,你对照自己的环境填数字,就知道瓶颈在哪:
| 环节 | 典型耗时(同机房) | 优化空间 |
|---|---|---|
| 主库事务提交与日志落盘 | 0.1 ~ 2ms(取决于刷盘策略) | 组提交合并、调整刷盘策略 |
| 日志从引擎层投递到发送线程 | 0.05 ~ 0.5ms | 减少锁竞争、批量投递 |
| 网络传输(发送 + 接收 + 协议栈) | 0.05 ~ 1ms | 这一段才是 RDMA 的战场 |
| 从库接收与应用队列排队 | 0.1 ~ 10ms(抖动最大) | 并行回放、拆分大事务 |
| 从库回放与提交 | 0.1 ~ 2ms | 关闭双一、并行度调整 |
| 业务侧感知(连接路由、读一致性判断) | 视实现而定 | 会话粘滞、位点等待 |
你会发现,网络这一段的绝对值在理想状态下并不大,但它的方差往往最大。TCP 路径上,一旦碰上拥塞、重传、软中断集中打满某个核,延迟能从 80 微秒直接飙到几十毫秒。而主从延迟的告警阈值通常是秒级,能被打爆的恰恰就是这种尾部抖动。这就是我认为 RDMA 在这类场景里有价值的核心理由:它主要不是把平均值从 80 微秒压到 15 微秒,而是把 P99 那条尾巴削平。
1.2 为什么不去直接改复制逻辑,而要动网络
有人会问,延迟大就加并行回放线程、拆大事务、升级存储,为什么要折腾网卡?这问题问得很对,我一开始也是这个态度。后来在一次真实压测里看到:把回放线程从 8 加到 32,延迟曲线前半段确实降了,但 P99 几乎没动,因为瓶颈已经转移到"主库把日志推出去"这一段——发送线程被 TCP 的窗口和确认机制拖着走,中间还夹着内核拷贝与上下文切换。
那批机器单实例的日志产生速率峰值在 180MB/s 左右,走 10Gbps 链路理论余量只有 1.25GB/s 的四分之一不到,一旦碰上批量导入或大事务,链路直接打满,后面的日志只能排队。这时候再回头看,网络这一段已经从"可忽略"变成了"主导项"。判断要不要上 RDMA 的标准不是"它先进",而是"网络在你的延迟预算里占比超过三成,且抖动主要来自协议栈"。达不到这个前提,上了也是白上。
2. RDMA 到底省掉了什么:把一次写操作的成本账摊开
RDMA 这个词经常被当成玄学,其实它解决的问题非常朴素:让一台机器的网卡直接读写另一台机器的内存,中间不经过对方操作系统的协议栈,也不经过本机的内核拷贝。名字里的"远程直接内存访问"就是字面意思。为了做到这一点,代价是必须提前把内存"交出去"——注册成网卡可以直接访问的区域,并且自己管理队列和完成事件。
对比一下一次普通的数据发送:应用调用写接口,数据从用户态缓冲区拷进内核的套接字缓冲区,协议栈加头、分段、计算校验和,驱动把数据交给网卡,网卡 DMA 读走再发出去。接收端反过来走一遍,网卡 DMA 写内存,触发硬中断,软中断处理,协议栈重组、校验、按序交付,唤醒等待的进程,最后再拷一次到用户缓冲区。整个过程至少两次内存拷贝、若干次上下文切换、一次中断。数据量越大、并发越高,CPU 在"搬运"上烧掉的时间就越多。
RDMA 的路径是:应用把要发的数据直接放进已经注册好的内存区,往发送队列里塞一个描述符,网卡自己去读、自己封包发出;对端网卡收到后,按描述符里带的地址和密钥,直接把数据写进对端应用事先注册好的内存区,然后在对端的完成队列里放一条完成记录。对端 CPU 全程可以不知情,只在自己主动去查完成队列的时候才知道"数据到了"。这就是所谓的"内核旁路"和"零拷贝",两个词说的是同一件事的两面。
2.1 三个必须搞懂的对象:内存区、队列对、完成队列
想把 RDMA 用起来,绕不开三个概念,我用寄快递打比方。**内存区(MR)**相当于你在收发室租了一个带编号的固定货架,并且把货架编号和一把钥匙交给了快递公司——只有登记过的货架,快递员才能直接放件取件,没登记的他一概不碰。这个"登记"动作叫内存注册,代价不小:它要把虚拟地址映射成物理页并锁住,防止被换出,同时把页表信息写进网卡。所以注册是重操作,绝不能每次发送都注册一次,必须提前建内存池复用。
**队列对(QP)**是一对收发队列的绑定关系,相当于你和某个固定快递员之间的专属通道。发送方往自己的发送队列里放任务,接收方在自己的接收队列里放好"我这儿有空货架"的凭证。通道建立之后是双向的,并且有连接状态机要走:初始态、可接收态、可发送态,每一步都要交换对方的标识信息和序号,跳步会直接失败。**完成队列(CQ)**则是回执箱,任务完成后网卡往里投一条记录,应用轮询它来确认"这单成了"或者"这单失败了,错误码是多少"。
注意:轮询完成队列是主动空转,占用一个核。高吞吐场景这是划算的,因为省掉了中断和唤醒;但如果你的复制通道是低频小包,空转就是纯浪费。我一般会给轮询线程加自适应退避,空闲超过一定次数就短暂让出,把 CPU 还回去。
2.2 延迟与吞吐的量化账,别信"快十倍"这种说法
实测数据我给一组自己环境里的参考值,同机房两台机器,同样的消息大小,跑双向小包延迟和单向大包带宽:
| 指标 | 传统套接字路径 | RDMA 路径(RoCEv2,25Gbps) | 说明 |
|---|---|---|---|
| 小包往返延迟(64B) | 60 ~ 90 微秒 | 12 ~ 20 微秒 | 差距主要来自协议栈与中断 |
| 延迟 P99 | 300 微秒 ~ 数毫秒 | 25 ~ 40 微秒 | 尾部差距比均值更明显 |
| 单流带宽 | 约 2.1 GB/s(CPU 受限) | 约 2.9 GB/s(链路受限) | 25Gbps 理论约 3.1GB/s |
| 发送 1GB 数据的 CPU 占用 | 接近一个物理核打满 | 低于 10% 单核 | 省下来的是实打实的机器成本 |
这组数字说明什么?单看平均值,收益是 4 到 5 倍;看尾部和 CPU 占用,收益是十倍级的。对于主从复制这种"平时风平浪静、一到大事务就雪崩"的负载,尾部指标才是决定体验的那个。我做过一个粗略测算:假设半同步复制每条事务多一次确认往返,业务峰值 8000 TPS,那每秒就是 8000 次往返。延迟从 80 微秒降到 18 微秒,单这一项每秒就省下约 0.5 秒的累计等待时间——换算成并发度,大约是 4000 个请求可以少等一个往返。这不是靠调参数能挤出来的。
3. 方案选型:把 RDMA 塞进复制链路的哪个位置
这步最容易翻车。很多人一上来就想"把数据库的复制协议整体改成 RDMA",工程量巨大且不可维护。我的经验是分三种落点,按侵入性从低到高排:
第一种,链路层替换。保持数据库原有复制协议不变,在网络层做加速,让两台机器之间的通信走 RDMA 通道。这种方案改动最小,但要处理一个硬约束:原有的复制实现是基于流式套接字的,你得在中间架一层适配,把流式语义映射到消息语义上。适配层的缓冲区管理、粘包拆包、背压,全得自己做。
第二种,日志通道自研。让主库把变更日志写到一块共享的日志区(或通过旁路采集),由独立的传输进程用 RDMA 单边写直接推到从库的预分配接收区,从库的应用线程再从接收区消费。这个方案绕开了数据库自身的复制发送线程,隔离性好,主库压力可控,代价是引入了外部组件,一致性边界要自己守。
第三种,只加速确认链路。复制数据仍走原路径,只把从库回给主库的确认消息走 RDMA。这招最轻,收益也最有限,但在半同步模式下确实能削掉一部分往返开销。适合"只差一点点"的场景。
3.1 RoCEv2 还是 InfiniBand,选型看运维能力而不是看参数
两种主流路线:一种是专用网络,从网卡到交换机整套换,性能和控制最干脆,代价是成本高、运维体系另起一套;另一种是在以太网上承载 RDMA,复用现有网络设备,部署门槛低。我所在的环境最终选了后者,理由很现实:机房里已经有成熟的以太网运维体系、监控和故障处理流程,全套换掉意味着要重新培养一整套技能。
但选了以太网承载,就必须接受一个前提:它默认不是无损的。以太网交换机在拥塞时会丢包,而 RDMA 的可靠连接模式对丢包极其敏感——传统协议栈丢一个包重传一下,上层几乎无感;RDMA 的连接一旦检测到丢包,可能直接进入错误态,整个连接要重建。所以必须在链路上做流控,让"拥塞"以暂停的方式表达,而不是以丢包的方式表达。这就是后面要讲的优先级流控和显式拥塞标记配置,这部分配不好,性能还不如原来的套接字路径,我踩过这个坑,第一次压测带宽只有预期的三分之一,延迟抖动比 TCP 还大。
3.2 哪些场景我明确不建议上
说点反向的。第一,日志速率长期低于 20MB/s 的实例。这点流量在千兆链路上都绰绰有余,套接字路径那点开销完全不构成瓶颈,上 RDMA 属于用大炮打蚊子,运维复杂度却是实打实增加。第二,跨机房、跨城的复制链路。广域网上往返时间以毫秒计,RDMA 省的那几十微秒被淹没得干干净净,而且长距离链路根本没法保证无损。第三,团队里没人能看懂网卡计数器。这类技术一旦出问题,排查深度直接下沉到硬件层,没人能接住的话,故障恢复时间会远高于收益。
4. 实操:从网卡到复制通道的完整搭建
下面是我实际走过一遍的流程,环境是两台同型号机器直连一台交换机,双口网卡做冗余,操作系统用主流开源内核,数据库是某开源关系型数据库引擎。整个过程分五步,前四步都是基础设施,第五步才是业务改造。
4.1 硬件与固件的准备检查清单
先别急着装驱动,硬件这一层没对齐,后面全是玄学问题。我的检查清单是这样的:
- 网卡型号两端一致,至少同一代。混用不同代际的网卡,特性支持矩阵对不上,会出现"一端能协商、另一端不认"的情况。
- 固件版本对齐,并且记录在案。固件升级会改变默认参数,比如重传次数、拥塞控制算法的默认开关,升级后必须重新验证基线。
- 交换机端口速率与网卡协商一致,
ethtool看一下协商结果,别出现一端 25G 一端 10G 的情况,那带宽直接砍半还不报错。 - 链路两端最大传输单元一致。这个是经典坑:一端 4200,一端 1500,小包测试全过,大包一跑就卡死或者性能断崖。我吃过一次,排查了两个小时才发现是两端配置不一致。
- PCIe 带宽核对。25Gbps 网卡插在 PCIe 3.0 x4 上理论是够的,但如果同一张卡上跑多个端口,或者和高速存储卡共享通道,就可能成为瓶颈。用带宽测试工具先跑一遍本地回环,确认卡本身没问题。
准备阶段我建议先用两根线直连两台机器做基线测试,排除交换机的干扰。直连能跑出预期的延迟和带宽,再去接交换机,一旦性能掉下来,问题范围立刻锁定在网络设备侧。这个"先直连后上交换机"的顺序,帮我省过好几次扯皮。
4.2 无损网络配置:参数不是抄来的,是算出来的
以太网承载 RDMA 的核心是让流量在拥塞时"暂停"而不是"丢弃"。做法是给 RDMA 流量打上独立的优先级标签,在链路上为这个优先级启用基于优先级的流控,同时开启显式拥塞标记,让发送端在交换机队列变长时主动降速。
第一步,确定优先级。别用默认优先级 0,那是给普通业务流量用的,你一旦给它开流控,等于把整个机房的普通流量都纳入暂停范围,风险极大。我一般选 3 或 5,先在两端网卡和所有中间交换机上把这一优先级标记为"无损队列",配置顺序是先配交换机,再配主机。反过来的话,主机会在交换机还没准备好的时候开始发流控帧,直接触发异常。
第二步,算缓冲区余量。这是最容易被忽略的一步。流控的原理是接收端在缓冲区快满时发暂停帧,发送端收到后停发。问题在于:从接收端发出暂停帧,到发送端真正停下来,这中间的飞行时间里,链路上还有数据在跑,接收端必须预留足够的缓冲接住这些"在途数据"。余量不足,就会出现"暂停帧还没生效,包已经丢了"。
余量的粗略算法是:链路速率 × 单向往返时间 × 端口数。举例,25Gbps 约等于 2.5 字节每纳秒,单向往返时间按 2 微秒算,一个端口就需要约 5KB 的余量。如果一台交换机有 48 个端口同时接入这种流量,最坏情况要考虑多个端口同时向一个出端口发送,余量按端口数放大到 240KB 量级。这个数字必须小于交换机为该队列分配的总缓冲,否则在突发时必然丢包。我在一台缓冲偏小的交换机上就遇到过:稳态压测好好的,一做突发流量测试就出现少量丢包和连接重建,最后靠调小突发窗口和增大队列缓冲才稳住。
第三步,打开显式拥塞标记。光有流控是"急刹车",只有拥塞标记才是"提前松油门"。让交换机在队列长度超过阈值时给数据包打标记,接收端把标记回传给发送端,发送端据此降速。阈值建议设成队列缓冲的 20% 到 30%,太低会频繁降速跑不满带宽,太高则等于没开。我一开始设成 10%,结果带宽利用率只有六成,调到 25% 之后曲线就漂亮了。
注意:流控配置错误最典型的症状是"暂停帧风暴"——一个端口的拥塞通过暂停帧扩散到整片网络,最终大面积停顿。判断方法是看交换机的流控帧计数是否在短时间内暴涨,同时业务侧出现无规律的卡顿。遇到这种情况,第一反应应该是缩小无损队列的范围,把不相关的流量从这个优先级里踢出去。
4.3 verbs 层连通性与性能基线验证
基础设施配完,先用底层工具验证,不要直接上数据库。步骤是:查设备、查端口状态、查链路层类型,然后跑带宽和延迟。命令行工具在 verbs 开发包里自带,常用的有查设备的、跑带宽的、跑延迟的。
# 查看设备与端口状态,关注 state 是否为 ACTIVE、物理层状态是否 LINK_UP ibv_devinfo -v # 确认两端 GID 索引与类型,以太网承载的流量选 IPv4 映射的那一条 show_gids # 双向带宽测试,服务端先起,客户端连过去 ib_write_bw -d <设备名> -i 1 -x <GID索引> -s 65536 -D 30 ib_write_bw -d <设备名> -i 1 -x <GID索引> -s 65536 -D 30 <对端IP> # 小包延迟测试,看平均与 P99 ib_send_lat -d <设备名> -i 1 -x <GID索引> -s 64 -n 10000这里有几个必查点:state必须是PORT_ACTIVE,如果卡在PORT_INIT或者PORT_DOWN,基本是链路或交换机侧的问题;GID 索引选错是最常见的连不通原因,索引 0 通常是链路本地的,跨机通信要用 IPv4 映射的那一条,具体编号在不同厂商的卡上不一样,用show_gids一看便知。
基线跑出来之后,我建议记录下来作为验收标准,包括:单流带宽、多流并发带宽、64 字节延迟、4KB 延迟、连续跑 30 分钟的延迟 P99 与最大值。后面任何一次固件升级、交换机配置变更,都拿这组数字比对。我吃过一次亏,某次交换机策略调整后带宽掉了 15%,因为没人对比基线,硬是带着这个问题跑了两周。
4.4 复制通道改造:队列规划、内存池与背压
到这一步才真正动业务。我采用的方案是前面说的第二种——日志通道自研。整体结构是:主库侧有一个采集进程,把变更日志按块读出来,写进一块注册好的发送内存区;用单边写把整块数据推送到从库侧预分配的接收区;从库侧有一个应用进程,轮询完成队列,把收到的块交给回放逻辑。
队列规划。我给每对主从建了 4 条队列对,按日志流分片,避免单队列成为串行瓶颈。为什么要多条?因为单条队列对内部是保序的,多分片能提高并行度,但分片之间不保证顺序,所以必须在数据块里带序号,由接收端重排,这一点不能省,否则回放会乱序,数据就真不一致了。
内存池。接收端预分配一个大环形缓冲区,比如 2GB,切成 256KB 的块,全部提前注册。发送端的内存区按同样的块大小对齐。块大小怎么定?太小则单位开销占比高,太大则小事务延迟增加。我的经验值是:日志速率 100MB/s 量级时,256KB 是甜点,既能让单次传输摊薄固定开销,又不至于让一个 1KB 的小事务等 256KB 的块攒满。如果是小事务密集型负载,块大小要往 32KB 或 64KB 降,并且配合超时刷出机制——块没攒满但超过 200 微秒也发出去。
背压。这是自研通道最容易漏掉的一环。发送方不能无脑往队列里塞任务,必须跟踪"已发送但接收端未确认消费"的量。我的做法是接收端定期回一个"已消费水位",发送端维护在途字节数,超过阈值就暂停发送。阈值取接收缓冲区的 60%,留出 40% 应对抖动。没有这一层,接收端应用一旦卡住(比如回放线程被大事务阻塞),发送端的任务队列会一直涨到耗尽内存,最后整个进程被系统杀掉,那才是灾难。
一致性保障。传输快了不等于一致了。我在数据块头里带了三个字段:日志起始位点、日志结束位点、块内事件数。接收端应用完成后,把已应用位点写到一个共享的、可被查询的地方。业务侧读从库前,先比对当前位点和主库提交位点,差值超过阈值就走主库或者等待。这个机制是最朴素但最可靠的兜底,RDMA 只是让差值变小、变小得更稳定,兜底逻辑一个都不能少。
4.5 验收:从"更快"走到"更一致"
改造完之后要验证两件事。第一是性能,压测对比改造前后的复制延迟分布,重点是 P99 和最大值,不是平均值。我那次的结果是:平均延迟从 22ms 降到 9ms,P99 从 480ms 降到 65ms,最大值从 3.2s 降到 210ms。P99 那一段的改善才是业务能感知到的部分。第二是正确性,跑一致性校验工具,逐表比对主从内容,以及在高压力下人为制造网络抖动、暂停接收端消费,验证背压和重排逻辑是否守得住。
我特意做了一次故障注入:把从库接收进程暂停 30 秒,观察主库侧行为。第一次的结果很难看——发送端的在途计数没有及时回落,接收缓冲被写满,触发连接错误,恢复后需要全量重建。后来加了"接收端心跳超时即熔断、暂停发送并保留位点"的逻辑,再测就平滑了:暂停 30 秒,恢复后从断点继续,延迟在 2 秒内回到正常水平。
5. 常见问题与排查实录
这一节是我踩过的坑,按症状归类,配排查顺序。
5.1 带宽上去了,延迟却没降甚至更高
最典型的一种情况:大包带宽测试漂亮得很,一跑小包延迟,比原来的套接字路径还差。原因通常有三个。一是轮询线程和业务线程抢核,建议把轮询线程绑到独占核上,并关闭该核上的其他中断。二是队列对数量太少,单队列串行处理,小包场景下每条任务的处理开销被放大,加队列能显著改善。三是完成队列深度不够,任务投递速度超过回收速度,出现"队列满、投递失败、重试"的循环,表现为延迟抖动。查法是看完成队列的溢出计数和投递失败次数,这两个指标一涨,说明深度要调大。
还有一种更隐蔽的:注册的内存区跨越了非本地内存节点,导致跨节点访问延迟翻倍。多路机器上跑的话,用绑核与内存绑定工具把进程和内存都约束在同一个节点上,能挤出一截延迟。
5.2 连接时通时不通,或者跑一段时间就断
按这个顺序查:先看两端 GID 索引是否一致,索引不对能连上但只能跑小流量,大流量下会出问题;再看最大传输单元是否一致,不一致时小包全通、大包必挂;然后看流控配置是否在交换机侧完整生效,优先级标记有没有在中间某台设备上被重置;最后看网卡计数器里的重传与丢弃计数,非零就说明链路在丢包,回到无损配置那一节重新核对。
我遇到过一次"每隔十几分钟断一次"的怪问题,最后定位到是中间一台交换机的流控队列缓冲不足,在周期性批量任务触发突发流量时溢出丢包。把批量任务的发送速率做了限流,并把该交换机上无关的流量从这个优先级移走,问题消失。这类问题的排查思路是:先看周期性,再看并发量,最后才怀疑硬件。
5.3 内存注册导致启动慢、偶发卡顿
内存注册要把内存锁定,这个过程在大内存池上可能耗时几百毫秒甚至更久。如果在请求路径上做注册,业务会感觉到明显卡顿。解决办法只有一条:启动时一次性预注册,运行期零注册。如果确实需要动态扩容,用后台线程做增量注册,注册完成后再切换使用,绝不能在处理请求的线程里同步注册。
另一个坑是内存池过大导致系统可用内存紧张,触发其他进程换页,反而拖慢整体。我的做法是接收缓冲不超过物理内存的 25%,并且明确设置进程的内存锁定上限,避免把整机拖垮。
5.4 排查速查表
| 症状 | 优先检查 | 常见根因 | 处理方向 |
|---|---|---|---|
| 连接建立失败 | GID 索引、端口状态 | 索引选了链路本地的那条 | 换成 IPv4 映射的索引 |
| 小包通、大包挂 | 两端最大传输单元 | 一端 4200 一端 1500 | 统一配置并重启链路 |
| 带宽只有预期三分之一 | 流控与拥塞标记阈值 | 阈值过低频繁降速 | 阈值调到队列缓冲的 20%~30% |
| 无规律大面积卡顿 | 流控帧计数 | 暂停帧风暴 | 缩小无损队列范围 |
| 延迟抖动大、重传计数增长 | 交换机队列缓冲 | 余量不足导致丢包 | 按链路速率与往返时间重算 |
| 运行一段时间连接断开 | 网卡与交换机计数器 | 突发流量溢出 | 发送端限流、增大缓冲 |
| 启动慢、偶发长时间卡顿 | 内存注册路径 | 请求路径上同步注册 | 改为启动预注册 |
| 回放乱序 | 数据块序号 | 多队列无重排 | 接收端按序号重排后再回放 |
6. 监控、灰度与回退,别把改造做成单程票
技术改造最怕的是上线之后发现不对,却退不回去。所以整套方案在设计阶段就要留好三条退路。第一条是链路可切换:复制通道同时保留原路径,通过配置开关切换,切换过程不重启进程,只切换发送端的目标。我要求的切换时间是不超过 30 秒,实测能做到 5 秒内。第二条是数据可对齐:任何时刻都保留完整的位点记录,出问题可以按位点增量补齐,不需要全量重建。第三条是容量可降级:在 RDMA 通道异常时自动降级到原路径,降级要有告警,不能静默降级——静默降级等于把问题藏起来,等你发现时已经积累了一堆延迟。
监控指标我盯这几个:复制延迟的实时分布(不只是平均值,要有 P99 和最大值)、链路两侧的在途字节数、背压触发次数、完成队列错误计数、网卡重传与丢弃计数、队列投递失败次数。其中背压触发次数这个指标特别有价值,它不增长说明通道余量充足,持续增长说明接收端消费能力不足,是延迟恶化的最早期信号。
灰度我按 1:10:50:100 的比例分四批,每批观察至少一个完整的业务高峰周期。第一批只切读流量不敏感的从库,观察延迟和错误率;第二批加上一个核心业务的从库;第三批覆盖大部分实例;最后一批处理那些有大事务、批量任务的特殊实例——这些往往是最难搞的,放在最后,前面几批的经验正好用得上。
我个人在实际操作中的体会是,这类底层改造真正的难点从来不在"能不能跑通",而在"出问题的时候能不能三分钟内定位到是哪一层"。所以我花在监控和可观测性上的时间,几乎和花在功能实现上的时间一样多。每次上线前我都会问自己一个问题:如果现在延迟突然翻十倍,我第一眼看哪个指标、第二个动作查什么、多久能判断是网络还是数据库侧的问题?答不上来就先别上线,回去把指标补齐。这套东西跑到今天,最让我踏实的不是延迟降了多少,而是每次告警响起来,我大概知道该去哪儿看。