首页 > 其他分享 >执行计划不走索引的几种情况总结

执行计划不走索引的几种情况总结

时间:2024-04-01 15:48:04浏览次数:25  
标签:总结 rows 扫描 id 索引 test 几种 col

优化器不想用索引,主要原因是优化器认为走索引还不如走顺序扫描代价低,因为索引扫描对应的是离散IO,我们可以通过调整random_page_cost告诉优化器随机IO代价值,非特殊情况不建议修改此值。

1.表太小场景

经常有开发问,为什么有索引而不走索引呢?因为优化器认为走索引方式太慢了!

test=# create table sma(id int);
CREATE TABLE
test=# create index on sma (id);
CREATE INDEX
test=# insert into sma values(generate_series(1,100));
INSERT 0 100
test=# analyze sma;
ANALYZE
test=# explain select id from sma where id = 10;
                      QUERY PLAN                       
-------------------------------------------------------
 Seq Scan on sma  (cost=0.00..2.25 rows=1 width=4)
   Filter: (id = 10)
(2 rows)

test=# set enable_seqscan to off;
set
test=# explain select id from sma where id = 10;
                                    QUERY PLAN                                     
-----------------------------------------------------------------------------------
 Index Only Scan using sma_id_idx on sma  (cost=0.14..8.12 rows=1 width=4)
   Index Cond: (id = 10)
(2 rows)

可以看到索引的总成本8.12要比顺序扫描2.25高了4倍左右,所以优化器选择走顺序扫描了。
为什么小表场景中,优化器可能选择顺序扫描而不是索引扫描,因为服务器进程可能在1个数据块里就获得了所需数据,相比索引扫描,在索引块的叶子节点获得对应数据块的指针后还需要去表里去扫描取得行记录,这样扫描的总块数,或者说IO次数就会多余顺序扫描所需的块数。

2.关联度

高效的索引扫描,只需扫描几次就能获取到所需数据,而糟糕的索引扫描往往需要返回大量数据,需要多次扫描,每次都要经历根节点 → 分支节点 → 叶子节点,索引的level越高,需要耗费的IO越大,导致大量的离散IO,因此优化器也会将这个关联度考虑进去。
高效的顺序扫描数据更加紧凑,数据块都集中在一起,这样IO的效率就提高了,只需很少的IO,就可以获取到大部分数据。
而糟糕的顺序扫描则数据相对离散,需要扫描大量数据并过滤才能获取到想要的数据,个就是我在统计信息里面提到的correlation字段的作用,表示列的物理顺序和逻辑顺序的相关性,相关性越高,走索引扫描的离散块扫描更少,代价越低。

例如:

test=# \d tbl
    Table "public.tbl"
  Column  |  Type   | Modifiers 
----------+---------+-----------
 col      | text    | 
 col_asc  | integer | 
 col_desc | integer | 
 col_rand | integer | 
 data     | text    |
Indexes:
    "tbl_asc_idx" btree (col_asc)
    "tbl_desc_idx" btree (col_desc)
    "tbl_rand_idx" btree (col_rand)

test=# SELECT col,col_asc,col_desc,col_rand FROM tbl;
   col    | col_asc | col_desc | col_rand 
----------+---------+----------+----------
 Tuple_1  |       1 |       12 |        3
 Tuple_2  |       2 |       11 |        8
 Tuple_3  |       3 |       10 |        5
 Tuple_4  |       4 |        9 |        9
 Tuple_5  |       5 |        8 |        7
 Tuple_6  |       6 |        7 |        2
 Tuple_7  |       7 |        6 |       10
 Tuple_8  |       8 |        5 |       11
 Tuple_9  |       9 |        4 |        4
 Tuple_10 |      10 |        3 |        1
 Tuple_11 |      11 |        2 |       12
 Tuple_12 |      12 |        1 |        6
(12 rows)

test=# SELECT tablename,attname, correlation FROM sys_stats WHERE tablename = 'tbl';
 tablename | attname  | correlation 
-----------+----------+-------------
 tbl       | col_asc  |      1
 tbl       | col_desc |     -1
 tbl       | col_rand | 0.1258
(3 rows)

表有三个索引,其中
col_asc索引是顺序递增的
col_desc索引是倒序递减的
col_rand索引则是随机分布的
因此如果SQL需要查询2到4的数据
SELECT * FROM tbl WHERE col_asc BETWEEN 2 AND 4;,对于col_asc,只需要读第一个页面即可,因为数据排序是紧凑连续的。

