TiDB 处理混合负载的核心难题,不在于能不能同时跑 OLTP 和 OLAP,而在于当一条复杂的分析查询毫无征兆地打进来,瞬间占满计算资源,导致那些毫秒级响应的在线交易开始排队超时。这背后是资源争抢的典型场景:一个全表扫的 COUNT 操作,可能让整个集群的 TiKV 节点 CPU 飙高,在线业务的读写延迟毛刺瞬间拉大。解决这个问题的思路,不是简单地做物理隔离,而是在同一套集群内,让在线查询和离线查询走不同的执行路径,用资源管控把两类负载在逻辑上彻底分开。
资源争抢的本质:为什么离线查询会拖垮在线业务要理解隔离策略,先得看清争抢发生在哪些层面。TiDB 的分布式架构下,SQL 计算在 TiDB Server 层完成,数据读取在 TiKV 层完成。一条离线查询通常要扫描海量数据,它会在 TiKV 侧发起大量 RPC 请求,同时把数据拉到 TiDB Server 侧做聚合和计算。这带来两个维度的压力:一是 TiKV 的 IO 和 CPU 被大量 scan 操作占据,二是 TiDB Server 的内存和 CPU 被中间结果集撑满。在线业务大多是点查或小范围扫描,对延迟极度敏感,一旦被离线查询挤占 CPU 时间片,P99 延迟会迅速恶化。单纯靠增加节点无法根治这个问题,因为一条大查询就能把单个 TiKV 节点的资源吃干抹净。
TiDB 7.x 的资源组机制:从软限制到硬隔离TiDB 从 7.1 版本开始引入资源组(Resource Group)功能,这不再是简单的优先级建议,而是基于实际资源消耗的硬控。你可以创建两个资源组,比如把在线业务划入 rg_oltp,离线分析划入 rg_olap,然后给每个组设定明确的资源上限。资源组的控制粒度包括 CPU 时间、IO 带宽和内存使用。当一个离线查询试图越界时,它会被主动降速,而不是等系统资源耗尽后再被动反应。这套机制依赖 TiKV 侧的细粒度资源统计和流控,通过令牌桶算法对请求进行限速,确保在线资源组始终有足够的资源余量。
-- 创建在线业务资源组,设置较高优先级和资源保障 CREATE RESOURCE GROUP rg_oltp RU_PER_SEC = 5000 PRIORITY = HIGH; -- 创建离线分析资源组,设置资源上限和低优先级 CREATE RESOURCE GROUP rg_olap RU_PER_SEC = 3000 PRIORITY = LOW; -- 将用户绑定到对应资源组 ALTER USER 'app_user' RESOURCE GROUP rg_oltp; ALTER USER 'analyst' RESOURCE GROUP rg_olap;
上面的配置中,RU_PER_SEC 代表每秒可消耗的请求单元,这是一个统一度量 CPU 和 IO 消耗的抽象单位。在线组分配 5000 单位,离线组分配 3000 单位,两者各自独立,互不挤占。PRIORITY 参数决定了当系统整体资源紧张时,哪个组的请求优先被调度。这种隔离方式比单纯靠连接数限制或 SQL 超时有效得多,因为它直接作用于执行层面,而不是在入口处做粗粒度拦截。
TiFlash 列存副本:物理隔离在线与离线查询的最佳实践资源组解决的是逻辑层面的隔离,但离线查询对大量数据的扫描需求依然会给 TiKV 带来 IO 压力。更彻底的方案是让离线查询完全不走 TiKV,而是走 TiFlash 列存副本。TiFlash 是 TiDB 的列式存储引擎,专门为分析场景设计,数据通过 Raft 协议从 TiKV 实时同步过来。开启 TiFlash 后,你可以通过 SQL Hint 或引擎自动选择,强制离线查询只读 TiFlash,在线查询只读 TiKV,实现存储层面的物理隔离。这样做的好处是,离线查询无论扫多少数据,都不会对 TiKV 的行存引擎产生任何 IO 干扰,在线业务的读写完全不受影响。
-- 为分析型查询强制指定 TiFlash 引擎 SELECT /*+ READ_FROM_STORAGE(TIFLASH[orders, lineitem]) */ l_returnflag, l_linestatus, SUM(l_quantity) AS sum_qty FROM lineitem WHERE l_shipdate <= '1998-09-02' GROUP BY l_returnflag, l_linestatus;
要让这套方案稳定运行,需要做好几件事。首先,TiFlash 节点的数量要足够承载离线查询的并发量,建议根据数据量和查询复杂度做容量规划,通常 TiFlash 与 TiKV 的节点比例在 1:3 到 1:5 之间。其次,要确保 TiFlash 副本的同步延迟在可接受范围内,对于实时性要求高的分析场景,可以监控同步 lag 指标。最后,需要在应用层做好路由逻辑,所有离线报表、BI 工具、adhoc 查询统一走 TiFlash,在线交易走 TiKV,这个分流可以在连接串层面配置,也可以在 ORM 框架中根据查询类型动态选择数据源。
SQL 绑定与查询分类:自动识别并路由查询类型实际业务中,很难要求开发人员每一条 SQL 都手动加 Hint。更工程化的做法是利用 TiDB 的 SQL 绑定功能,把离线查询的特征模式预先定义好,让优化器自动选择 TiFlash。比如,你可以把包含 GROUP BY、窗口函数、大表 JOIN 的查询模板绑定到 TiFlash 引擎。同时,结合资源组,让这些走 TiFlash 的查询自动归入 rg_olap 资源组,实现从存储到计算的全链路隔离。
-- 创建 SQL 绑定,将匹配模式的查询强制路由到 TiFlash CREATE GLOBAL BINDING FOR SELECT l_returnflag, l_linestatus, SUM(l_quantity) FROM lineitem WHERE l_shipdate <= ? GROUP BY l_returnflag, l_linestatus USING SELECT /*+ READ_FROM_STORAGE(TIFLASH[lineitem]) */ l_returnflag, l_linestatus, SUM(l_quantity) FROM lineitem WHERE l_shipdate <= ? GROUP BY l_returnflag, l_linestatus;
SQL 绑定的威力在于,它可以在不修改应用代码的前提下,改变查询的执行计划。你可以通过分析慢日志,找出那些消耗资源大的查询模式,批量创建绑定规则。TiDB 的绑定支持模板匹配,问号作为占位符可以匹配不同的常量值,这样一条绑定规则就能覆盖一类查询。当离线查询被自动路由到 TiFlash 并归入低优先级资源组后,在线业务几乎感知不到它们的存在。
监控与告警:验证隔离效果的关键指标隔离策略上线后,必须通过监控来验证效果。需要重点关注的指标包括:TiKV 节点的 CPU 使用率和 IO 延迟,按资源组拆分的 RU 消耗量,TiFlash 节点的查询延迟和吞吐量,以及在线业务的 P99 延迟变化趋势。TiDB Dashboard 提供了资源组维度的监控面板,可以直观看到 rg_oltp 和 rg_olap 各自的资源消耗曲线。如果发现在线资源组的 RU 消耗出现尖刺,说明有离线查询逃逸到了在线路径,需要检查 SQL 绑定是否覆盖完整,或者是否有新的查询模式出现。
一个常见的坑是,某些离线查询的中间结果集过大,即使走了 TiFlash,也会在 TiDB Server 侧消耗大量内存,间接影响同一节点上的在线查询。解决这个问题需要开启 TiDB Server 的内存限制,并结合资源组的内存配额控制。另一个容易被忽略的点是,DDL 操作和统计信息收集也会消耗大量资源,建议把这些维护操作也归入 rg_olap 资源组,并安排在业务低峰期执行。
渐进式落地路径:从单集群混合负载到彻底隔离对于已经在生产环境运行的 TiDB 集群,不建议一次性把所有隔离策略全部上线。更稳妥的做法是分三步走。第一步,先开启资源组功能,给在线和离线业务分配不同的资源组,观察一周,确认在线延迟不再受离线查询影响。第二步,为关键的大表添加 TiFlash 副本,把最消耗资源的几条离线查询通过 SQL 绑定强制路由到 TiFlash,进一步释放 TiKV 压力。第三步,全面梳理慢日志,把所有符合离线特征的查询都绑定到 TiFlash 和 rg_olap 资源组,形成闭环。每一步都要有监控数据支撑,确保在线业务的延迟指标没有回退。
这套方案的本质,是把 TiDB 原生具备的行列混合存储能力和资源管控能力结合起来,在不引入外部组件、不拆分集群的前提下,实现混合负载的有效隔离。相比传统的 MySQL 加 Hadoop 或 MySQL 加 ClickHouse 的架构,TiDB 的单集群方案在运维复杂度、数据实时性和成本控制上都有明显优势。只要把资源组和 TiFlash 这两个特性用好,绝大多数混合负载场景都能得到妥善解决。
