黄东旭解析 TiDB 的核心优势
484
2024-02-24
针对 v5.3.0 版本的 TiCDC,优化 Sorter 算子内存参数和 Sink 同步并发可以极大改善同步的性能,测试验证实时同步的 QPS 从 5k 优化到 60k,提升 12 倍以上。
测试验证,在满负载追数据同步场景,TiCDC 的相关参数 per-table-memory-quota 设为 800 MB 和同步任务 worker-count 设为 1250 可以保证同步的性能和稳定性,优先建议这一组配置值。
同步到 TiDB 的稳定 QPS 约为 53k ,同步到 S3 的稳定 QPS 约为 10k。
这组优化参数在保证性能的前提下,在当前的业务背景,消耗相对较少的系统资源。其他场景,需要重新测试和验证才能找到最佳组合参数。
不断调大 per-table-memory-quota 参数,同步的速度也会跟着提升。
峰值最高可以到 66k 的 QPS,稳定均值为 60k 。但设置过大导致节点消耗的内存太大,节点 出现 OOM 的风险很高。注意结合业务的写特点和所在集群环境的资源情况,谨慎进行合理调整。
如果 TiCDC 同步流有延迟,出现重启(手动调参重启或自动重启)时,可能会有很高的瞬时 CPU 冲击。如果 TiCDC 是和集群其他节点混部,可能会对集群产生冲击影响。测试中发现在极端场景,延迟 23 小时后重启同步流,瞬时消耗 CPU达到了 6000%。
这是因为 TiCDC 重启后需要重新全部获取落后的数据,重新进行落后这段时间内的变更数据拉取、排序和同步,处理的数据量过大导致消耗过多的节点资源。
延迟越高,同步流重启时对节点资源消耗越多。对于 TiCDC 同步流的延迟告警,需要尽快处理,越快越好,尽量缩短延迟同步的时间。
线上集群经常有 cdc_resolvedts_high_delay 告警,甚至会出现 ErrGCTTLExceeded 异常而导致增量备份到 S3 的任务直接失败。这些异常信息的出现已经影响到正常的增量备份流程的执行,进而影响集群备份数据的可用性和安全性,该问题迫切需要得到优化和解决。
为了更准确定位问题的根因,我们有必要先对 TiCDC 的底层同步原理进行梳理,然后再进行根因排查和性能优化。
请参考文章:https://tidb.net/blog/d500bb57
回到具体问题,首先我们明确收到的异常内容主要是 cdc_resolvedts_high_delay 告警和 ErrGCTTLExceeded 异常,从这两个信息入手逐一进行分析。
最频繁出现的报警内容是 cdc_resolvedts_high_delay,这个告警是指 TiCDC 同步任务延迟超过了 5 分钟。
触发该告警时,也就说明了我们的同步任务出现了较大的延迟情况,且延迟超过了至少 5 分钟。这个告警可以通过 Grafana 的 changefeed checkpoint lag 监控项进行复核和确认,如下图,可以看到 checkpoint 落后的时间差越来越大,说明同步的延迟越来越高。
一个完整的同步任务是由上游 TiDB 集群、同步工具 TiCDC 、下游 TiDB 集群和组件之间的网络链路共同组成,我们很自然地从这几个涉及的环节节点分析,做出合理的推测,同步流出现延迟可能会有下面几个场景和原因:
组件之间的网络延迟过大,导致同步延迟。
如果上下游网络延迟较大,例如超过 100 ms 时,由于 MySQL 传输协议的原因 TiCDC 向下游执行 SQL 的延迟会急剧增加,导致系统的吞吐下降, TiCDC 数据同步效率过低,从而出现延迟问题。
TiCDC 出现异常,同步任务中断。
TiCDC 同步流因为某种原因而中断了,可能是遇到不兼容的DDL、踩到坑等。由于多个线上集群出现过该问题且无运维操作,所以这里我们排除人为的停止任务的情况。
同步任务正常,但是上游 TiDB 写入量过大或者下游 TiDB 写入速度过慢,导致延迟。
如果上游业务有了调整或者新上业务量,导致上游写数据量太大,超过了TiCDC的处理瓶颈,同步任务会出现延迟。
如果下游 TiDB 集群有写入的性能问题,TiCDC 所同步过来的数据无法以正常的速度写入集群,这样 TiCDC 的数据因为得不到及时的消费而出现阻塞,从而导致同步延迟不断升高,最终也会触发告警。
同步任务正常,上下游 TiDB 集群的业务写入量和集群性能也正常,但还是出现延迟。
这种情况可以排除上下游集群和业务的问题,可能是 TiCDC 工具本身存在性能问题,需要针对性地进行优化才能解决同步延迟问题。
通过上述分析,我们能够大概明确了一些排查问题的方向性内容。继续查看另一个告警内容 ErrGCTTLExceeded,这个告警在每天的固定时间出现,且一旦出现该告警,同步任务就会失败。导致这个报错的原因是,当同步任务停滞时间超过 GC TTL 设置的默认值 24 小时,延迟的数据会被集群 GC 掉而导致任务无法被恢复,任务就会直接失败。
从 TiDB v4.0.0-rc.1 版本起,PD 支持外部服务设置服务级别 GC safepoint。任何一个服务可以注册更新自己服务的 GC safepoint。PD 会保证任何晚于该 GC safepoint 的 KV 数据不会在 TiKV 中被 GC 清理掉。
在 TiCDC 中启用了这一功能,用来保证 TiCDC 在不可用、或同步任务中断情况下,可以在 TiKV 内保留 TiCDC 需要消费的数据不被 GC 清理掉。
TiCDC 的 gc-ttl默认值为 24 小时,所以 24 小时是TiCDC 中某个同步任务中断或者被手动停止时所能停滞的最长时间,若同步任务停滞时间超过该值,那么该同步任务就会进入failed 状态,无法被恢复,并且不会继续影响集群的 GC safepoint 的推进。
所以这个告警可以理解为是同步任务出现了延迟的最终结果,如果延迟超过 24 小时任务就会自动失败,目的是为了防止 TiCDC 中某个同步任务停滞时间过长,导致上游 TiKV 集群的 GC safepoint 长时间不推进,保留的旧数据版本过多,进而影响上游集群性能。
对于我们的问题场景,排查问题的方向应该还是聚焦于前面的分析内容。
我们的集群版本是 v5.4.0,TiCDC版本为v5.3.0 。
由于业务使用中有写 S3 的需求,v5.3.0 支持(未GA)但 v5.4.0 暂不支持( v6.5.0 正式GA),所以使用的 v5.3.0 ,测试和使用过程当中没有兼容性问题。
通过前面一小节的分析,我们可以确认到同步延迟的原因一定是在下面的某个或某几个环节当中:
组件网络问题
上游写入流量量过大
下游写入性能太差
TiCDC 本身的性能问题
针对上面的推断场景,分别进行排查和确认。
组件网络延迟排查
对线上有开启 TiCDC 集群同步的网络进行了完整的链路排查和测试,从上游集群到TiCDC,再到下游集群,整条链路的网络延迟大概在 10ms 到 20ms 之间。链路的延迟时间远远低于官方的建议值 100ms,说明我们的同步网络是正常的,问题不在此。
下游集群写性能排查
对于有延迟的下游 TiDB 集群,我们排查和确认这些集群的情况 ,大部分集群的 95% 访问延迟都在 10ms 以下,且能达到上万的 QPS,集群的 IO、内存、CPU、带宽等资源充足。说明下游集群的写入性能正常,且余量充足,不是同步延迟的问题瓶颈。
上游集群写流量排查
对开启了 TiCDC 同步的上游 TiDB 集群,我们分析其写入流量情况。我们排查了线上部署有 TiCDC 的集群,集群的线上写入流量 QPS 从几百到 30k 之间都有。经过排查发现除非集群的写流量很低(低于 1k),否则都会有同步延迟出现,对于线上生产集群而言写流量上千过万是非常常见的,这也说明了 TiCDC 的同步性能就是瓶颈所在,与上游集群的写流量基本无关。
TiCDC 同步性能排查
结合上游集群写流量排查的结果,我们进一步确认 TiCDC 的同步QPS,如下图所示,一个正常执行的同步任务从 TiKV 拉取变更数据 QPS 可以达到 30k 以上,而最终的 Sink QPS 只有 800 左右,说明 TiCDC 的同步性能就是瓶颈所在。
根据同步情况,我们进一步分析 Puller、Sorter、Mounter 和 Sink 这几个环节,其对应的 QPS 大概为 32k、0.8k、0.8k、0.8k,如下图所示,Puller 到 Sorter 的QPS 出现断崖式下跌,也说明性能卡点在内部的 Sorter 环节。
继续对 TiCDC 的配置文件、同步参数进行排查,除了 worker-num 外发现都是采用默认的同步配置,并没有使用额外的配置或同步参数。所以,既然性能过低,那么调大同步并发是我们最先考虑到的优化方案。
经过各个可能得问题场景排查,我们确认了是 TiCDC 的同步性能过低导致同步延迟的出现。结论大概有下面两点:
同步延迟的原因在于 TiCDC 同步工具本身,与组件之间的网络、上下游 TiDB 集群无关。
TiCDC 同步性能过低,可以考虑通过调整同步并发提升性能,重点关注 Sorter 算子的调优。
我们的同步下游是 TiDB 集群,所以评估 QPS 是以 Grafana TiCDC 面板的 Sink-write-rows-count/s 为参照的。为了阐述方便,如果无其他说明我们认为 Sink-write-rows-count/s 就是 TiCDC 同步到下游的 QPS。
我们在测试环境使用压测工具模拟线上生产流量,以 15k 左右的 Replace QPS 写入上游集群, TiCDC 进行同步写入到下游 TiDB 集群。通过观察发现,v5.3.0 的 TiCDC 工具会把上游的 Replace 转换为下游的 Delete + Replace 操作,会有写放大的现象。
当前版本下,经过观察上游的 DML 经过 TiCDC 工具后会有下面的转换:
1 delete -> 1 delete
1 insert -> 1 delete + 1 replace
1 update -> 1 delete + 1 replace
1 replace -> 1 delete + 1 replace
默认情况下,ticdc 的 mysql sink 使用 safe_mode ,即上面的方式,会影响性能,但是可以保证同步可重入。 mysql sink 配置可关闭 safe_mode(ticdc v6.1.3 默认情况下关闭 safeMode)。为了保证业务数据同步的幂等性,还是保留默认的 safe_mode 配置。
测试验证中我们在上游集群写入 15k 左右的 Replace QPS,它会被转化为下游 30k 的写入 QPS,TiCDC 达到这个性能才能保证数据实时同步,否则就会有延迟出现。为了保证足够的写入压力,先执行压测一段时间后,再使用压测开始时的 TSO 进行同步,这使得 TiCDC 一开始就进入了落后而追数据的状态,方便充分验证 TiCDC 的数据同步能力。
测试时集群配置为 CPU 112、512GB 内存,2.9TB NVME,万兆带宽,1 个 pd 节点和 1 个 TiCDC 节点混部在同一台机器上,共3个 TiCDC 节点。集群5台 tikv机器,3台 tidb-server 机器。
根据前面一个小节的同步 QPS 情况,第一个出现 QPS 骤降卡到的节点在 Sorter,在 v5.3.0 版本下我们排查官方文档并没有找到相关优化参数。但看到在部署 TiCDC 时有推荐使用 worker-num。 worker-num 是 mounter 处理时的应用线程数,用于解码 TiKV 输出的数据。
我们首先尝试调整 worker-num 进行调优,看看对优化有没有效果,具体的测试结果如下。
测试结论:
调大 worker-num 的并发对 TiCDC 的同步 QPS 没有提升作用。
反而对 worker-num 的值调整越大,其同步 QPS 越低,这个是有点意外的。仔细分析以下也是合理的,提高 work-num 就是增加处理时的应用线程数,上下文切换的代价也会越来越高,导致系统整体吞吐量下降。
为了获取良好的 mounter 效率,生产环境推荐设置 worker-num 为 8 或者 16。
我们在配置 TiCDC 的同步下游地址时,可以在 Sink URI 里指定同步到下游的数据库 MySQL 或者 TiDB 的并发,如:
同步并发 worker-count 默认是 16 和 单个事务包含的行数大小 max-txn-row 默认是 256。
我们线上的业务几乎都是对单行或者少数行的事务操作,经过几次测试验证后,也证明 max-txn-row 无论调大或调小,对同步 QPS 的提升都没有帮助。
所以,这里重点分析worker-count 同步并发的影响,其中worker-num=64。具体的测试结果如下:
测试结论:
调大 Sink 同步并发 worker-count 可以明显提升同步 QPS。
在其他条件相同的情况下,在 worker-count 设置为 512 时,QPS可以达到峰值 5.4k ,之后再继续调大 QPS 的值基本保持平稳,但有略微下降。
这说明在上游写流量充足的情况下,TiCDC 可以通过调整 worker-count 获得一定的同步 QPS 提升,但由于没有根本解决性能瓶颈的问题,所以最高也只能到 5.4k 左右。
在 worker-count 设置为 512 、800,甚至是 1024 这么高的同步并发,QPS 能够保持整体平稳。原因是下游 TiDB 集群对客户端的链接数承接能力足够大,支持超高并发的写入,能够处理来自客户端(TiCDC Sink 算子)的全部请求。
生产环境 worker-count 的配置建议值是 512。
根据上游集群的写流量情况和下游数据库的承接能力,这个值可以适当调大。
优化 worker-count 参数只是稍有提升,并不能根本解决性能瓶颈,说明还需要处理 Sorter 慢的问题。
我们排查 TiCDC v5.3.0的源码,排查和参考其 Test Case (ticdc/pkg/config/config_test.go)的配置内容,
{ "addr": "192.155.22.33:8887", "advertise-addr": "", "data-dir": "", "gc-ttl": 86400, "tz": "System", "capture-session-ttl": 10, "owner-flush-interval": 200000000, "processor-flush-interval": 100000000, "sorter": { "num-concurrent-worker": 4, "chunk-size-limit": 999, "max-memory-percentage": 30, "max-memory-consumption": 17179869184, "num-workerpool-goroutine": 16, "sort-dir": "/tmp/sorter" }, "per-table-memory-quota": 10485760, "kv-client": { "worker-concurrent": 8, "worker-pool-size": 0, "region-scan-limit": 40 }, "security": { "ca-path": "", "cert-path": "", "key-path": "", "cert-allowed-cn": null }, "log-file": "", "log-level": "info", "log": { "file": { "max-size": 300, "max-days": 0, "max-backups": 0 } } }看到有类似控制并发的配置参数:
sorter.num-concurrent-workers sorter.num-workerpool-goroutine但是调整上面的参数后,多组测试验证后发现没有提升的效果,调整后的同步速度和之前无异。正当我们还在探索新的解决思路时,排查看到新版本 v7.4 关于 TiCDC 的最佳实践内容,里面有一个关键的场景:
使用 TiCDC 同步单行较大 (> 1k) 的宽表时,推荐设置 TiCDC 参数per-table-memory-quota,使得 per-table-memory-quota = ticdcTotalMemory / (tableCount * 2)。ticdcTotalMemory 是一个 TiCDC 节点的内存,tableCount 是一个 TiCDC 节点同步的目标表的数量。
考虑到我们线上集群的写流量业务场景,特点是:
几乎全部的写流量集中在 A、B 和 C 这三个表 ,且 A 的写流量占据了 83% 以上。
A 表插入一行数据,有 25 列,数据量约为 2kB,很符合这个新版本的优化场景。
我们进一步确认上面源码里找到的 Test Case 配置参数,有 per-table-memory-quota 这个配置项,默认值是 10 MB。 per-table-memory-quota 是 TiCDC 在 Sorter 阶段用于给单个表处理的内存配额。
per-table-memory-quota 验证于是我们开始新一轮测试,我们先尝试性设置 per-table-memory-quota 为 100MB ,然后调整 Sink 同步并发 worker-count,观察 TiCDC 同步都下游的性能情况。
测试结果让我们大喜,具体如下:
测试结论:
调大 per-table-memory-quota 参数的值,对于提升 TiCDC 的同步性能效果非常明显!
在设置 per-table-memory-quota 为 100MB 时,work count 并发数在 1250 时达到同步 QPS 的峰值,过后随着并发越大同步 QPS 会有所下降,所以并不是 Sink 并发越大越好。
通过优化 per-table-memory-quota 参数,可以帮助根本解决了TiCDC 的同步性能瓶颈。
per-table-memory-quota 调优下游同步到 TiDB根据上一小节的测试结论,我们固定 work count 并发数为 1250 ,调整 per-table-memory-quota 参数的值,验证同步 QPS 的趋势情况。
我们测试了多个不同值情况下的同步性能表现,具体的测试结果如下:
测试结论:
优化 per-table-memory-quota 参数可以根本解决 TiCDC 的同步性能瓶颈。
在固定 work count 并发数为 1250 情况下,随着不断调大 per-table-memory-quota 参数,TiCDC 同步到下游的 QPS 也越来越高,最高可以到 60k 的同步 QPS。当配置超过 800MB 以后同步速度的提升减缓,即使内存调整再大,同步性能的改善已经不明显。
使用更大 per-table-memory-quota 参数虽然可以一定程度上提升同步性能,但是内存消耗急剧上升,大大增加 OOM 的风险。如果同步任务需要同步的表个数较多,会更容易触发 OOM 问题。
建议 per-table-memory-quota 参数设置为 800 MB,此时已经能达到 53k 左右的同步 QPS,已经可以覆盖大部分集群的写同步需求。除非,如果有特殊场景可以适当调大该值,但同时需谨慎评估 OOM 的风险。
下游同步到 S3在生产使用的时候,我们除了主从 TiDB集群同步外,还有一个场景是同时还有增量同步到 S3 的需求。所以在保持主从集群同步任务的情况下,再另外开启一个同步到 S3 的实时增量备份任务,调整优化 per-table-memory-quota 参数看看性能情况。
在 per-table-memory-quota = 800MB,sink TiDB worker-count=1250时同步到 S3 的 QPS 在 10k 左右,此时集群的监控大概如下:
测试结论:
调整 per-table-memory-quota 参数 可以提升同步写入到 S3 的性能,随着调大该参数,同步 QPS 不断升高,当到达 800MB后达到峰值 10k。之后再继续调大该值,同步 QPS 不再增长。
虽然优化 TiCDC 同步到 TiDB 和 到 s3 都是用 per-table-memory-quota 参数,但是效果不同,同步到 TiDB 最高可以到 60k,但是同步到 S3 的均值最高只能到 10k 左右。
同步到 S3 的过程,有一个类似批量写的操作,导致 Table Sink 这里出现一些锯齿状的同步QPS,出现高低相间的水位线,高位为 22k 左右,均值为 10k 左右。
从上面 Dataflow 的监控面板可以看到,TiCDC 同步到 S3 的性能瓶颈点也是在 Sorter,但是调大 per-table-memory-quota 参数到 800MB后同步 QPS 并没有提升。
这里推测是当前 v5.3.0 的 TiCDC 对同步到 S3 外部存储的性能支持有限,且该版本同步到 S3 的功能仅仅来实验特性并未正式 GA ,后面到更高的版本正式发布支持同步到 S3 后才会更好支持。
在当前 v5.3.0 的 TiCDC,如果要使用同步到 S3 的功能,建议 per-table-memory-quota 设置为 800MB,此时同步节点消耗的CPU大概在 800% 左右,内存约为 41GB。
这个值和前面同步到下游 TiDB 的相同。
通过调整 mounter 处理时的应用线程数 worker-num ,对于同步优化没有帮助。
对于下游是 TiDB 的同步场景,Sink 同步并发参数 worker-count 调大可以使得同步性能提升。但是并发数达到一定的数值后,同步 QPS 会出现拐点,导致同步性能下降,所以这个值并不是越大越好。
每个不同的业务场景和运行环境可能不同,如无其他需求建议 worker-count 设置为 512。
Sorter 的 num-concurrent-workers 和 num-workerpool-goroutine 这两个控制处理并发的参数,调整它们的值对于同步性能没有提升。
调整 Sorter 的 per-table-memory-quota 参数可以根本改善 TiCDC 的同步性能。
在固定 work count 为 1250 情况下,不断调大 per-table-memory-quota 参数,TiCDC 同步到下游的 QPS 最高可以到 60k 。当配置超过 800MB 以后同步速度的提升减缓,即使内存调整再大同步性能的改善已不明显。
在我们的业务场景下,建议 per-table-memory-quota 参数设为 800 MB,work count 设为 1250,此时同步到下游 TiDB 能达到 53k 左右的 QPS,远超过线上最大写流量。如果有特殊场景可以适当调大该值,但同时需谨慎评估 OOM 的风险。
使用更大 per-table-memory-quota 参数虽然可以一定程度上提升同步性能,但是内存消耗会急剧上升,大大增加 OOM 的风险,如果同步的表有多个会更容易触发。
关于调大 per-table-memory-quota 参数时消耗内容的情况,如下面的 3 个场景,节点被使用的内存越来越多:
1)在work count =1250 和 per-table-memory-quota = 800MB时 同步到 TiDB 的 QPS 达到53k ,同步正常进行时同步节点的内存消耗大约为 43 GB 。
2)在work count =1250 和 per-table-memory-quota = 5000MB时 同步到 TiDB 的 QPS 达到 59 k ,同步正常进行时同步节点的内存消耗大约为 49 GB 。
3)在work count =1250 和 per-table-memory-quota = 10000MB时 同步到 TiDB 的 QPS 达到 60 k 左右 ,同步正常进行时同步节点的内存消耗大约为 83 GB 。
通过调整 TiCDC 进程的内存配置参数 per-table-memory-quota 和 Sink 同步并发参数 worker-count 可以极大改善同步性能,能够根本解决当前的同步性能瓶颈问题。
对于下游是 TiDB 的同步场景,Sink 同步并发参数 worker-count 调大可以使得同步性能提升。但是并发数达到一定的数值后,同步 QPS 会出现拐点,导致同步性能下降,所以这个值并不是越大越好,通常可以设置为 512。
不同集群的最佳值需要根据不同业务写场景重新测试得到。
优化 per-table-memory-quota 参数,不管下游是同步到 TiDB 还是同步到 S3,有不同程度的性能提升。
该参数对于同步到 TiDB 的提升更大,最高可以到 66k 的 QPS,稳定均值为 60k 。但是此时消耗内存太大,节点 OOM 的风险很高。
同步到 S3 的 QPS 最高到 22k ,稳定均值为 10k,性能提升的幅度比下游到 TiDB 的少。
测试验证表明,在当前业务压测流量的背景下, per-table-memory-quota 设置为 800 MB 和 worker-count 设置为 1250 可以保证同步的性能和稳定性,在生产环境建议这一组配置值。
同步到 TiDB 的稳定 QPS 约为 53k ,同步到 S3 的约为 10k。
这组优化参数在保证性能的前提下,在当前的业务背景下,消耗相对较少的系统资源。其他场景,需要重新测试和验证才能找到最佳组合参数。
per-table-memory-quota 参数的调大会同步提升同步速度,但是也伴随有 OOM 的风险,注意结合业务的写特点和所在集群环境的资源情况进行合理调整。
在测试验证过程中发现,如果 TiCDC 同步流有延迟,出现重启(手动调参重启或自动重启)时,会有很高的瞬时 CPU 冲击,如下图在延迟超过 20 小时后重启同步流,瞬时消耗 CPU达到了 6000%。此时可能会带来一些额外问题,如果是和其他节点混部,可能会对集群产生冲击。
说明在极端场景,尤其是延迟接近 24h时,重启同步流会有很夸张的 CPU 瞬时冲击。这是因为 TiCDC 重启后需要重新全部获取落后的数据,重新进行落后这段时间内的变更数据拉取、排序和同步。
延迟越高,同步流重启时对节点资源消耗越多。所以,对于 TiCDC 同步流的延迟处理,建议尽快处理,越快越好,尽量缩短延迟同步的时间。
一句话,如果你的 TiCDC 同步性能一直无法提升,也暂时找不到好的处理方法,试试调大 per-table-memory-quota 参数吧,很可能会有意外的惊喜!
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。