在特殊数据场景下,你写的SQL必须添加hint!

12 阅读2分钟

请先右侧关注 持续分享SQL性能调优实战、SQL开发、MySQL原理、数据库设计等知识

今天在生产环境上遇到一个SQL 明明join表的关联字段上有索引,MySQL就是不走,由于版本又是8.0.13之前的版本。没有hash join 只能走BNL 让整个SQL奇慢无比 执行计划是这样的 1710405191689.png 执行需要93S 非常非常慢 但实际我在关联字段上不仅有索引,而且两表关联关系是1对1的。 我加了force index后 执行只需要 1.47S

为了弄清楚原因。我在测试环境进行了数据构造进行重现 构造测试数据

create table test1
(
 table_schema varchar(50),
 tablename varchar(200),
 ver int,
 id int primary key
);

create table test2
(
 table_schema varchar(50),
 tablename varchar(200),
 ver int,
 fromid int,
 id int primary key
);

set @i:=0;
insert into test1(table_schema,tablename,ver,id)
select a.table_schema,a.table_name,a.version ,@i := @i+ 1 as  id
 from information_schema.tables a, information_schema.tables b;
 
set @i:=0;
insert into test2(table_schema,tablename,ver,id)
select a.table_schema,a.table_name,a.version ,@i := @i+ 1 as  id
 from information_schema.tables a, information_schema.tables b;
 
 update test2 set fromid = 0;
 update test2 set fromid  = id where mod(id,2000000)= 0;

alter table test2 add index idx_fromid(fromid);
analyze table test2;
explain 
select * from test1 a 
left join test2 b  on a.id = b.fromid 
where a.id <1000000

image.png

重现了,因为我本地是8.0.32 有了hash join 所以显示了使用hash join

即使加use index 也无用 执行计划也和上面一模一样

explain 
select * from test1 a 
left join test2 b  use index(idx_fromid) on a.id = b.fromid 
where a.id <1000000

执行需要178s

加上force index

explain 
select * from test1 a 
left join test2 b  force index(idx_fromid) on a.id = b.fromid 
where a.id <1000000

执行只需要0.01S

出现这个问题的原因。MySQL估算行数严重错误。实际上因为100000以下的fromid都为0 ,一行都不符合。MySQL估算行数和区分度严重相关。因为我formid只有两个值,且偏移很大。200多W行数据 只有一行>0 其它都=0 而我的join 字段都是>0的。

**结论:以后当由于设计原因。区分度低,且数据分布极不均匀的,极有可能关联是不能走索引。需要开发在开发的时候就强制指定索引 force index **