探索索引的奥秘 - 有索引就一定会用么?

上一篇文章《探索索引的奥秘 - 索引的属性》,我们了解了索引的属性,回顾一下,

> 索引设置为unusable,会有以下特点,

    1. 索引设置为unusable,此时会删除索引段。

    2. 索引处于unusable期间,对表数据做DML操作,此时不维护索引。

    3. 索引处于unusable期间,优化器会忽略此索引。

    4. 索引处于unusable期间,由于不需要维护索引,因此可以提升批量导入性能。

    5. 索引unusable变为usable,有两种方法,一种是删除-重建索引,一种是使用alter index ... rebuild,两种方法,都相当于重新构建了索引。

> 索引设置为invisible,会有以下特点,

    1. 索引设置为invisible,不会删除索引段。

    2. 索引处于invisible期间,对表数据做DML操作,此时会维护索引。

    3. 索引处于invisible期间,优化器会忽略此索引。

    4. 索引invisible变为visible,直接使用alter index ... visible。
> unusable比invisible优先级要高,同时设置,起作用的是unusable。

> 只有函数索引可以设置disable和enable,涉及函数索引维护的操作,会被禁止,且执行计划,不会用这索引。

关于索引,还有一些存在模糊的知识点,这篇文章我们关注的是,是否有索引,就一定会用索引?

我们时常会碰见这种问题,

我们创建了索引,但为什么SQL未使用这个索引?

Oracle 10g之前默认的优化器模式,是RBO,数据的访问效率,会参考一些规则,说白了就是一些硬编码,定义了优先级,优先级高的,认为效率就高,例如索引就比全表扫描效率高,如下是优先级1-15的列表,最快的是基于ROWID的访问,最慢的则是全表扫描,

但Oracle 10g开始,优化器默认模式就是CBO了,C表示的就是Cost,即以成本为依据,结合对象的统计信息,谁的成本值低,谁的效率就高,相比RBO,这样更科学些,当然有些前提,例如统计信息要准确。

我们对一张表建立了索引,但并不代表SQL一定会用索引,究其原因可能有很多种情况,下面列举出两个场景,对于这样的问题,尝试提供一些思路和方法。

场景一:正确的有索引却不用



创建测试表,插入一条数据,创建索引,采集表和索引的统计信息,USER_TABLES视图显示有1条记录,平均行长为14字节。

执行update语句,条件是索引字段id,执行计划显示,对表的扫描,用全表扫描而不是索引扫描,

如果各位对索引的结构,比较了解的话,就比较容易理解其原因了,我们此处用的是BTree索引,即平衡二叉树索引,他的结构类似一棵树形,有根节点、分支节点,以及叶子结点,唯一索引和非唯一索引,叶子结点存储的信息会略有不同,我们此处建立的是非唯一索引,因此叶子结点中存储的,则是索引字段键值,以及对应的rowid,rowid是一个伪列,通过他可以快速定位,一条记录对应的物理位置,因为他的信息包括了,这条记录对应的文件号、块号、行号等信息,rowid的访问CBO时代他的优先级是最高的,关于rowid,内容其实还是很丰富的,有机会我们再聊。

再说索引结构,为什么说索引快,主要就是因为索引的查找,就是以这棵树的根节点开始,找分支节点,如果等值查询,则可以直接定位到具体的叶子结点,如果是范围查询,因为叶子结点是排序的,因此只要找出起始节点,按照叶子结点的指针,就可以找出对应结果集,无论何种用法,我们可以看出,他的执行路径都是有限的,根节点-分支节点-叶子结点,而且即使表的数据量再增加,只要索引数层级不变,其消耗的代价就是稳定的,而全表扫描,则会随着表数据量的增加,高水位不断上升,导致增加的成本消耗。

但一些情况下,索引扫描效率未必高,比如上面实验,因为要是SQL语句需要的数据,除了索引字段外,还有其他字段,则首先使用索引扫描,定位叶子结点,根据其中存储的rowid,回表找出对应的其他字段信息,而且若是INDEX RANGE SCAN这种索引范围扫描,会是单块读,而全表扫描则是多块读,相比之下,1次IO读的数据块数量就不同,对应的数据量就不同,效率就会不同,如果使用全表扫描,由于只有1条记录,则可以1次IO就完成数据读取。如果使用索引扫描,则先要消耗IO扫描索引,再回表消耗IO读取数据,成本高于全表。

