当前位置: 首页 > 科技观察

为什么查询中不使用索引

时间:2023-03-14 09:33:55 科技观察

“为什么不使用索引”是一个广泛的问题。不使用索引的原因有很多。下面列出了一些非常有用的检查。请点击以下链接详细查看文章:快速检查表上是否存在索引?检查索引是否实际定义在您认为应该通过索引访问的表上。这些索引可能已被删除或创建失败——例如,一种可能的情况是在对表执行导入或加载操作后,由于软件或人为错误而未创建索引。以下语句可用于检查索引是否存在。SELECTindex_nameFROMuser_indexesWHEREtable_name=&Table_Name;是否应该使用索引?Oracle不一定仅仅因为索引存在就使用它们。如果一个查询需要检索这个表中的所有记录(比如表之间的join操作),为什么要访问索引中的所有数据和表中的所有数据呢?在这种情况下,只访问表数据会更快。对于所有查询,Oracle优化器根据统计信息计算包括索引在内的各种访问路径,并选择最佳路径。索引本身的问题单表(非join)查询的Where条件(谓词列表)中是索引列还是索引的谓词列?如果不是,至少索引的谓词列必须在查询谓词列表中,查询才能使用索引。(例外:请参阅下面的跳过扫描)。示例:在EMP.EMPNO列上定义单列索引EMPNO_I1,在EMP.EMPNO和EMP.DEPT列上定义联合索引EMPNO_DEPT_I2(EMP.EMPNO为索引的前列)。然后在优化器可以使用这两个索引之一之前,必须在查询谓词列表(where子句)中使用列EMP.EMPNO。SELECtename,sal,deptnoFROMempWHEREempno<100;例外情况:CBO可以使用索引快速全扫描(INDEX_FFS),只要索引包含查询所需的所有列,并且至少有一个索引列具有非空约束。执行INDEX_FFS不需要索引在列之前。请注意,INDEX_FFS不保证返回的行已排序。结果的顺序与读取索引块的顺序相同,仅当使用“orderby”子句确保结果有序时。CBO可以使用索引跳过扫描(INDEX_SS)。执行INDEX_SS不需要索引在列之前。CBO可以选择一个索引来避免排序,但是索引列必须存在于orderby子句中。连接谓词中是否使用了索引列?例如,下面的连接谓词定义了如何在表emp和dept的deptno列上进行连接:emp.deptno=dept.deptno如果索引列是连接谓词的一部分,那么查询时使用什么类型的连接被执行?哈希/排序合并连接(Hash/SortMergeJoin):对于哈希连接和排序合并,在执行连接时,还没有获取到外部表的信息,所以无法从内部表中检索行。它的处理方式是分别查询外部表和内部表后的结果合并。散列连接和排序合并的内部表不能仅通过连接的索引列访问。这是连接类型的执行机制的限制。嵌套循环连接的不同之处在于它们允许通过索引查询内部表的连接列。嵌套循环连接:嵌套循环连接读取外表,然后使用收集的信息访问内表。该算法允许对内部表进行基于索引的查询。只有嵌套循环连接(Nestedloopsjoin)允许索引只基于连接列在内部表中查找。另外,连接顺序是否允许使用索引?在内表上使用索引之前,必须先访问嵌套循环连接的外表。检查解释计划以确定使用了哪些访问路径。由于此限制,表的连接顺序很重要。示例:如果我们通过“emp.deptno=dept.deptno”连接EMP和DEPT,并且在EMP.DEPTNO上有一个索引,假设查询中没有其他与EMP.DEPTNO相关的谓词,则EMP在DEPT之前已访问,则在EMP.DEPTNO索引中没有可用于查询的值。在这个连接顺序中,要使用这个索引我们只能使用全索引扫描或者索引快速全扫描。在这种情况下,全表扫描(FTS)的成本可能会更小。IN或多个OR语句中的索引列?例如:emp.deptnoIN(10,23,34,....)或emp.deptno=10ORemp.deptno=23ORemp.deptno=34....在这种情况下,查询可能已转换为无法执行的语句使用索引。索引列是否被函数修改?索引不能用于由函数修改的列。基于函数的索引可以用来解决这个问题。什么是隐式类型转换?如果被比较的两个值的数据类型不同,Oracle必须对其中一个值进行类型转换,使其具有可比性。这称为隐式类型转换。当开发人员将数字存储在字符列中时,通常会出现此问题。Oracle将在运行时强制这些值之一,(由于固定规则)在索引字符列上使用to_number。由于向索引列添加了函数,因此未使用索引。实际上,这就是Oracle所能做的,类型转换是一个应用程序设计因素。这可能会导致性能问题,因为转换是在每一行上完成的。使用索引在语义上是不可能的吗?出于对查询整体成本的考虑,可能无法在低成本的执行计划中使用索引。在某个连接顺序和方法中可能已经考虑了一个索引,但是从“语义”的角度来看,成本最高的执行计划不能使用该索引。索引扫描类型错误?示例:快速全索引扫描而不是索引范围扫描这可能是优化器选择了所需的索引但使用了客户端未预期的扫描方法。在这种情况下,使用INDEX_FFS、INDEX_ASC和INDEX_DESC提示强制要求的扫描类型。我们还可以将索引的排序顺序定义为升序或降序。Oracle将降序索引视为基于函数的索引,因此具有与默认使用的升序不同的执行计划。通过查看执行计划,您看不到使用升序或降序,需要对视图DBA_IND_COLUMNS的“DESCEND”列进行额外检查。索引列是否可以为空?索引不存储NULL值,除非索引是复合索引(即多列索引),或者是位图索引。只有至少一个索引列有值时,联合索引才存储空值。联合索引中的尾随空值也存储在索引中。如果所有的列值都为null,则该行不会存储在索引中。由于索引中缺少NULL值,一些可能在结果中返回NULL值的操作(例如计数)可能被索引禁用。这是因为优化器在单独使用索引时不能保证准确的信息。关于使用NOTIN和NULL的一些其他注意事项。位图索引允许存储空值。因此优化器将使用这些索引,无论它们的结果是否可信。索引上的空值有时很有用,特别是对于某些类型的SQL语句,例如使用聚合函数COUNT的查询。例子:SELECTcount(*)FROMemp;NLS_SORT是否设置为二进制(BINARY)?如果NLS_SORT未设置为二进制,则不会使用索引。这是因为索引是根据Key值的二进制顺序建立的(pre-sortedusingbinaryvalues)。当NLS_SORT不是二进制时,无论优化器设置如何,都会使用全表扫描。更多关于使用NLS_SORT和索引的信息你在使用不可见的索引吗?从Oracle数据库11g第1版开始,您可以创建不可见索引或将??现有索引标记为不可见。优化器不会考虑不可见索引,除非参数OPTIMIZER_USE_INVISIBLE_INDEXES在会话或系统级别设置为TRUE。DML操作仍将维护这些不可见的索引。对于优化器和成本计算相关的问题,是否有准确和适当的统计信息(Statistics)?CBO依靠准确、准确和完整的统计信息来确定特定查询的最佳执行计划。如果使用CBO,请确保收集了统计信息。如果没有统计数据,CBO将使用预定义的统计数据,这很可能不会产生好的计划或应用程序来使用该索引。请注意,CBO将根据成本(COST)决定使用不同的索引。除了基本的表和索引信息外,如果某些列上的数据分布不均匀,那么还需要收集这些列的数据分布。通常,对象数据或结构的更改会使以前的统计信息不准确,因此应重新收集新的统计信息。例如,在表上加载大量数据后,需要收集新的统计信息。还建议在安装新补丁集后重新收集统计信息。表访问的最大作用是统计信息是在同一版本的数据库上生成的。一个指数是否与其他指数具有相同的等级或成本(cost)?对于成本(COST)相同的索引,CBO会使用各种方法来区分不同的索引,例如按字母顺序对索引名称进行排序,精确匹配索引扫描会选择更大的NDK(不同键值的个数)索引(不适用于快速全扫描)或选择叶块数较少的索引。请注意,这很少发生。索引是不是很有选择性?该索引不是很有选择性。使用它可能不是一个好的选择......列数据分布不均匀。CBO假定列数据不倾斜且分布均匀。如果不是这样,那么统计数据可能不能反映真实情况,即使有些值被高选,索引也会因为整列没有被高选而不适用于索引。如果是这种情况,则考虑使用直方图来记录更准确的列数据分布或使用提示。不准确的统计会导致索引显得无选择性,不被选择。可能的解决方法:收集更精确的统计数据。对于数据分布不均的列,可以考虑收集列统计信息在整体成本中,表扫描的成本占了大部分价值,这个操作比检索索引本身要昂贵得多。由于优化器是根据整体成本来计算执行计划的,如果通过索引检索表的成本很高,超过了一定的阈值,优化器就会考虑其他的访问路径。例如:SELECTempnoFROMempWHEREempno=5这条语句可能会使用基于列empno的索引,因为所有需要的数据都存储在索引中,所以不需要对表做额外的访问。相反:SELECTenameFROMempWHEREempno=5这个语句将需要外部访问表,因为ename字段没有存储在索引中。随着查询返回的记录数量的增加,检索ename的开销变得昂贵。如果使用索引,优化器使用“聚类因子”来确定需要对表进行多少次访问。访问空索引并不意味着它比访问有值索引更有效。重组、截断或删除操作不一定会影响SQL语句执行的成本。请注意,删除操作实际上并没有从对象中释放空间。也就是说,删除操作不会重置对象的高水位线。截断操作重置高水位线。空块的存在会使索引/表扫描比它们应该的更昂贵。删除和重建重新组织对象的结构,可能会有帮助(或坏处)。在比较两个不同系统对相同数据的查询性能时,通常更容易发现这类问题。参数设置某些参数的设置可能会影响索引的使用。比如大多数情况下建议使用DB_FILE_MULTIBLOCK_READ_COUNT和OPTIMIZER_INDEX_COST_ADJ的默认值。除非对某些操作有具体建议,否则使用其他值会使索引成本不切实际地降低或变大,并大大降低查询性能。其他问题:是否使用了视图/子查询?涉及视图或子查询的查询可能会被重写,以便不使用索引(尽管这种重写的目标之一是扩展更多访问路径)。这些重写一般都是合并操作。有没有远程表(remotetable)?通常远程表不使用索引。分布式查询中索引的使用取决于发送到远程的查询。CBO将评估远程访问的成本,并比较向远程站点发送或不发送索引谓词的成本。因此,CBO可以就远程表上使用的索引做出更明智的决策。一个非常有效的方法是创建一个包含相关谓词的远程视图并强制使用索引,然后在本地查询中使用这个视图。您在使用并行执行(PX)吗?索引的使用在并行执行中比在串行执行((serialexecution))中更为严格。检测这种情况的一种快速方法是禁用并行性并查看是否正在使用索引。它是包含子查询的Update语句吗?在某些情况下,基于成本考虑,不选择索引是因为它取决于子查询返回的值。在这种情况下,可以使用提示来强制使用索引。查询是否使用绑定变量?CBO无法为like或range谓词的绑定变量生成准确的成本。这可能会导致不选择索引。查询是否引用具有延迟约束的列?如果表中的某列包含延迟约束(例如NOTNULL),并且该列上有索引,那么无论该约束当前是延迟约束还是显式设置为Rightnow,我们都不会考虑在该列上使用索引柱子。示例:此现象记录在以下错误中,作为“不是错误”关闭:索引提示(提示)不起作用请使用表别名有用的提示: