Skip to content
MOITOITECH
EET--:--:--
SUN--:--

MoiToi.TECHTiDB EngineeringGuide

TiDB query plans and statistics

Updated · Andres Kepler

In short

TiDB's optimizer is cost-based: it picks an index, join order and pushdown from row estimates, and those estimates come from table statistics. When statistics are stale or miss data skew, estimates go wrong and a query that was fast can switch to a slow plan without any code change. The diagnostic is EXPLAIN ANALYZE: a large gap between estRows and actRows points at statistics.


01

Why statistics decide the plan

For each query, the optimizer estimates how many rows every candidate step will touch and chooses the cheapest plan: which index, whether to read the index and then the table (IndexLookUp) or scan the table, which join algorithm and in what order, and how much to push down to TiKV or TiFlash.

Those row estimates come from statistics, not from the data itself. Correct statistics give good plans; wrong statistics give confidently wrong ones.


02

What TiDB statistics contain

Statistics are collected by ANALYZE TABLE, and automatically when the share of modified rows passes tidb_auto_analyze_ratio (0.5 by default), within the configured auto-analyze time window.

  • — Table level — total row count and how many rows have changed since the last ANALYZE.
  • — Column and index level — histograms of the value distribution, the most frequent values (TopN), the number of distinct values, and null counts.

03

Reading a plan

EXPLAIN shows the plan the optimizer intends: each operator, its estimated rows (estRows) and where it runs — root on the TiDB server, cop[tikv] or cop[tiflash] on storage. EXPLAIN ANALYZE runs the query and adds what actually happened: actRows, time and memory per operator.

Compare estRows with actRows from the bottom of the plan up. Where they diverge by orders of magnitude, the optimizer was working from a wrong picture, and every decision above that point is suspect.

-- What the optimizer expected vs what happened
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 42 AND status = 'open';

-- Which tables have drifted since their last ANALYZE
SHOW STATS_HEALTHY WHERE Healthy < 80;

-- Refresh statistics after a bulk load or large delete
ANALYZE TABLE orders;

04

Why a plan changes overnight

  • — A bulk load, delete or backfill changed the data, and auto-analyze has not run yet — or ran in the middle of it.
  • — Skewed data: a few values account for most rows, and the query hits one of them.
  • — Different parameter values make the same query shape touch very different row counts.
  • — An upgrade changed the optimizer's cost model or defaults.

05

Keeping plans stable

  • — Run ANALYZE as part of any bulk data change, not afterwards by chance.
  • — Bind the known-good plan for critical queries with SQL plan management (CREATE GLOBAL BINDING … USING …), or fix it with optimizer hints in the query.
  • — Lock statistics on tables whose statistics are known to be good and whose data should not move them.
  • — Find the queries worth attention from the slow query log and the statements summary tables (INFORMATION_SCHEMA.STATEMENTS_SUMMARY), not from guesswork — and track their latency over time.

Next step

Need this done on your cluster? TiDB Performance Sprint.

For a real performance or reliability problem that needs evidence, a change and a measurement. 2 weeks minimum; fixed price per sprint, scoped up front.