最近项目的老代码又出问题了,mysql的in查询一次查询了5万个数据,cpu一下飙升到100%,这条查询sql也运行的近600s,那么是什么原因导致mysql的in查询效率如此之低,并且没有走索引呢?
IN的取值范围较大
因为本次的查询sql的in查询的范围大约有5万条,当IN的取值范围较大时会导致索引失效,走全表扫描。
那么IN的取值范围有多少条的时候会走索引呢?
新建了一张表,test_id有索引,插入了30万的数据。
explain上述sql,发现in的取值范围在10000条以内的时候,此条查询走索引。
但是一旦in的取值范围超过10000条后,将不会走索引。
当然在in的字段添加了索引的情况下,最终in走不走索引由mysql 优化执行器去判断,此底层逻辑较为复杂,但是在笔者的条件下,in的取值范围限制在10000条以内时,将走索引。
PostgreSQL in也有这个问题
应该有一部分原因 跟索引有关系,发现当in 项目过多时,索引会失效。目前本地测试是 in项目数>8 时,SQL的索引失效。PS:可以用 explain关键字分析执行的SQL的过程。索引有效:in项目数 =8
explain SELECT * FROM tableA where no in ('1','2','3','4','5','6','7','8') 返回结果: Bitmap Index Scan on tableA_pk (cost=0.00..5.24 rows=8 width=0) Index Cond: ((no)::text = ANY ('{1,2,3,4,5,6,7,8}'::text[])) ==》可以看到是 Index Scan
索引失效:in项目数 >8
explain SELECT * FROM tableA where no in ('1','2','3','4','5','6','7','8','9') 返回结果: Seq Scan on tableA (cost=0.02..15.72 rows=9 width=183) Filter: ((no)::text = ANY ('{1,2,3,4,5,6,7,8,9}'::text[])) ==》可以看到变成了Seq Scan,就是顺序检索,全表扫描了。
代替in的写法
explain WITH cte AS ( SELECT unnest(ARRAY['1','2','3','4','5','6','7','8']) AS number ) SELECT * FROM tableA inner join cte on no = cte.number;
Hash Join (cost=0.24..15.54 rows=8 width=215)" Hash Cond: ((tableA.no)::text = (unnest('{1,2,3,4,5,6,7,8}'::text[])))" Seq Scan on tableA (cost=0.00..13.80 rows=380 width=183)" Hash (cost=0.14..0.14 rows=8 width=32)" ProjectSet (cost=0.00..0.06 rows=8 width=32)" Result (cost=0.00..0.01 rows=1 width=0)"