而假如是对随机列进行查询的话,SELECT * FROM tbl WHERE col_rand BETWEEN 2 AND 4;则需要读取所有的页面,因为数据分布是离散的,它们可能存在不同的数据块。这时优化器就会评估顺序扫描和索引扫描的代价,最终选择代价更低的执行计划。

因此在索引扫描的时候也要将correlation考虑进去。
在Kingbase数据库中,我们可以使用cluster命令进行聚簇,对于某些时序类的数据进行范围查询,会有性能提升作用,因为索引排序和数据分布都是连续的。

3.返回的结果集过大
索引会对应大量的离散IO,我想说一下选择率这个词,如果大部分数据都需要查询到,那么索引的作用就微乎其微了,这时候再用索引扫描代价明显高于顺序扫描。
如果一个索引能够在查询中使用索引扫描中过滤掉大部分的行,则这个索引有较好的选择率,选择率更低,这时候索引更高效。反之,选择率更高,使用索引的效率不高,那么优化器就不会使用索引。

test=# create table big_t(id int);
CREATE TABLE
test=# create index on big_t (id);
CREATE INDEX
test=# insert into big_t values(generate_series(1,100000));
INSERT 0 100000
test=# analyze big_t ;
ANALYZE
test=# explain select id from big_t where id > 1;
                                      QUERY PLAN                                      
--------------------------------------------------------------------------------------
 Index Only Scan using big_t_id_idx on big_t  (cost=0.29..3304.28 rows=99999 width=4)
   Index Cond: (id > 1)
(2 rows)

test=# set enable_seqscan to on;
SET
test=# explain select id from big_t where id > 1;
                         QUERY PLAN                         
------------------------------------------------------------
 Seq Scan on big_t  (cost=0.00..1693.00 rows=99999 width=4)
   Filter: (id > 1)
(2 rows)

这个例子说明,当获得大量结果集时候,选择率更高,这时候索引更低效,则优化器优先选择全表扫描。

4.最左原则

test=# create table t(id int,info text);
CREATE TABLE
test=# create index on t(id,info);
CREATE INDEX
test=# insert into t select n,md5(random()::text) from generate_series(1,100000) as n;
INSERT 0 100000
test=# analyze t;
ANALYZE
test=# explain select id,info from t where id = 1 and info = 'hello';
                                 QUERY PLAN                                  
-----------------------------------------------------------------------------
 Index Only Scan using t_id_info_idx on t  (cost=0.42..8.44 rows=1 width=37)
   Index Cond: ((id = 1) AND (info = 'hello'::text))
(2 rows)

test=# explain select id,info from t where info = 'hello';
                     QUERY PLAN                      
-----------------------------------------------------
 Seq Scan on t  (cost=0.00..2084.00 rows=1 width=37)
   Filter: (info = 'hello'::text)
(2 rows)
可以看到,我创建了一个复合索引,假设是(a,b,c)的复合索引,那么索引会先按照a列排序存储,接着按照b列排序,最后是c列,假如SQL直接查询的b或者c,意味着基可能需要访问整棵索引树,这样代价太高了。
test=# set enable_seqscan to off;
SET
强制使用索引扫描,成本要比上面的顺序扫描高,所以对于查询条件中跳过联合索引的索引前导列的情况,优化器默认使用了顺序扫描。
test=# explain select id,info from t where info = 'hello';  
                                   QUERY PLAN                                   
--------------------------------------------------------------------------------
 Index Only Scan using t_id_info_idx on t  (cost=0.42..3630.43 rows=1 width=37)
   Index Cond: (info = 'hello'::text)
(2 rows)

这里运用逆向思维,我们可以从执行计划的cost成本估算中理解优化器选择的检索数据方式,然后结合联合索引的排列原理,理解优化器的估算方式。

5.查询条件模糊

如果查询条件使用了不等于(<>)、LIKE等运算符或者使用了函数等,那么索引可能无法被使用。
因为正常情况下,等于(=)操作符可以直接利用B-tree或哈希索引进行查找。这是因为这些操作符可以在索引结构中找到对应的记录选项。
而不等于(<>)操作符则需要查找所有不符合条件的记录,这会导致需要遍历整个索引树来找到匹配的记录,带来的结果是使用索引的成本比全表扫描成本更高。
LIKE操作符也可能导致无法使用索引。例如’%abc’,则索引将不会被使用,如果通配符是abc%,则可以使用索引。原理同上。

