较多的索引选择错误

【 TiDB 使用环境】生产环境
【 TiDB 版本】8.5.4
【遇到的问题:做SQL优化时遇到很多索引选择错误的,在mysql能正确使用索引,tidb中就经常不能正确使用,比如下面这个例子:
SQL:

SELECT
	L.*,
	G.GOODS_NAME,
	G.OUT_GOODS_ID,
	G.UPC_CODE,
	G.COVER_PIC
FROM 
	 D_GOODS G 
-- 	 force index(INDEX_OUT_GOODS_ID)
	left JOIN WM_GOODS_LOG L 
-- 	force index(INDEX_GOODS_ID) 
	ON G.GOODS_ID = L.GOODS_ID 
WHERE
	L.MERCH_ID = 635 
	AND L.WM_PLAT = 'MTSG'
	AND L.STORE_ID = 55599
	AND G.OUT_GOODS_ID = '423550'
limit 20;

执行计划的estrows非常高,索引应该用INDEX_OUT_GOODS_ID


如果去掉AND L.STORE_ID = 55599条件就是能正常:

健康度:

Db_name	Table_name	Partition_name	Healthy
ystpos	wm_goods_log	global	65
ystpos	wm_goods_log	p20260316	80
ystpos	wm_goods_log	p20260317	73
ystpos	wm_goods_log	p20260318	54
ystpos	wm_goods_log	p20260319	68
ystpos	wm_goods_log	p20260320	62
ystpos	wm_goods_log	p20260321	66
ystpos	wm_goods_log	p20260322	78
ystpos	wm_goods_log	p20260323	84
ystpos	wm_goods_log	p20260324	0
ystpos	wm_goods_log	p20260325	0
ystpos	wm_goods_log	p20260326	0
ystpos	wm_goods_log	p20260327	0
ystpos	wm_goods_log	p20260328	0
ystpos	wm_goods_log	p20260329	0
ystpos	wm_goods_log	p20260330	0
ystpos	wm_goods_log	p20260331	0
ystpos	wm_goods_log	p20260401	0
ystpos	wm_goods_log	p20260402	0
ystpos	wm_goods_log	p20260403	0
ystpos	wm_goods_log	p20260404	0
ystpos	wm_goods_log	p20260405	0
ystpos	wm_goods_log	p20260406	0
ystpos	wm_goods_log	p20260407	0

Db_name	Table_name	Partition_name	Healthy
ystpos	d_goods		100

和健康度关系不大,其他类型的SQL我整理表后依旧是这样

show index from WM_GOODS_LOG;

Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality
wm_goods_log	0	PRIMARY	1	GOODS_LOG_ID	A	232652800
wm_goods_log	0	PRIMARY	2	LAST_MODIFIED	A	631552
wm_goods_log	1	INDEX_LAST_MODIFIED	1	LAST_MODIFIED	A	631552
wm_goods_log	1	INDEX_MERCH_LAST_MODIFIED	1	MERCH_ID	A	109
wm_goods_log	1	INDEX_MERCH_LAST_MODIFIED	2	LAST_MODIFIED	A	631552
wm_goods_log	1	INDEX_GOODS_ID	1	GOODS_ID	A	340608

实际count的数据行数:241853739

优化器可能选择了以 WM_GOODS_LOG 为驱动表,或者在 Join 之后才过滤 OUT_GOODS_ID ,导致中间结果集巨大

https://docs.pingcap.com/zh/tidb/stable/sql-plan-replayer/

是不是有倾斜度或者字段关联性之类的影响

倾斜度是什么,字段是正常的varchar

就是数据分布不均匀

把WM_GOODS_LOG L的统计信息发出来。统计信息不对。

估行和实际的行数相差很多吗

show index from WM_GOODS_LOG; 的信息和count实际数据是差不多的

show index from WM_GOODS_LOG;

Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality
wm_goods_log	0	PRIMARY	1	GOODS_LOG_ID	A	232652800
wm_goods_log	0	PRIMARY	2	LAST_MODIFIED	A	631552
wm_goods_log	1	INDEX_LAST_MODIFIED	1	LAST_MODIFIED	A	631552
wm_goods_log	1	INDEX_MERCH_LAST_MODIFIED	1	MERCH_ID	A	109
wm_goods_log	1	INDEX_MERCH_LAST_MODIFIED	2	LAST_MODIFIED	A	631552
wm_goods_log	1	INDEX_GOODS_ID	1	GOODS_ID	A	340608

实际count的数据行数:241853739

感谢老师分享

1 个赞

有这种情况比如L.STORE_ID = 55599的数据明显比L.STORE_ID = 其他值的数据多很多
或者是L.MERCH_ID = 635
AND L.WM_PLAT = ‘MTSG’
AND L.STORE_ID = 55599
三个字段有关联性,就是L.MERCH_ID = 635以及L.WM_PLAT = 'MTSG’的数据大部分都是L.STORE_ID = 55599的
可能会导致优化器任务走全表扫描的代价更低?

WM_GOODS_LOG 是以那个字段做的分区,是STORE_ID嘛

INDEX_GOODS_ID 去重后的估算值是 340608,这列应该存在大量的重复值

创建联合索引 (MERCH_ID, WM_PLAT, STORE_ID, GOODS_ID)试试

应该用G.OUT_GOODS_ID这个索引的,能过滤非常多的数据,也是最快的,L表任何索引都不快

主键分区GOODS_LOG_ID + LAST_MODIFIED ,问题是应该用G表的OUT_GOODS_ID 索引,但是没用

这个问题是应该用g表的OUT_GOODS_ID 字段索引,不应该用l表的

region分布不匀么,重建表看看

但是得看优化器认为的过滤性高不高,如果数据分布不均,优化器可能认为全表扫描效率更好就不会走索引