在讨论混合负载(HTAP)架构时,业界常常容易陷入一种技术浪漫主义。认为只要引入了一套能在行存与列存之间自动复制数据的系统,OLTP 和 OLAP 的水火不容就能迎刃而解。TiDB 凭借 TiKV 和 TiFlash 双引擎架构,确实在物理存储层面提供了一个优雅的解法:通过 Raft Learner 机制将数据从行存节点异步复制到列存节点,既保证了数据的一致性,又避免了列存节点参与选举对主业务造成干扰。然而,当你真正在极高并发的生产环境中将这两套引擎推向极限时,你会发现物理层面的数据复制只是万里长征的第一步,真正的深水区在于计算层的代价评估模型、I/O 调度机制以及残酷的资源争抢。
一切的失控往往始于优化器的一次误判。TiDB 的基于代价优化器(CBO)在决定一条查询究竟是路由给 TiKV 还是 TiFlash 时,依赖于底层的统计信息。它会计算访问行存引擎的代价(包含网络传输成本、CPU 算力成本、单行读取成本)与访问列存引擎的代价,并取其低者。在理想状态下,点查或者带有极高选择率索引的查询会走向 TiKV,而大表的全表扫描或复杂聚合会交由 TiFlash 处理。但在真实的业务场域中,统计信息的更新往往滞后于数据的剧烈变动。一个看似带有强过滤条件的复杂分析 SQL,可能会因为直方图过时或者 Pseudo 估算失真,使得优化器错误地认为走 TiKV 的索引扫描代价更低。于是,灾难发生了。
当一个原本应该交由 TiFlash 的重型聚合查询被错误下推到 TiKV 时,它会瞬间生成数以万计的 Coprocessor 请求。TiKV 底层的 Unified Read Pool 被这些沉重的扫描任务彻底淹没,gRPC 线程池开始堆积请求。由于 TiKV 采用的是多线程协同工作的模型,这种突发的扫表 I/O 不仅会消耗大量的 CPU 周期,还会挤占底层 RocksDB 的 Block Cache。原本只需几毫秒即可完成的核心在线交易(OLTP)请求,因为无法在线程池中获得调度时间片,或者因为缓存被污染而被迫从磁盘读取数据,响应延迟瞬间飙升到秒级。这对于任何对延迟敏感的在线系统来说,都是毁灭性的打击。
要在这个修罗场中建立秩序,第一道防线往往是粗暴但有效的人工干预。通过在 SQL 中强行注入 /+ readfromstorage(tiflash[table_name]) / 这样的 Hint,可以绕过 CBO 的代价计算,强制查询引擎将物理算子下推到列存节点。在工程实践中,往往需要配合中间件或者定制化的数据库驱动,对特定来源的报表查询自动拼接 Hint。不过,这种依赖开发者纪律和硬编码的路由策略,随着业务规模的膨胀会变得越来越脆弱。应用层的开发人员很难在编写每一句 SQL 时都清晰地知道数据规模的量级,更难以应对动态变化的数据分布。
因此,真正的资源隔离必须下沉到底层的系统调度层面。TiDB 引入的 Resource Control(资源管控)机制,正是为了应对这种同源集群内的资源互相踩踏问题。它并非仅仅是在 TiDB Server 层做简单的并发限制,而是深入到了整个分布式计算与存储的骨髓。资源管控的核心在于引入了 Request Unit (RU) 这个抽象指标,它将 CPU 时间、网络读写字节数和磁盘 I/O 统一量化。当为不同的业务账号绑定不同的 Resource Group 时,系统实际上是在执行一套复杂的令牌桶(Token Bucket)算法。
剖析这一机制的源码逻辑,你会发现 TiDB 会在解析和执行 SQL 时,实时追踪该会话消耗的 RU,并将当前的资源配额上下文(Context)通过 gRPC 的 Metadata 传递给下游的 TiKV 节点。在 TiKV 端,调度器并不会对所有请求一视同仁。它内部实现了一个优先级队列,结合传入的 RU 剩余量和 Resource Group 优先级,动态决定是否要对某个耗时巨大的请求进行限流(Throttle)甚至退避等待。这意味着,哪怕分析业务的查询依然被路由到了 TiKV,只要它的 RU 消耗触及了配额上限,存储节点就会在内核态强制压制其处理速率,从而为高优先级的 OLTP 事务让出宝贵的 I/O 吞吐能力和 CPU 时间片。
在橙星云技术团队构建高并发的专业心理测评系统时,我们便曾直面过这种混合负载的极致拉扯。我们的平台每天需要处理大量 C 端用户的实时测评答题、状态流转和支付交易,这是绝对不能有任何毛刺的 OLTP 核心链路;与此同时,B 端的企业机构和心理咨询师又需要实时调取多维度的群体心理健康分析报告、预警大屏和数据看板,这类重度聚合分析天然会带来庞大的计算压力。如果我们选择拆分集群,利用数据同步工具将数据异构到独立的大数据数仓中,不仅会带来难以忍受的数据延迟,还会使系统架构变得臃肿不堪,大幅增加运维成本。
为了在单体集群内彻底驯服这两股截然不同的流量,橙星云技术团队在中间件架构上做了深度改造。我们不仅将面向机构后台的报表微服务的数据源强制绑定到独立的低优先级 Resource Group,更是对复杂查询链路进行了二次拦截,通过 AST(抽象语法树)解析自动为报表统计请求追加下推列存的 Hint。同时,针对 TiFlash 的 MPP(Massively Parallel Processing)架构,我们对其内存管理进行了极为严苛的调优。TiFlash 在执行分布式的 Hash Join 和聚合计算时,会像贪婪的巨兽一样吞噬内存。如果不对其进行硬性约束,多个并发的大表 Join 极易引发 OOM,导致整个列存节点崩溃。因此,我们深入调参了 TiFlash 的 Spill to Disk(落盘)机制,严格限定单条查询的内存配额(Memory Quota),宁可牺牲一部分分析报表的响应速度,通过将中间结果溢写到磁盘来换取稳定性,也绝不允许内存暴涨波及整个基础设施的健康度。
然而,哪怕资源隔离做得再极致,底层硬件的物理极限依然摆在那里。在双引擎架构中,Region 的分裂与调度本身也在悄悄消耗着资源。当系统面临突发的大批量数据写入时,TiKV 节点上会产生大量的 Raft 日志,这些日志需要源源不断地通过 Learner 身份同步给 TiFlash 节点进行重放和列转行转换。这种后台的同步流量如果失去控制,同样会占据大量的网络带宽,使得前台的查询请求在网络层出现排队。为此,在系统调优的过程中,必须深入修改 PD(Placement Driver)的调度策略,合理控制 leader-schedule-limit 和 region-schedule-limit 等参数,并为 Raft 同步流量配置独立的 QoS 队列,确保数据复制的洪流能够平缓而均匀地流淌,而不是在业务高峰期引发惊涛骇浪。
这种从 SQL 路由、CBO 代价模型修正,到 Request Unit 细粒度资源限制,再到内存防线与网络 QoS 的全方位立体防御,构成了我们在面对混合负载时必须掌握的核心工程能力。分布式数据库的 HTAP 愿景无疑是美好的,它打破了长久以来事务处理与分析处理之间的数据孤岛。但在冰冷而复杂的代码世界里,从来没有什么能够做到真正的“开箱即用”。只有对优化器的心智模型有着源码级的理解,对存储引擎的底层 I/O 调度有着精确的把控,才能在高速狂奔的在线交易与沉重繁杂的数据分析之间,维持那一种脆弱而又迷人的平衡。
