有一个数据库索引不生效的问题

最近看线上的数据库的慢日志,发现有些一条非常慢的sql。

SELECT
*
FROM
    "batch"
WHERE
    compensation_end_time >= '2024-07-31 21:02:58+08'
    AND "subsidy_id" = '100498835946114078'
    AND "type" = '1'
    AND status NOT IN ('4','6','5'); 

但是已经存在这个索引了

create index idx_batch_subsidy_status
    on batch (subsidy_id, status);

所以执行explain显示

Gather  (cost=1000.43..334292.14 rows=1455 width=533)
   Workers Planned: 2
   ->  Parallel Index Scan using batch_subsidy_id_status_idx on batch  (cost=0.4
3..333146.64 rows=606 width=533)
         Index Cond: (subsidy_id = '100498835946114078'::bigint)
         Filter: ((compensation_end_time >= '2024-07-31 21:02:58+08'::timestamp
with time zone) AND (type = '1'::smallint) AND (status <> ALL ('{4,6,5}'::smalli
nt[])))

但是因为索引过滤掉的数据不多,后面留给通过filter过滤的太多,导致缓慢造成的。

第一时间想到的是, 加一个下面的索引, 然后把上面已经存在的索引删除掉,因为下面这个明显包含上面那个了。

CREATE INDEX CONCURRENTLY idx_batch_subsidy_status_type_compensation
    ON batch (subsidy_id, status, type, compensation_end_time);

测试发现,加上新的以后,查询依然不走新索引,走的依然是旧的索引。非常奇怪,最终分析发现,是表某些subsidy_id关联的数据存在比例,已经不符合之前的预期了。正常业务预期status比type更具有选择性一些,甚至不用关心type,但是因为某些业务使用习惯,产生了很多条数据,比如有百万条。但是type =1的一条都没有,索引建立的时候status在前面,符合条件的本来就很多,通过subsidy_id+status 查询出来符合条件的本来就很多。type索引的选择性不强,索引没有去走索引,而是通过过滤。考虑到这个以后,调整新的索引位置。

CREATE INDEX batch_subsidy_id_idx ON public.batch (subsidy_id,"type",status,compensation_end_time);

第一步通过type就直接过滤掉不存在的数据,后面也不用去过滤了。此时sql执行就正确利用索引了,原来旧的subsidy_id + status的索引继续保留。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容