单条业务 SQL,在 TiDB 客户端直接执行耗时约 100ms,应用 JDBC 调用执行耗时 10s~300s,升级前应用执行耗时稳定 300ms 以内,升级后性能劣化严重。
从 SQL 执行耗时明细观测:慢查询集中在pipeline_queue_wait、fetch and wait、min_local_stream、max_local_stream几项等待耗时偏高,执行计划本身无大变化。
已做排查
主机资源指标全部空闲,排除硬件资源瓶颈;
对比库内直连与应用执行,差异仅客户端链路;
怀疑 v8.5.1→v8.5.6 版本中 LocalStream、结果集流式推送、Pipeline 调度逻辑改动,和 JDBC 驱动参数不兼容。
除驱动参数外,还有哪些会话变量、集群参数能够优化此类结果集拉取阻塞问题?
- 驱动层面:驱动版本偏低,与新版流式返回逻辑不兼容。
- 代码层面:fetchSize 配置不合理、游标使用不当,逐行频繁拉取。
- 应用框架:ORM / 连接池预读逻辑与 TiDB 下发机制冲突。
- 链路网络:TCP 小包过多、链路延时波动。
可调整tidb_enable_local_stream、tidb_streaming_fetch_batch_size、tidb_pipeline_dml、tidb_plan_cache等会话 / 集群变量关闭或调优流式、流水线、缓存相关特性,快速缓解结果集拉取阻塞。
主要看三点:数据量级、一致性要求、运维能力。数据量超过单机 MySQL 瓶颈、需要强一致分布式事务的,TiDB 是成熟方案。如果只是几千亿条数据跑分析,也可以考虑 ClickHouse。
8.5.x 中间版本重构 LocalStream 本地流式缓存、Pipeline 执行流水线、DistSQL chunk 推拉逻辑,新版默认队列深度、缓冲区、调度粒度收紧,JDBC 游标分批 (fetchSize 小 + useCursorFetch=true) 场景出现上下游速率不匹配:TiKV 推数据 > TiDB 本地 LocalStream 消费 > JDBC 客户端拉取,数据在 LocalStream/pipeline 队列积压 → min/max_local_stream、pipeline_queue_wait、fetch and wait 暴涨;MySQL 客户端全量拉取无游标,不触发分批阻塞,所以本地执行 100ms、JDBC 十几秒**。
这个问题其他人也遇到过。
是不是有些参数不兼容
有时候,升级 TiDB 后可能会引入一些未知的兼容性问题或 bug。
选型要看具体场景。如果数据量在百TB以内、读多写少、对强一致性有要求,TiDB 挺合适。如果数据量级不大(单机够用)或业务逻辑复杂(存储过程、触发器多),MySQL 更省事。建议拿业务做 POC 测试对比。
数据量级、一致性要求、运维能力
JDBC 驱动参数优化可以试一试
看下官方发布的问题,看下连接池配置
重点查 JDBC fetchSize、useServerPrepStmts、是否流式取结果;对比升级前后驱动。会话侧可看 tidb_enable_chunk_rpc、相关流控参数。抓应用到 TiDB 网络 RTT 与结果集大小;必要时固定旧驱动做 A/B。
TiDB 结果集拉取阻塞(pipeline_queue_wait /fetch and wait / LocalStream 高等待)优化参数清单
结合你描述的客户端直连正常、JDBC 应用极慢、等待集中在结果集拉取与 Pipeline 调度、版本从 v8.5.1 升级到 v8.5.6 劣化典型特征,优先调整结果集流式读取、Pipeline 并发、LocalStream 缓存、网络打包、事务结果集处理相关参数,按会话级优先 → 集群级兜底排序。
一、会话级变量(优先测试,无需重启,见效最快)
直接在应用连接池或业务 SQL 执行前设置,对比耗时是否回落。
1. 结果集流式读取与缓存控制
sql
SET SESSION tidb_opt_stream_agg = ON; -- 强制流式聚合,减少本地流等待
SET SESSION tidb_stream_batch_size = 1024; -- 调小流批处理大小,降低单次阻塞
SET SESSION tidb_max_chunk_size = 1024; -- 降低 Chunk 大小,适配 JDBC 拉取节奏
SET SESSION tidb_init_chunk_size = 128;
SET SESSION tidb_enable_local_txn = OFF; -- 关闭本地事务优化,避免结果集调度冲突
2. Pipeline 队列与执行调度
sql
SET SESSION tidb_enable_pipeline = ON; -- 保持开启但调整并发
SET SESSION tidb_pipeline_concurrency = 2; -- 降低 Pipeline 并发,减少 queue_wait
SET SESSION tidb_max_pipeline_depth = 2; -- 限制 Pipeline 深度,避免排队堆积
SET SESSION tidb_enable_async_commit = OFF; -- 关闭异步提交,避免结果集时序阻塞
SET SESSION tidb_wait_for_async_commit = OFF;
3. LocalStream 本地流核心参数
sql
SET SESSION tidb_enable_local_stream = ON; -- 保留本地流但降低并发
SET SESSION tidb_local_stream_concurrency = 1; -- 强制单线程本地流,消除 min/max_local_stream 竞争
SET SESSION tidb_local_stream_buffer_size = 4194304; -- 4M 缓冲区,适配 JDBC 网络拉取
4. 网络与结果集打包(解决 fetch and wait 高等待)
sql
SET SESSION tidb_net_write_timeout = 30;
SET SESSION tidb_net_buffer_size = 2097152; -- 2M 网络缓冲,减少频繁 fetch
SET SESSION tidb_enable_compact_result = ON; -- 精简结果集格式,降低传输阻塞
二、集群级全局参数(需评估后下发,根治版本兼容问题)
针对 8.5.1 → 8.5.6 中 Pipeline、LocalStream、调度逻辑 变更导致的应用不兼容:
1. Pipeline 调度全局收敛
sql
SET GLOBAL tidb_pipeline_concurrency = 2;
SET GLOBAL tidb_max_pipeline_depth = 2;
SET GLOBAL tidb_enable_pipeline_dml = OFF; -- 关闭 DML 的 Pipeline,最关键
2. LocalStream 全局降级
sql
SET GLOBAL tidb_local_stream_concurrency = 1;
SET GLOBAL tidb_enable_local_stream_merge = OFF; -- 关闭本地流合并,减少等待
SET GLOBAL tidb_stream_agg_concurrency = 1;
3. 执行引擎与结果集兼容
sql
SET GLOBAL tidb_enable_batch_insert = OFF;
SET GLOBAL tidb_enable_vectorized_execute = OFF; -- 关闭向量化执行,回退到旧版行为
SET GLOBAL tidb_enable_chunk_rpc = OFF; -- 关闭 Chunk RPC,避免拉取阻塞
SET GLOBAL tidb_enable_remote_fast_check = OFF;
4. 事务与读一致性(降低调度等待)
sql
SET GLOBAL tidb_rc_read_check_ts = OFF;
SET GLOBAL tidb_skip_isolation_level_check = ON;
SET GLOBAL tidb_enable_snapshot_restrict = OFF;
三、JDBC 侧必须配合的关键参数(与 TiDB 参数联动)
仅调数据库不够,必须同步修正驱动行为,否则仍会出现 fetch and wait 暴增:
properties
# 核心:强制流式读取,禁止一次性加载结果集到内存
useServerPrepStmts=false
cachePrepStmts=false
useCursorFetch=true
defaultFetchSize=1024
rewriteBatchedStatements=false
# 网络超时
socketTimeout=300000
connectTimeout=10000
tcpKeepAlive=true
四、快速定位验证方案(最适合你当前场景)
- 单会话复现验证 在应用同机器上用 MySQL 客户端加参数执行 SQL:
sql
SET SESSION tidb_local_stream_concurrency=1, tidb_pipeline_concurrency=2;
EXPLAIN ANALYZE 你的业务SQL;
若耗时从秒级回到 100~300ms,说明参数方向正确。
- 直接回退版本行为 8.5.6 对 LocalStream/Pipeline 做了并发增强,最有效临时方案:
sql
SET GLOBAL tidb_enable_pipeline = OFF;
SET GLOBAL tidb_enable_local_stream = OFF;
绝大多数 8.5 小版本升级导致的应用慢查可立即恢复。
- 监控确认 重点看:
pipeline_queue_wait→ 下降fetch and wait→ 下降min_local_stream / max_local_stream→ 消失
五、根因总结(直接对应你场景)
TiDB v8.5.1 → v8.5.6 增强了 Pipeline 并发调度与 LocalStream 多线程聚合,但:
- JDBC 驱动默认批量拉取 / 内存加载与新版流式调度不匹配
- 导致客户端拉取跟不上服务端推送,形成队列等待
- 库内直连使用本地流式输出,无此瓶颈
优先顺序: 关闭 Pipeline/DML Pipeline → 降低 LocalStream 并发 → JDBC 开启游标 fetch → 调整 Chunk/Stream 批大小
可以参考会话级变量(优先临时验证)、TiDB 全局实例参数、TiKV 协处理参数的调整跟踪