业务 SQL

单条业务 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

四、快速定位验证方案(最适合你当前场景)

  1. 单会话复现验证 在应用同机器上用 MySQL 客户端加参数执行 SQL:

sql

SET SESSION tidb_local_stream_concurrency=1, tidb_pipeline_concurrency=2;
EXPLAIN ANALYZE 你的业务SQL;

若耗时从秒级回到 100~300ms,说明参数方向正确。

  1. 直接回退版本行为 8.5.6 对 LocalStream/Pipeline 做了并发增强,最有效临时方案:

sql

SET GLOBAL tidb_enable_pipeline = OFF;
SET GLOBAL tidb_enable_local_stream = OFF;

绝大多数 8.5 小版本升级导致的应用慢查可立即恢复。

  1. 监控确认 重点看:
  • 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 会话级变量

可以参考会话级变量(优先临时验证)、TiDB 全局实例参数、TiKV 协处理参数的调整跟踪