postgresql----索引失效

本文探讨了数据库中索引失效的多种情况,包括计算、函数调用、类型转换、使用!=或NOT、模糊查询、索引字段占比高、多字段索引使用不当及使用OR等情况,并通过实际测试案例进行了说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是索引失效?如果where过滤条件设置不合理,即使索引存在,且where过滤条件中包含索引列,也会导致全表扫描,索引不起作用。什么条件下会导致索引失效呢?

1.任何计算、函数、类型转换

2.!=

3.NOT,相当于使用函数

4.模糊查询通配符在开头

5.索引字段在表中占比较高

6.多字段btree索引查询条件不包含第一列

7.多字段索引查询条件使用OR(有时也会走索引扫描,但查询效率不高)

 

测试表

test=# \timing 
Timing is on.
test=# create table tbl_index(a bigint,b timestamp without time zone ,c varchar(12));
CREATE TABLE
Time: 147.366 ms
test=# insert into tbl_index select generate_series(1,10000000),clock_timestamp()::timestamp without time zone,'bit me';
INSERT 0 10000000
Time: 30982.723 ms

 

1.任何计算、函数、类型转换

crtest=# create index idx_tbl_index_a on tbl_index (a);
CREATE INDEX
Time: 19634.874 ms
test=# 
test=# explain analyze select * from tbl_index where a = 1;
                                                          QUERY PLAN                                                          
------------------------------------------------------------------------------------------------------------------------------
 Index Scan using idx_tbl_index_a on tbl_index  (cost=0.43..8.45 rows=1 width=23) (actual time=59.844..59.850 rows=1 loops=1)
   Index Cond: (a = 1)
 Planning time: 22.788 ms
 Execution time: 60.011 ms
(4 rows)

Time: 84.865 ms
test=# explain analyze select * from tbl_index where a + 1 = 1;
                                                           QUERY PLAN                                                            