虽然此处用了1条记录测试,有些极端,但即使有很多记录,还是需要综合考虑多块读、单块读、表的记录数、平均行长、回表等各种因素,只要TABLE ACCESS FULL的成本值低,无论是否有索引,都会选择TABLE ACCESS FULL。如果要用科学的数据,则可以做一个10053事件,就可以看出全表扫描和索引扫描两种方法对应的成本计算过程和结果,了解Oracle自己的选择。


场景二:错误的有索引却不用



我们接着插入10000条记录,但不执行统计信息更新,USER_TABLES视图显示表只有1条记录,可实际此时应该有10001条记录了。

我们执行comment表操作,让Oracle重新生成执行计划,但发现还是采用了全表扫描,

其实我们就可以看出问题,TABLE ACCESS FULL会扫描所有数据,但此处Rows值是1,说明Oracle认为表记录只有1条,自然TABLE ACCESS FULL是比较合适的选择,无可厚非。

接下来我们用一个11g推出的工具,STA(SQL Tuning Advisor),来看看此时Oracle可以给我们什么建议,首先创建任务,其中sql_id是我们执行update语句对应的sqlid,

接着执行report_tuning_task输出建议结果,请注意要是不设置开始的set,则可能结果显示为空,

内容如下,表示Oracle对这条SQL有两个建议,

第一个建议是,手工采集表和索引的统计信息,并且给出了SQL语句,

第二个建议,则是使用SQL Profile,固定执行计划,

并且给出了按照原始SQL,以及使用了SQL Profile的SQL,各执行10次的统计信息平均值数据,原始SQL用的TABLE ACCESS FULL,

使用SQL Profile的SQL,用的索引扫描INDEX RANGE SCAN,

可以看出,通过SQL Tuning Advisor,可以让Oracle来提供一些优化建议,并且直接给出了一些方法SQL,能辅助我们进行优化工作。

接下来针对实验问题,我们采用手工收集统计信息,再次执行,就会发现SQL用了索引范围扫描,相应地可以看10053事件,就会发现索引的成本,此时就会低于TABLE ACCESS FULL,


总结:

1. CBO时代,并不是有了索引,就一定会用索引,能不能用上,需要看谁的成本更低,影响成本值计算的因素很多,本文的问题,只有1条记录的时候,不用索引是对的,因为多块读的全表扫描,成本低于单块读的索引扫描(需要回表),但当有10001条记录的时候,不用索引就是错误的了,原因就是由于统计信息不准,造成Oracle计算成本值出现偏差,此时要么手工采集统计信息,要么使用SQL Profile固化执行计划,当然有索引但不用的场景,还有其他的因素,具体问题具体分析。

2. 像本文中,灌入大量数据,此时需要手工收集统计信息,才能保证Oracle估算成本值的正确,虽然Oracle有自动收集统计信息的job,但前提是要求这张表,当日的增删改数据量超过表总量的10%(参数可以调整),或者执行过truncate操作,可以参考dbsnake的书,而且每晚定时才能执行,因此之前这段时间其实是有可能使用了错误的执行计划,这就会有一些副作用。

3. SQL Tuning Advisor工具,可以让Oracle为我们优化SQL提出一些建议,自动化指出一些方向,还是比较有用的一种方法。

如果您觉得此篇文章对您有帮助,欢迎关注微信公众号:bisal的个人杂货铺,您的支持是对我最大的鼓励!共同学习,共同进步:)

时间: 2024-10-23 08:47:39

探索索引的奥秘 - 有索引就一定会用么?的相关文章

探索索引的奥秘 - 索引的属性

索引是一种奇特的对象,他就像一把双刃剑,用好了可以提高性能,用不好就可能会影响性能,但如何才能用好索引? 可能我们日常工作中,同事.朋友,甚至我自己会问这种问题, 我们创建了索引,为什么这条SQL未用这索引? 创建的索引越多,应用是不是就会越快? 只要SQL运行慢,一定是索引有问题? 应用是否用索引,是谁来决定的? 是否有了索引,应用就一定不会有性能问题? ... 其实这些问题,都蕴含着丰富的信息,就像学习Oracle一样,之所以说Oracle入门不难(例如写一些SQL语句),但要精通掌握就很难

PostgreSQL 索引虚拟列 - 表达式索引 - JOIN提速

