各位大佬好,想请教下关于 tidb_mem_quota_binding_cache 参数线上调优相关经验:
1. 生产环境中配的多大?
2. 如果把这个绑定计划缓存调大,会有什么隐性隐患?比如 OOM、锁竞争、SQL解析变慢?
3. 实际运维中,是怎么实现的binding_cache监控?(我们的监控平台只支持select开头的查询,SHOW binding_cache status命令无法放到监控里。)
其实原因很简单,因为SHOW命令没法被你们的监控平台直接抓取,所以得换个思路,用SELECT去查系统表 来曲线救国。
搜了半天,没找到哪个系统视图存了binding_cache的使用大小。
tidb_mem_quota_binding_cache 控制的是 binding 计划缓存的内存上限,默认 64MB。这个值只影响 binding(SQL 绑定)的缓存,不是 SQL 执行内存,所以调大它本身不会直接导致执行阶段 OOM,主要影响是能缓存更多 binding。几点建议:1)生产上除非 binding 数量特别多(成千上万条)导致缓存放不下、频繁淘汰,否则默认 64MB 通常够用,先确认是否真有淘汰再调;2)判断依据看 SHOW GLOBAL BINDINGS 的条数和 binding_cache 的命中/淘汰情况,如果 binding 不多就没必要调大;3)监控问题:你说只能跑 select,可以查 INFORMATION_SCHEMA 里的相关表,binding 相关信息在系统表 mysql.bind_info 里,也可以用 SELECT 查 information_schema.cluster_* 相关视图,把这些接入监控,绕开 SHOW 命令。调大的隐患主要是占用 TiDB server 内存,量级不大,比起 SQL 执行内存(tidb_mem_quota_query)风险小很多。
关于tidb_mem_quota_binding_cache,建议先看看当前命中率:SHOW binding_cache status。如果命中率低,调大意义不大。
- 生产我们一般设4GB-8GB,具体看binding数量。建议先设4GB,观察后再调整。
- 调大主要风险是OOM,因为这块内存是TiDB进程共享的,不会自动释放。另外binding过多会增加解析开销,建议控制binding数量在1万以内。
设到4g,你们绑定了多少条?
社区 topic/1057681 综合分析报告
问题:tidb_mem_quota_binding_cache 生产配置参考、调大隐患、监控方案
版本:v7.5.3
一、参数机制(源码确认)
tidb_mem_quota_binding_cache 控制 binding 计划缓存的字节级内存配额,默认 64MB。底层是 kvcache.SimpleLRUCache,条目数无上限,纯按字节触发 LRU 淘汰。通过 memory.Tracker 挂到 TiDB 全局内存树,受 server 总 OOM 管控。
二、核心澄清:调大不会让 SQL 匹配变慢
这是社区最常见的误解。源码确认 binding 匹配是 O(1) hash lookup(bind_cache.go:137),通过 SQL digest 哈希直接定位,与 cache 内 binding 总条数 N 无关。cache 里 100 条还是 10000 条,单 SQL 匹配开销一样。
真正受 N 影响的操作(低频,不在 SQL 热路径上):
ADMIN RELOAD BINDINGS(全量重建 cache):O(N)- 后台 lease 周期 reload / baseline evolve 扫描:O(N),默认 3s/次
SHOW [GLOBAL] BINDINGS:O(N)
三、调大的真实风险
| 风险 | 说明 | 严重度 |
|---|---|---|
| 进程内存常驻增加 | 1 万条 binding ~10-30MB,与 query cache / session pool 抢 TiDB 进程内存 | 中 |
| bind_info 表查询变慢 | N 过大 → mysql.bind_info 内部查询开销增加(TICKET-810 先例) | 低 |
| baseline evolve CPU 开销 | 启用 tidb_evolve_plan_baselines 时,N 越多后台 CPU 越高 |
低 |
| bug 影响面放大 | v8.5.6 曾出现 LRU 非线程安全导致 OOM(tidb#68015,TICKET-8523)——cache 越大 bug 触发时膨胀越快。v7.5.3 不受此 bug 影响(仍用 Mutex 非 RWLock),但教训:quota 越大未来出现类似 bug 时影响面越大 | 参考级 |
注意:v7.5.3 使用 sync.Mutex 保护所有 cache 操作(bind_cache.go:32),不存在 v8.5.6 的并发 Get 改坏 LRU 链表问题,锁竞争风险极低。
四、生产配置建议
| binding 数量 | 建议配置 | 理由 |
|---|---|---|
| < 1000 条 | 保持默认 64MB | 足够,调大无收益 |
| 1000-5000 条 | 256MB-1GB | 按需调,观察进程内存 |
| > 5000 条 | 1-4GB | 先控制 binding 数量,再考虑调大 |
建议先查 binding 数量再决策:
SELECT COUNT(*) FROM mysql.bind_info WHERE status = 'enabled';
五、监控方案(SELECT-only 兼容)
-- 1. binding 总条数和趋势(唯一可信方式,SHOW 有截断 bug,见 TICKET-8148)
SELECT COUNT(*), SUM(LENGTH(original_sql) + LENGTH(bind_sql))
FROM mysql.bind_info WHERE status = 'enabled';
-- 2. 各库 binding 分布
SELECT original_db, COUNT(*)
FROM mysql.bind_info WHERE status = 'enabled'
GROUP BY original_db ORDER BY COUNT(*) DESC;
-- 3. 最近创建/更新的 binding
SELECT original_db, original_sql, bind_sql, create_time, update_time
FROM mysql.bind_info WHERE status = 'enabled'
ORDER BY update_time DESC LIMIT 20;
监控盲区:TiDB 未暴露 binding cache hit/miss ratio 的 Prometheus metric(源码确认),无法直接量化调大后的命中率收益。只能通过 TiDB 进程总内存趋势间接判断。
六、建议对客回复
tidb_mem_quota_binding_cache默认 64MB 控制的是 binding 计划缓存的内存上限。调大不会让 SQL 匹配/解析变慢(匹配是 O(1) 哈希查找,与 binding 总数无关),主要影响是 TiDB 进程常驻内存增加。生产建议:
- 先用
SELECT COUNT(*) FROM mysql.bind_info WHERE status='enabled'查当前 binding 数量- binding < 1000 条保持默认即可;数千条以上可酌情调到 256MB-1GB
- 监控用
SELECT FROM mysql.bind_info(不要用 SHOW GLOBAL BINDINGS,长 SQL 会被截断)- 调大后关注 TiDB 进程总内存趋势
风险方面:v7.5.3 的 binding cache 使用 Mutex 保护,不存在并发安全问题;主要风险是占用进程内存导致与其他内存区域(query cache、session pool)竞争。建议 binding 总数控制在 1 万条以内。
分析基于:bind_cache.go / optimize.go release-7.5 源码 + KB 历史工单
tidb_mem_quota_binding_cache:SQL Binding 计划缓存内存上限,单位字节,控制所有绑定规则缓存执行计划占用总内存; 每条 Binding 会缓存优化后的物理执行计划,频繁更新 / 大量绑定语句会持续占用这块内存,达到阈值后会淘汰最久未使用的 Binding 缓存计划,不会直接删除绑定规则(规则持久化在 mysql.bind_info 表,只是缓存失效,下次执行重新生成计划)。
1)通用中小集群(TP 业务,绑定条数<500)
- 标配:268435456(256MB) 适用:在线 OLTP,单 TiDB 节点绑定 SQL 不超过 500 条,QPS 1~3w,无大量复杂多表 JOIN、CTE、窗口函数。
2)中大型 TP / 混合负载(绑定 500~2000 条,复杂 SQL 多)
- 标配:536870912(512MB) 适用:金融、政务系统,大量慢 SQL 绑定,报表复杂查询多,单节点绑定上千条。
3)重报表 / 复杂 OLAP 混合集群(绑定>2000 条,大 JOIN、子查询多)
- 标配:1073741824(1GB) 上限不建议超过 2GB(2147483648),极少场景用到。