---------------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..84399.00 rows=50000 width=23) (actual time=7678.109..7678.109 rows=0 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on tbl_index  (cost=0.00..78607.33 rows=20833 width=23) (actual time=7350.047..7350.047 rows=0 loops=3)
         Filter: ((a + 1) = 1)
         Rows Removed by Filter: 3333333
 Planning time: 0.112 ms
 Execution time: 7688.615 ms
(8 rows)

Time: 7780.024 ms
test=# explain analyze select * from tbl_index where power(a,2) = 1;
                                                           QUERY PLAN                                                            
---------------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..94815.67 rows=50000 width=23) (actual time=47.516..6902.399 rows=1 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on tbl_index  (cost=0.00..89024.00 rows=20833 width=23) (actual time=4607.894..6892.174 rows=0 loops=3)
         Filter: (power((a)::double precision, '2'::double precision) = '1'::double precision)
         Rows Removed by Filter: 3333333
 Planning time: 13.564 ms
 Execution time: 6904.232 ms
(8 rows)

Time: 7051.482 ms
test=# 
test=# explain analyze select * from tbl_index where a::varchar = '1';
                                                           QUERY PLAN                                                            
---------------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..94815.67 rows=50000 width=23) (actual time=1.239..6689.272 rows=1 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on tbl_index  (cost=0.00..89024.00 rows=20833 width=23) (actual time=4449.890..6679.233 rows=0 loops=3)
         Filter: (((a)::character varying)::text = '1'::text)
         Rows Removed by Filter: 3333333
 Planning time: 1.029 ms
 Execution time: 6692.329 ms
(8 rows)

Time: 6723.530 ms

 

在表tbl_index.a字段创建btree索引,使用a=1索引生效,但是下面的例子运算、函数、类型转换却导致索引失效了。

where a + 1 = 1

where power(a,2) = 1

where a::varchar = '1'

如何解决呢?可参考前面的表达式索引解决:

create index idx_tbl_index_a on tbl_index ((a+1));

create index idx_tbl_index_a on tbl_index ((power(a,2)));

create index idx_tbl_index_a on tbl_index ((a::varchar));

 

2.!=

test=# explain analyze select * from tbl_index where a != 1;
                                                       QUERY PLAN                                                        
-------------------------------------------------------------------------------------------------------------------------
 Seq Scan on tbl_index  (cost=0.00..140899.00 rows=9999999 width=23) (actual time=0.049..11004.864 rows=9999999 loops=1)
   Filter: (a <> 1)
   Rows Removed by Filter: 1
 Planning time: 0.206 ms
 Execution time: 11585.859 ms
(5 rows)

Time: 11587.146 ms

 

3.NOT,相当于使用函数

test=# explain analyze select * from tbl_index where a is null;
                                                          QUERY PLAN                                                          
------------------------------------------------------------------------------------------------------------------------------
 Index Scan using idx_tbl_index_a on tbl_index  (cost=0.43..4.45 rows=1 width=23) (actual time=30.092..30.092 rows=0 loops=1)
   Index Cond: (a IS NULL)
 Planning time: 33.783 ms
 Execution time: 41.838 ms
(4 rows)

Time: 102.544 ms
test=# explain analyze select * from tbl_index where a is not null;
                                                        QUERY PLAN                                                        
--------------------------------------------------------------------------------------------------------------------------
 Seq Scan on tbl_index  (cost=0.00..115899.00 rows=10000000 width=23) (actual time=3.062..6309.908 rows=10000000 loops=1)
   Filter: (a IS NOT NULL)
 Planning time: 0.099 ms
 Execution time: 6877.566 ms
(4 rows)

Time: 6878.156 ms

以上比较可知where a is null索引生效,但是where a is not null导致索引生效。类似导致索引失效的还有NOT IN,NOT LIKE等,但是NOT EXISTS不会导致索引失效。下面的例子可以看到tbl_index表仍进行索引扫描,但是性能仍有限制,使用NOT IN虽然索引失效,但性能比NOT EXISTS要高。这个和我之前的认识有些出入,之前测试发现NOT EXISTS比NOT IN性能高,看来情况不同,性能也是不一定的。

test=# explain analyze select * from tbl_index where a not in (select a from tbl_test );
                                                       QUERY PLAN                                                        
-------------------------------------------------------------------------------------------------------------------------
 Seq Scan on tbl_index  (cost=14.50..140913.50 rows=5000000 width=23) (actual time=1.393..3717.312 rows=9999000 loops=1)
   Filter: (NOT (hashed SubPlan 1))
   Rows Removed by Filter: 1000
   SubPlan 1
     ->  Seq Scan on tbl_test  (cost=0.00..12.00 rows=1000 width=8) (actual time=0.038..0.236 rows=1000 loops=1)
 Planning time: 0.134 ms
 Execution time: 4147.857 ms
(7 rows)

Time: 4148.615 ms
test=# explain analyze select * from tbl_index where not exists (select null from tbl_test where tbl_test.a = tbl_index.a);
                                                                       QUERY PLAN                                                    
                    
-------------------------------------------------------------------------------------------------------------------------------------
--------------------
 Merge Anti Join  (cost=62.45..218187.26 rows=9999000 width=23) (actual time=1.698..16909.581 rows=9999000 loops=1)
   Merge Cond: (tbl_index.a = tbl_test.a)
   ->  Index Scan using idx_tbl_index_a on tbl_index  (cost=0.43..193110.43 rows=10000000 width=23) (actual time=0.035..14781.400 row
s=10000000 loops=1)
   ->  Sort  (cost=61.83..64.33 rows=1000 width=8) (actual time=0.390..0.659 rows=1000 loops=1)
         Sort Key: tbl_test.a
         Sort Method: quicksort  Memory: 71kB
         ->  Seq Scan on tbl_test  (cost=0.00..12.00 rows=1000 width=8) (actual time=0.038..0.194 rows=1000 loops=1)
 Planning time: 0.339 ms
 Execution time: 17530.472 ms
(9 rows)

Time: 17594.258 ms

 

4.模糊查询通配符在开头

test=# explain analyze select * from tbl_index where c like 'bit%';
                                                        QUERY PLAN                                                        
--------------------------------------------------------------------------------------------------------------------------
 Seq Scan on tbl_index  (cost=0.00..140899.00 rows=10000000 width=23) (actual time=0.099..1685.317 rows=10000000 loops=1)
   Filter: ((c)::text ~~ 'bit%'::text)
 Planning time: 55.373 ms
 Execution time: 2104.863 ms
(4 rows)

Time: 2164.464 ms

test=# explain analyze select * from tbl_index where c like '%me';
                                                        QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 Seq Scan on tbl_index  (cost=0.00..140899.00 rows=10000000 width=23) (actual time=20.172..5507.741 rows=10000000 loops=1)
   Filter: ((c)::text ~~ '%me'::text)
 Planning time: 65.603 ms
 Execution time: 6007.367 ms
(4 rows)

 

5.索引字段在表中占比较高

test=# insert into tbl_index values (10000001,'2015-05-23 00:00:00','haha');
INSERT 0 1
Time: 88.226 ms
test=# explain analyze select * from tbl_index where c = 'bit me';
                                                        QUERY PLAN                                                        
--------------------------------------------------------------------------------------------------------------------------
 Seq Scan on tbl_index  (cost=0.00..140899.00 rows=10000000 width=23) (actual time=0.051..6758.236 rows=10000000 loops=1)
   Filter: ((c)::text = 'bit me'::text)
   Rows Removed by Filter: 1
 Planning time: 0.128 ms
 Execution time: 7237.900 ms
(5 rows)

Time: 7238.685 ms
test=# explain analyze select * from tbl_index where c = 'haha';
                                                         QUERY PLAN                                                         
----------------------------------------------------------------------------------------------------------------------------
 Index Scan using idx_tbl_index_c on tbl_index  (cost=0.43..4.45 rows=1 width=23) (actual time=0.063..0.065 rows=1 loops=1)
   Index Cond: ((c)::text = 'haha'::text)
 Planning time: 0.219 ms
 Execution time: 2.869 ms
(4 rows)

Time: 4.942 ms

 

test=# drop index idx_tbl_index_a;
DROP INDEX
Time: 134.873 ms
test=# drop index idx_tbl_index_c;
DROP INDEX
Time: 173.572 ms

 

6.多字段btree索引查询条件不包含第一列

test=# explain analyze select * from tbl_index where a = 10000001 and c = 'haha';
                                                           QUERY PLAN                                                           
--------------------------------------------------------------------------------------------------------------------------------
 Index Scan using idx_tbl_index_a_c on tbl_index  (cost=0.43..6.20 rows=1 width=23) (actual time=23.254..23.257 rows=1 loops=1)
   Index Cond: ((a = 10000001) AND ((c)::text = 'haha'::text))
 Planning time: 36.050 ms
 Execution time: 35.710 ms
(4 rows)

Time: 78.816 ms
test=# explain analyze select * from tbl_index where  c = 'haha';
                                                         QUERY PLAN                                                          
-----------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..68982.44 rows=1 width=23) (actual time=7869.579..7890.974 rows=1 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on tbl_index  (cost=0.00..67982.34 rows=0 width=23) (actual time=7468.480..7468.480 rows=0 loops=3)
         Filter: ((c)::text = 'haha'::text)
         Rows Removed by Filter: 3333333
 Planning time: 0.130 ms
 Execution time: 7891.137 ms
(8 rows)

Time: 7891.937 ms
test=# explain analyze select * from tbl_index where a = 10000001;
                                                          QUERY PLAN                                                          
------------------------------------------------------------------------------------------------------------------------------
 Index Scan using idx_tbl_index_a_c on tbl_index  (cost=0.43..8.45 rows=1 width=23) (actual time=0.154..0.156 rows=1 loops=1)
   Index Cond: (a = 10000001)
 Planning time: 0.257 ms
 Execution time: 0.206 ms
(4 rows)

Time: 1.119 ms

 

7.多字段索引查询条件使用OR

test=# explain analyze select * from tbl_index where a = 10000001 or c = 'haha';
                                                         QUERY PLAN                                                          
-----------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..79399.11 rows=1 width=23) (actual time=7321.821..7323.593 rows=1 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on tbl_index  (cost=0.00..78399.01 rows=0 width=23) (actual time=7307.413..7307.413 rows=0 loops=3)
         Filter: ((a = 10000001) OR ((c)::text = 'haha'::text))
         Rows Removed by Filter: 3333333
 Planning time: 0.163 ms
 Execution time: 7324.821 ms
(8 rows)

Time: 7325.532 ms
test=# explain analyze select * from tbl_index where a = 10000001 and c = 'haha';
                                                          QUERY PLAN                                                          
------------------------------------------------------------------------------------------------------------------------------
 Index Scan using idx_tbl_index_a_c on tbl_index  (cost=0.43..6.20 rows=1 width=23) (actual time=0.040..0.041 rows=1 loops=1)
   Index Cond: ((a = 10000001) AND ((c)::text = 'haha'::text))
 Planning time: 0.165 ms
 Execution time: 0.093 ms
(4 rows)

Time: 32.904 ms

 

转载于:https://www.cnblogs.com/alianbog/p/5648455.html

### PostgreSQL 索引失效的条件 在 PostgreSQL 中,索引的使用效率受到查询条件、数据分布以及索引结构的影响。以下是一些常见的导致索引失效的条件: #### 1. **对索引列进行计算或函数操作** 当在查询条件中对索引列进行计算、使用函数或类型转换时,PostgreSQL 通常无法直接使用索引。例如: ```sql SELECT * FROM users WHERE EXTRACT(YEAR FROM birth_date) = 1990; ``` 此查询无法使用 `birth_date` 上的普通 B-tree 索引,因为查询条件中使用了函数 `EXTRACT` [^3]。 #### 2. **使用 `!=` 或 `<>` 运算符** 当查询条件中使用了 `!=` 或 `<>` 运算符时,索引通常不会被使用,因为这些操作符无法有效利用 B-tree 索引的有序性。 ```sql SELECT * FROM users WHERE status != 'active'; ``` 该查询通常不会使用 `status` 列上的索引 。 #### 3. **使用 `NOT` 条件** `NOT` 条件本质上等同于使用了函数,因此可能导致索引失效。例如: ```sql SELECT * FROM users WHERE NOT (status = 'active'); ``` 该查询通常不会使用 `status` 列上的索引 。 #### 4. **模糊查询以通配符开头** 当使用 `LIKE` 或 `ILIKE` 并以通配符(如 `%`)开头时,索引通常无法被使用。 ```sql SELECT * FROM users WHERE name LIKE '%john'; ``` 该查询无法使用 `name` 列上的普通 B-tree 索引 [^3]。 #### 5. **索引列在中占比过高** 当某个索引列的值在整个中占比过高时(例如超过 5%),PostgreSQL 查询优化器可能会选择全扫描而不是使用索引,因为索引扫描的代价可能更高。 ```sql SELECT * FROM orders WHERE status = 'pending'; ``` 如果 `status = 'pending'` 的记录占比过高,查询优化器可能不会使用该列上的索引 [^1]。 #### 6. **多字段 B-tree 索引未包含第一列** 在使用多字段 B-tree 索引时,如果查询条件中有包含索引的第一列,索引可能不会被使用。 ```sql CREATE INDEX idx_orders_user_status ON orders(user_id, status); SELECT * FROM orders WHERE status = 'shipped'; ``` 该查询不会使用 `idx_orders_user_status` 索引,因为查询条件中有包含 `user_id` [^3]。 #### 7. **使用 `OR` 条件** 当查询条件中使用了 `OR`,并且 `OR` 两侧的列有全部被索引覆盖时,索引可能不会被使用,或者即使使用,效率也不高。 ```sql SELECT * FROM users WHERE name = 'Alice' OR email = 'alice@example.com'; ``` 如果 `name` 和 `email` 上分别有索引,但查询优化器可能无法高效合并使用多个索引 [^3]。 #### 8. **数据分布不均** 当某个字段中某些值出现频率过高时(例如性别字段中“男”占绝大多数),在查询其他值时索引可能失效。例如: ```sql SELECT * FROM users WHERE gender = 'female'; ``` 如果 `gender = 'female'` 的记录占比很小,但索引扫描的效率仍然可能不如顺序扫描 [^1]。 #### 9. **查询条件频繁变化** 当查询条件频繁变化时,PostgreSQL 的查询优化器可能无法稳定地选择最优的索引路径,导致某些查询无法有效利用索引 [^2]。 --- ### 相关问题 1. PostgreSQL 中如何优化模糊查询以提升索引使用效率? 2. 如何在 PostgreSQL 中创建合适的多字段复合索引? 3. PostgreSQL 查询优化器是如何决定是否使用索引的? 4. 在 PostgreSQL 中如何处理数据分布不均导致的索引失效问题? 5. PostgreSQL 中如何监控索引的使用情况?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值