标签 PostgreSQL , join , 表达式索引 , 虚拟列索引 , 静态数据 , immutable函数 背景 CASE: 使用虚拟索引,响应时间从2.3秒下降到0.3毫秒 业务系统在设计时,为了减少数据冗余,提升可读性,通常需要将不同的数据放到不同的表. 在查询时,通过多表JOIN来补齐需要查询或在过滤的内容. 比如这样的例子: 有两张表,分别有1千万和100万数据,当用户查询时,需要补齐那100万表中的某个字段进行过滤. create table a (id int, bid in

python-关于mysql中的位图索引和位片索引问题

问题描述 关于mysql中的位图索引和位片索引问题 老师给了一个作业要求利用其他语言例如Python或者C++来实现位图索引,但是一直教的就是mysql,没要办法建位图索引啊.不知道怎么办才好了,求大神指导... 解决方案 Mysql 索引 解决方案二: http://www.cnblogs.com/yuerdongni/p/4255395.html

Oracle B*tree索引和Oracle Bitmap索引有什么区别

(1) 建立B*tree索引 3:11:08 SQL>create index emp1_job_ind on emp1(job); (2)分析索引结构 3:11:08 SQL> ANALYZE INDEX EMP1_JOB_IND VALIDATE STRUCTURE; Index analyzed. (3)查看索引存储信息 03:11:41 SQL> SELECT BLEVEL,LEAF_BLOCKS,NUM_ROWS FROM USER_INDEXES 03:12:12   2  

聚集索引和非聚集索引

  聚集索引 一种索引,该索引中键值的逻辑顺序决定了表中相应行的物理顺序. 聚集索引确定表中数据的物理顺序.聚集索引类似于电话簿,后者按姓氏排列数据.由于聚集索引规定数据在表中的物理存储顺序,因此一个表只能包含一个聚集索引.但该索引可以包含多个列(组合索引),就像电话簿按姓氏和名字进行组织一样. 聚集索引对于那些经常要搜索范围值的列特别有效.使用聚集索引找到包含第一个值的行后,便可以确保包含后续索引值的行在物理相邻.例如,如果应用程序执行 的一个查询经常检索某一日期范围内的记录,则使用聚集索引可

mondodb-C#声明一个mongodb地理位置索引,并修改索引范围

问题描述 C#声明一个mongodb地理位置索引,并修改索引范围 默认情况下,mongodb地理空间索引假定你在索引经度/维度,并且这些值的范围是[-180180].但是我的坐标是大地坐标,不是经纬度,怎么在C#声明一个mongodb地理位置索引,并修改索引范围?shell是这样修改的:db.places.ensureIndex( { loc : ""2d"" } { min : -500 max : 500 } ) 解决方案 这里有现成的http://blog.c

SQLSERVER聚集索引与非聚集索引的再次研究(上)

原文:SQLSERVER聚集索引与非聚集索引的再次研究(上) SQLSERVER聚集索引与非聚集索引的再次研究(上) 上篇主要说聚集索引 下篇的地址:SQLSERVER聚集索引与非聚集索引的再次研究(下) 由于本人还是SQLSERVER菜鸟一枚,加上一些实验的逻辑严谨性, 单写<SQLSERVER聚集索引与非聚集索引的再次研究(上)>就用了12个小时,两篇文章加起来最起码写了20个小时, 本人非常非常用心的努力完成这两篇文章,希望各位看官给点意见o(∩_∩)o   为了搞清楚索引内部工作原理和

SqlServer(索引)--创建复合索引时,复合索引列顺序对查询的性能影响[转]

http://www.cnblogs.com/wy123/p/5604400.html SQL Server创建复合索引时,复合索引列顺序对查询的性能影响 说说复合索引 写索引的博客太多了,一直不想动手写,有一下两个原因: 一是觉得有炒剩饭的嫌疑,有兄弟曾说:索引吗,只要在查询条件上建索引就行了,真的可以这么暴力吗? 二来觉得,索引是个非常大的话题,很难概括出所有的情况,你不整出点新意来,倒是有抄袭照搬的嫌疑 既然写了,就写一点稍微不一样的东西出来, 好了,废话打住, /* 20160814备注

MySQL 查询索引的选择性、索引字段、注释等基本信息的SQL

链接:http://blog.itpub.net/28602568/viewspace-1819474/ 标题: MySQL 查询索引的选择性.索引字段.注释等基本信息的SQL 作者:lōττéry版权所有[文章允许转载,但必须以链接方式注明源地址,否则追究法律责任.] 如下sql 用于统计mysql数据库非系统db的全部表/索引信息  (包括:数据库.表名.表注释.表行数.表大小.索引名.索引字段.字段注释.基数.选择性比.索引类型..) SQL:      SELECT t.table_sc