TiKV的缺陷?有没有大佬出来看看是否对数据库的稳定性有大的影响

【TiDB 使用环境】测试环境
【TiDB 版本】V 8.5.4
我们针对数据库做了一些测试,有问题请教各位社区大佬。

测试背景:资源组A,配额20000,优先级high,且开了流量突发。资源组B,配额50000,优先级MEDIUM,没开流量突发。A中有个的查询(A.table 2千万全表扫描),占用了很多RU,tikv节点cpu波动不大,当RU占用达到最高的时候,资源组B的大量查询(B.table的查询)进来了,直接导致B的配额被打满,服务的连接池也瞬间被打满,所有tikv节点的cpu也直接被打满;数据库无法对外提供服务。

有以下6点针对tikv层面的问题分析:

:red_circle: 缺陷1:优先级限制器被硬编码禁用(最致命)
代码位置:components/resource_control/src/resource_group.rs:279-283

fn enable_priority_limiter(&self) → bool {
// TODO: reenable it once when we fix resource_control: slow read caused by auto priority limiter · Issue #18939 · tikv/tikv · GitHub
// self.get_group_count() > 1
false // :red_circle: 硬编码禁用!
}
问题分析:

这是TiKV官方承认的Bug,指向GitHub issue #18939
优先级限制器被硬编码禁用,导致资源组优先级完全失效
即使A.table在high组、B.table在mid组,两者平等竞争CPU
当非核心业务大查询到来时,无法限制其资源占用
为什么平常正常:

平常负载低,资源充足,即使没有优先级限制也不会崩溃
一旦负载上升,这个缺陷就会导致雪崩
:red_circle: 缺陷2:令牌桶容量固定,无法应对突发流量
代码位置:components/resource_control/src/resource_limiter.rs:148-151

limiter: Limiter::builder(limit)
.refill(Duration::from_millis(1000)) // :red_circle: 固定1秒填充
.min_wait(Duration::from_millis(1))
.build(),
问题分析:

令牌桶固定1秒填充,容量固定
无法应对突发流量(如某个时间点有大量查询)
没有自适应调整机制,系统负载90%时仍然按固定速率填充令牌
为什么平常正常:

平常请求量低,令牌桶容量足够
突发流量到来时,令牌桶瞬间耗尽,无法限流
:red_circle: 缺陷3:线程池自动调整间隔过长(10秒)
代码位置:src/read_pool.rs:45-53

const READ_POOL_THREAD_CHECK_DURATION: Duration = Duration::from_secs(10); // :red_circle: 10秒检查一次
const READ_POOL_THREAD_HIGH_THRESHOLD: f64 = 0.8; // 80% CPU使用率
const READ_POOL_THREAD_LOW_THRESHOLD: f64 = 0.7; // 70% CPU使用率
const RUNNING_TASKS_PER_THREAD_THRESHOLD: i64 = 3; // 每线程3个任务
问题分析:

线程池自动调整每10秒检查一次
崩溃只用了很短的时间
调整条件苛刻:必须满足4个条件才能扩容
当前线程数 < 最大线程数
扩容后CPU不超载
线程繁忙度 >= 80%
队列任务 >= 每线程3个
为什么平常正常:

平常负载稳定,10秒检查间隔足够
突发流量在10秒内就可能崩溃,来不及扩容
:red_circle: 缺陷4:轻任务阈值过低(5ms),大查询容易绕过并发限制
代码位置:src/coprocessor/endpoint.rs:57

const LIGHT_TASK_THRESHOLD: Duration = Duration::from_millis(5); // :red_circle: 只有5ms
问题分析:

查询执行小于5ms不需要获取信号量permit
A.table查询首次执行时可能快速返回(缓存命中)
但后续查询可能因为数据量大而恶化至6-8秒,已经绕过了并发限制
缺乏基于资源消耗的熔断,只有超时熔断
为什么平常正常:

平常查询快速返回(<5ms),无需限流
查询恶化后,已经绕过了并发限制
:red_circle: 缺陷5:缺乏查询级别的资源熔断机制
代码位置:src/coprocessor/endpoint.rs:479-480

let deadline = tracker.req_ctx.deadline;
let handle_request_future = check_deadline(handler.handle_request(), deadline);
问题分析:

只有超时熔断(deadline检查),没有资源熔断
emp_assign_detail扫描2530万keys,没有扫描keys上限
tm_charge_whitelist CPU时间恶化至12.5秒,没有CPU时间上限
缺乏基于以下维度的主动熔断:
:red_circle: 扫描keys数量
:red_circle: CPU时间消耗
:red_circle: IO读取字节
:red_circle: 内存使用量
为什么平常正常:

平常扫描量小(<100万keys),不会触发资源问题
大查询到来时,没有主动熔断,任由资源耗尽

:red_circle: 缺陷6:资源组优先级限制器修复算法"buggy"
代码位置:components/resource_control/src/worker.rs(被注释禁用)

// We disable the priority worker by default because the current adjust
// algorithm is buggy. We may reenable it only we find a better algorithm.
问题分析:

TiKV官方注释明确指出调整算法有Bug
优先级限制器的动态调整算法被禁用
无法根据系统负载动态调整各优先级的配额
当系统过载时,无法自动削减低优先级查询的资源配额
为什么平常正常:

平常负载低,不需要动态调整优先级配额
过载时,无法动态调整,导致雪崩

1 个赞

我理解目前现存问题都会通过版本迭代持续修复,迭代本身就是补齐缺陷、持续提升稳定性的过程

精神上支持下

MySQL迁移注意自增主键,高并发写入集中在最后一个Region。

TiDB 资源控制属于持续演进模块,v8.x 早期版本存在大量未完善逻辑,新版本逐步补齐自适应令牌桶、查询资源熔断、优先级调度算法;但当前 v8.5.4 生产环境必须通过参数 + 架构隔离规避风险,不能被动等待版本修复。

v8.5.4 TiKV 硬编码关闭了资源组优先级限制,高低优先级 SQL 无隔离,高消耗慢查询抢占全部 CPU。

好专业的疑问,加油!

跨版本问题源于优化器规则和存储引擎层变更。

现阶段这些太智能的变更搞复杂了大概率出问题,直接影响性能和口碑

你引用的 issue #18939 确属 TiKV 已知问题,优先级限制器在特定条件下未启用,导致资源组优先级隔离弱化。规避方向:给非核心大查询的资源组显式设置更低配额并关闭流量突发(burst),避免它抢占;对 2 千万全表扫描类查询加 RU 上限或用 tidb_rc_read_check_ts、SQL 限流;关键业务组配额留足余量。根治需升级到修复该 issue 的 TiKV 版本,建议关注对应 release note 确认已合入。

缺乏“资源熔断”

这中影响还没有遇到过