pingcap / pingcap/tidb

stats: DDL stats meta update can deadlock with analyze save on mysql.stats_meta

Open
#67,496 2 comments 0 reactions 1 assignee Claimed by @0xPoe View on GitHub
component/statistics may-affects-7.1 may-affects-7.5 may-affects-8.1 may-affects-8.5 severity/major sig/planner type/bug
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

## Bug Report

### 1. Minimal reproduce step (Required)

1. Start a **real TiKV** cluster.
2. Create and analyze a tiny table so a real `mysql.stats_meta` row exists:
```sql
create table test.t_deadlock_repro (a int primary key, b int, key idx_b(b));
insert into test.t_deadlock_repro values (1,1),(2,2),(3,3);
analyze table test.t_deadlock_repro all columns;
```
3. Get its `tidb_table_id`:
```sql
select tidb_table_id
from information_schema.tables
where table_schema='test' and table_name='t_deadlock_repro';
```
4. Split both `mysql.stats_meta` row keys and `tbl(table_id)` index keys into many regions to widen the timing window:
```sql
split table mysql.stats_meta between (0) and (2000000) regions 32;
split table mysql.stats_meta index tbl between (-2000000) and (2000000) regions 32;
```
5. Concurrently run these two pessimistic transactions in a short loop:
- DDL stats update side:
```sql
begin pessimistic;
select count, modify_count from mysql.stats_meta where table_id = for update;
```
- Analyze save side:
```sql
begin pessimistic;
select snapshot, count, modify_count from mysql.stats_meta where table_id in (-, ) for update;
```
6. Check `information_schema.deadlocks`.

### 2. What did you expect to see? (Required)

No deadlock between the DDL-side stats meta update path and the analyze-save path.

### 3. What did you see instead (Required)

`information_schema.deadlocks` records the exact SQL pair above. On the client side this can surface as `Error 1213 (40001): Deadlock found when trying to get lock`.

The two code paths are:
- DDL side: `pkg/statistics/handle/ddl/ddl.go` -> `StatsMetaCountAndModifyCountForUpdate()`
- Analyze side: `pkg/statistics/handle/storage/save.go`

This looks like the same underlying lock-order class already described in `save.go`: point get vs batch point get on `mysql.stats_meta` acquire locks in different orders. The current `fakeID := -tableID` mitigation reduces contention but does not eliminate the deadlock class.

### 4. What is your TiDB version? (Required)

Reproduced on current master `fd7c13a934` with a locally built `tidb-server` binary plus TiUP nightly PD/TiKV on real TiKV.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.