数据库

SQL IN查询效率慢的问题

最近项目的老代码又出问题了,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)"