6.糟糕的统计信息

过期的,糟糕的统计信息会让优化器产生误判,可能由于某种原因导致 autovacuum进程没有及时收集统计信息,这时需要做analyze手动收集统计信息,或者修改字段长度、数据类型、dml大量数据变更,也需要手工收集统计信息,否则选择率可能存在误差导致优化器估算不准。

标签:总结,rows,扫描,id,索引,test,几种,col
From: https://www.cnblogs.com/kingbase/p/17736774.html

相关文章

  • Django项目windows部署教程,详细踩坑总结
    Django项目windows部署教程,详细踩坑总结本篇文章主要关于Django在Windows上利用Apache部署,如果你想通过IIS部署,推荐这篇文章在IIS服务器上部署django_djangoiis-CSDN博客,我之前尝试部署在IIS上,发现Django的定时任务失效了,上网查找了一番,普遍说法是被服务器回收了,经过了一番操作,......
  • 关于openGauss中的虚拟索引
    关于openGauss中的虚拟索引作为曾经的Oracle资深使用者,对于Oracle11gR2版本推出的invisibleIndex感觉一直很良好;因为这对于大部分情况下做优化是比较友好的。实际上openGauss2.0版本中也提供了类似的功能,下面我们来进行简单测试。首先我们创建一个测试表用来验证ope......
  • 熬夜梳理!Python 技术大佬总结了53个使用技巧!
    大家好,今天给大家分享老司机总结的一些干货技巧,非常有价值,尤其是对Python编程基础比较薄弱的小伙伴,记得收藏哦,欢迎关注、点赞。交流群建立Python交流群,想要进技术交流群的同学,可以直接加微信号:dkl88191。加的时候备注一下:研究方向+学校/公司+知乎,即可。然后就可以拉你......
  • 什么是Docker引擎架构,Docker引擎架构详解及Vmware,CentOS7、Docker引擎的安装,CentOS7常
    Dockere引擎架构详解2.1Docker引擎发展历程2.1.1首发版本架构       Docker在首次发布时,其引擎有两个核心组件构成,LXC(LinuxContainer)与DockerDaemon构成。不过该架构依赖于LXC,使得Docker存在严重的问题:依赖于外部工具对Docker来说存在着巨大的生存风险。......
  • NumPy库中的ndarray对象——ndarray的切片和索引
    一、普通切片与索引ndarray对象的内容可以通过索引或切片来访问和修改,与Python中list的切片操作一样。ndarray数组可以基于0-n的下标进行索引,切片对象可以通过内置的slice函数,并设置start,stop及step参数进行,从原数组中切割出一个新数组。我们来介绍一下sta......
  • FIFO存储器选型参数,结构原理,工艺与注意问题总结
      ......
  • 腾讯2024技术研究岗-实习生笔试总结
    腾讯2024技术研究岗-实习笔试在牛客上考的,5道编程题,可以用本地IDE,但不能使用已有代码前四题都比较简单,枚举、贪心、二分、最短路的考点,大概40分钟做完,第五题卡住了,到最后也没做出来第五题是一道数学题,大体的题意忘记了,但是最后我的做法简化出来的难点应该在处理大质数\(p\)(\(1......
  • MySQL的索引
    索引的创建是非常重要的一环。索引可以显著提高查询性能,但也可能增加写入操作的开销,因此需要根据具体的应用场景和需求来权衡和设计。 索引类型 MySQL支持多种类型的索引,包括: B-TREE索引:最常见的索引类型,大多数存储引擎都支持它。HASH索引:只有MEMORY存储引擎支持。F......
  • 泛型总结(擦除机制+泛型上界+通配符的上下界)
    文章目录泛型一、什么是泛型1.能用于多种类型,把类型当做参数1.1作用1.2语法二、擦除机制1.为什么采用擦除机制实现泛型?向后兼容性移植兼容性2.为什么不能使用“newT()”?3.创建类型T的数组3.1不安全的写法3.2官方的写法3.3正确的写法4.反编译后,对比方法......
  • MySQL面试必备一之索引
    本文首发于公众号:Hunter后端原文链接:MySQL面试必备一之索引在面试过程中,会有一些关于MySQL索引相关的问题,以下总结了一些:MySQL的数据存储使用的是什么索引结构B+树的结构是什么样子什么是复合索引、聚簇索引、覆盖索引什么是最左匹配原则数据B+树中是如何查询的......