




已阅读5页,还剩14页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Document TitleSecurity Level: 大数据量下索引的应用作者:徐铭(54819) 蔡小华(00101632)2009-11-28华为技术有限公司2020-02-01HUAWEI ConfidentialPage1, Total19文档名称文档密级: 目 录一引言3二怎样使用索引才高效4三什么时候使用复合索引7四什么时候使用函数索引9五如何使用分区索引10六如何让优化器选择指定的索引12七哪些情况下无法使用索引18八总结18大数据量下索引的应用摘要:几乎所有的应用都会用到数据库,都需要从数据库中查询数据。而电信行业的数据库往往都是海量数据,从这种数据库中直接查询数据所等待的时间对于实时性要求很高的系统是不可接受的,而通过索引查询数据可以极大地缩短查询时间,满足实时性系统的要求,所以,我们需要为大数据量的表创建索引,索引是提高数据查询最有效的方法,也是最难全面掌握的技术,正确的索引可能使效率提高上万倍,而无效的索引可能浪费数据库空间,甚至大大降低查询性能。本文基于Oracle数据库,首先总结出高效使用B树索引的一般规律,然后分析了几种特殊形式索引的特性,并且分别给出了使用建议,最后探讨了优化器选择索引的常用方法。关键字:数据库,Oracle,索引,性能,优化,数据查询一引言Oracle数据库是当前主流的数据库之一,而B树索引是最常用的索引。B树索引有分支和叶子两种类型的存储数据块,所有叶子块都应该在同一层上,同时叶子节点都是双向链表,这样在进行索引区间扫描(index range scan)时,只需通过叶子节点的向前或者向后就可以了,无需再对索引结构进行导航。大多数情况下,B树索引的高度是2或者3,一般情况下,在索引中找到一个键只需要2或3次I/O。虽然索引可以极大的提高查询效率,但由于使用索引需要一定的成本,不仅需要占用很多的磁盘空间,而且由于需要自动维护,会增加执行DML操作的成本,所以索引也不是创建的越多越好。一般而言,如果需要从一个大数据量的表中访问很小比例的行,那么就应该在这个表上建立索引。而下面几种情况下应不建或少建索引:l 表的记录数太少。l 经常插入、删除、修改的表。l 数据重复且分布平均的表字段。l 经常和主字段一块查询且主字段的选择率(列上不同取值的个数与数据总数的比值)非常高的表字段。 上面列出的是建立索引的一般规律,但是当面对具体的查询语句时,仅仅这些是远远不够的。只有深刻理解了索引的原理,掌握其内部的规律,才能在面对错综复杂的SQL语句时灵活高效的使用索引。 本文重点阐述如何高效的使用B树索引,包括普通索引、复合索引、函数索引以及分区索引,最后介绍优化器是如何选择索引的以及哪些情况下无法使用索引。二怎样使用索引才高效经常有人问这样的问题:使用这个索引好不好?使用哪个索引好?我们应该根据什么原则来判定一个查询语句是否应该使用索引以及使用哪个索引?对于初学者来说,确实较难把握。为了弄清楚这些问题,我们要先搞清楚索引访问的原理。简单的说索引扫描分为两步:(1)扫描索引得到对应数据的地址(rowid)。 (2) 通过rowid从表中读出具体的数据。下面我们就来详细的探讨如何选择索引。1如何衡量查询语句的性能一个查询耗费的资源分成3个基本组成部分:I/O代价、CPU代价、network代价。I/O代价是将数据从磁盘读入内存所需的代价,一般情况下,该代价是处理一个查询的主要耗费,因此我们衡量查询语句性能时一般忽略其他次要因素,只比较查询所产生的I/O总次数。如果要访问的数据块已经在内存中,就不需要物理I/O,而只做一次逻辑I/O,这部分代价与物理I/O相比要小很多,所以也可以忽略。2全表扫描和通过索引来访问表数据的对比首先需要明确全表扫描支持多块读,即一次I/O可以读取多个数据块(由db_file_multiblock_read_count参数设定一次读取的块数),而索引扫描的每步都是单独的一次I/O,不过由于经常使用,绝大多数索引块都已经CACHE到内存中,所以第1步的I/O经常是逻辑I/O,即数据可以从内存中得到。为了讨论方便,我们做如下假设:l 每次索引扫描需要两次物理I/O,一次对索引块,一次对数据块l 所有数据块在查询开始时都没有被读入内存中l 多块读的参数设定为20l 表的数据量为1000万,每个数据块存储100条数据,即表中的数据块为10万个基于以上条件,作如下查询:(1)通过唯一索引的等值查询来访问表数据Select * from tableA where id = -1假设tableA表中不存在id为-1的数据。如果做全表扫描的话,所需要的I/O次数为10万/20,即5000次。而通过id列的索引扫描,由于没有符合条件的数据,所以只需要1次扫描索引块的I/O,全表扫描和索引扫描两者的效率比是5000:1,此时使用索引扫描远远优于全表扫描。(2)通过唯一索引的范围查询来访问表数据Select * from tableA where id -1假定id -1将查询出所有的数据。如果做全表扫描的话,仍然需要5000次I/O。而通过索引扫描访问一条数据需要2次I/O。由于需要多次读取同一个数据块,而内存中会缓存一定的数据块,所以大部分的I/O是逻辑I/O。假定索引块和数据块在内存中的命中率(逻辑I/O占I/O总数的比例)为90%,那么需要的I/O次数为1000万*2*0.9,即1800万次。全表扫描和索引扫描两者的效率比是1:3600。此时全表扫描远远优于索引扫描。通过上面两种极端情况的对比,可以看出通过索引查询数据并不总是可以加快查询,当取出的数据大于总量的一定比例时(一般为10%),使用索引扫描的效率会较差。从另一角度讲,一次索引扫描的效率相对于直接访问表数据是低很多的。因为一次索引扫描只能从表中读出一条数据,一般需要1至2次的I/O,可以说索引扫描的一次I/O只能读取一条数据;而全表扫描的一次I/O可以读取20个左右的数据块,如果每个数据块中有100条数据,那么一次I/O就可以读出2000条数据。可见二者读取数据的效率有多大的差距。通过索引扫描访问的数据比例越大,查询效率也就越低。下面通过一个实际的查询来说明某些情况下全表扫描远远优于索引扫描的(使用执行计划的COST来粗略比较查询的时间长短)。如下这个SQL将查询出表中的所有数据。如果使用全表扫描,COST为15911,而通过索引扫描,COST为3968907。索引扫描的代价比全表扫描大了200多倍,在这种情况就不应该通过phonenumber列上的索引来访问表中的数据。SQL select /*+full(a)*/a.passwd from t_userinfo a 2 where phonenumber =and phonenumber select /*+index(a pk_t_userinfo)*/a.passwd from t_userinfo a 2 where phonenumber =and phonenumber 0可以查出上百万的数据。显然应该使用phonenumber上的索引,而不能使用resourceid上的索引。从执行计划可以看出来,两者的COST相差了几十万倍。SQL select /*+index(a pk_t_personallib)*/* from t_personallib a 2 where phonenumber =and resourceid 0;Execution Plan- 0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=4 Card=2 Bytes=192 ) 1 0 TABLE ACCESS (BY INDEX ROWID) OF T_PERSONALLIB (TABLE) ( Cost=4 Card=2 Bytes=192) 2 1 INDEX (RANGE SCAN) OF PK_T_PERSONALLIB (INDEX (UNIQUE) ) (Cost=3 Card=2)SQL select /*+index(a ix_personallib_resid)*/* from t_personallib a 2 where phonenumber =and resourceid 0;Execution Plan- 0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=987456 Card=2 Byte s=192) 1 0 TABLE ACCESS (BY INDEX ROWID) OF T_PERSONALLIB (TABLE) ( Cost=987456 Card=2 Bytes=192) 2 1 INDEX (RANGE SCAN) OF IX_PERSONALLIB_RESID (INDEX) (Co st=2756 Card=1236594)因此,在面对具体的查询SQL语句时,应该根据查询数据量占总数据量的比率选择全表扫描还是索引扫描,根据具体的过滤条件选择使用哪个索引。三什么时候使用复合索引复合索引是建立在多个列上的索引,如create index idx_emp on emp(col1, col2, col3, ), 索引idx_emp就是复合索引。在复合索引中有一个重要的概念:引导列(leading column),在上面的例子中,列col1为引导列,并且当限制条件中包含引导列时,该限制条件才会使用到该复合索引。即我们进行查询时使用”where col1 = ? ”,或使用”where col1 = ? and col2 = ?”,都会使用复合索引,但”where col2 = ? ”就不会使用该索引。在什么情况下不适合建立复合索引。如果通过一个字段上的索引扫描就过滤为很少的数据(100条以内),就没有必要建立复合索引。虽然把各个查询条件的字段都建立在一个复合索引中可能会使查询更快,但建立复合索引的成本却要高很多,它对DML语句的影响更大,同时也要占用更多的磁盘空间,因此这点查询速度的变化对于成本的耗费就显得微乎其微。下面通过一个实际的测试来说明这个问题。查询id1为10,id2为200的数据,共有20条数据符合条件的。其中,id1为10的数据有100条,id2为200的数据有100000条。SQL select count(*) from t_test where id1 = 10; COUNT(*)- 100SQL select count(*) from t_test where id2 = 200; COUNT(*)-100000SQL select count(*) from t_test where id2 = 200 and id1 = 10;COUNT(*)- 20SQL select /*+index(a ix_id1)*/count(name) from t_test a where id2 = 200 and id1 = 10;Execution Plan- 0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=6 Card=1 Bytes=2007) 1 0 SORT (AGGREGATE) 2 1 TABLE ACCESS (BY INDEX ROWID) OF T_TEST (TABLE) (Cost= 6 Card=17 Bytes=34119) 3 2 INDEX (RANGE SCAN) OF IX_ID1 (INDEX) (Cost=4 Card=735)SQL select /*+index(a ix_id1_id2)*/count(name) from t_test a where id2 = 200 and id1 = 10;Execution Plan- 0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=4 Card=1 Bytes=2007) 1 0 SORT (AGGREGATE) 2 1 TABLE ACCESS (BY INDEX ROWID) OF T_TEST (TABLE) (Cost= 4 Card=17 Bytes=34119) 3 2 INDEX (RANGE SCAN) OF IX_ID1_ID2 (INDEX) (Cost=3 Card=18)这个查询通过使用id1上的索引就已经能满足要求。从COST值上比较,虽然走复合索引得到的COST为4,小于使用id1上索引的代价6,但是微小到几乎可以完全忽略。如果通过id1和id2的索引扫描都不能得到很小的结果集,而同时应用两个限制条件后可以得到非常小的结果集(100条以内),此时使用id1和id2的复合索引就非常合适。而id1和id2在复合索引中的顺序要根据列的唯一值概率决定,即索引扫描得到结果集较小的列应当安排在复合索引的前面。四什么时候使用函数索引函数索引与普通索引的区别是它使用列上的函数值作为key来建立索引,而不是直接使用列的值作为key。B树索引有一个特性,当SQL语句的查询条件中,列上有表达式时,列上的普通索引就无法使用,例如”where upper(tonename) = ?”,此时无法直接使用tonename上的索引。这时只有建立upper(tonename)的函数索引才能在查询中使用tonename的索引,如下:create index ix_fulltonelib_fname on t_fulltonelib (lower(tonename)函数索引只能通过提示在CBO优化器(基于成本的优化器)中使用,否则优化器是不会选择该索引的。函数索引相对于普通索引而言,查询效率较差,但有时我们会主动构造一些函数来使用:如果列的某一取值占数据总数的比例非常小,并且查询时使用该取值作为过滤条件,则适合在该列的该值上建立函数索引(B树索引有一个特性:如果key值为空,在索引中就没有相应的条目)。例如某表上的status列,它有1和2两种取值,其中99%的数据都是1,只有1%的数据取值为2。查询该表时如果查询1状态的数据肯定会使用其他列上的索引,例如电话号码(此时不会也不能使用status的索引)。而查询status为2的数据时没有其他条件,此时就需要使用status上的索引。如果在status列上建立一个普通索引,那么就有以下两个弊端:l 由于status为1的数据不会使用该索引,所以绝大部分的索引空间是浪费的,而维护这些索引的开销也是多余的。l 如果开发人员缺乏SQL优化的经验或是执行计划不够稳定(使用CBO优化器时,如果缺少统计信息,可能出现执行计划不稳定),有可能使查询status为1的语句使用了status上的索引,此时查询的时间会非常长。如果我们把查询status为2的语句改成”where decode(status, 2, 2, null) = 2”,然后以”decode(status, 2, 2, null) ”建立函数索引,就可以很好的解决上面的问题。由于status为2的比例非常小,因此该索引占用的空间较少,维护成本也相应小很多。同时由于要将查询条件写成decode的形式并且需要使用提示指定该函数索引,所以在查询status为1的数据不会因为执行计划不稳定而误用了status上的索引。五如何使用分区索引数据库表分区是一种对表的横向分割,它对用户的访问是透明的。自Oracle8i起,可以把一个表中的所有行分为几个部分,这种按照行为划分表数据的方式称为对表分区,被分区的表称为分区表,分成的每一部分为一个分区。当一张表预计数据量超过1000万时,为更方便地维护好数据,需要考虑是否应该创建分区表。分区功能可以将表、索引或索引组织表进一步细分为段。这些数据库对象的段叫做分区。每个分区有自己的名称,还可以选择自己的存储特性。从数据库管理员的角度来看,一个分区后的对象具有多个段,这些段既可进行集体管理,也可单独管理。这就使数据库管理员在管理对象时有相当大的灵活性。同时,从应用程序的角度来看,分区后的表与非分区表完全相同,一般无需修改SQL语句来访问分区后的表。如果需要在分区表上建立索引,并且索引也按照表分区时所选值的范围进行了分区(该索引必须是local本地索引)。例如:create index ix_l_userlog_phone on t_userlog ( phonenumber ASC) localtablespace ringidx/对于查询分区表的语句需要小心,如果在条件语句中没有包含分区列的条件,会导致SQL分区器选择分区全扫描。例如:用户下载日志表总数为4000万条记录,以下载时间作为表的分区列。如果仅通过用户号码来查询下载日志,从执行计划分析来看,优化器首先选择号码全分区索引扫描,然后再分区范围扫描。虽然优化器选择了索引,但优化器还是扫描了该表所有分区的数据,导致执行该SQL语句需要30秒左右。SQL select count(*) from t_personaldownreport t where t.phonenumber =Execution Plan- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1 Bytes=14) 1 0 SORT (AGGREGATE) 2 1 PARTITION RANGE (ALL) 3 2 PARTITION HASH (ALL) 4 3 INDEX (FAST FULL SCAN) OF IX_L_PERSONALDOWNRPT_PHON ENUM (NON-UNIQUE) (Cost=4 Card=4003 Bytes=56042)实际在OLTP应用系统中,一般查询是以表分区列作为条件来查询某用户在某时间范围内下载历史记录。优化器选择分区范围扫描,从而减少查询成本。下面语句的查询条件包含分区列的条件,该语句在现网环境中执行只需要0.5秒。SQL select count(*) from t_personaldownreport t where t.phonenumber =and t.downdate = trunc(sysdate, mm)-300;Execution Plan- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=51 Card=1 Bytes=23) 1 0 SORT (AGGREGATE) 2 1 PARTITION RANGE (ITERATOR) 3 2 PARTITION HASH (ALL) 4 3 TABLE ACCESS (BY LOCAL INDEX ROWID) OF T_PERSONALDO WNREPORT (Cost=51 Card=200 Bytes=4600) 5 4 INDEX (RANGE SCAN) OF IX_L_PERSONALDOWNRPT_PHONEN UM (NON-UNIQUE) (Cost=50 Card=1601)创建分区索引需要关注local关键字。在create index 命令中并没有指定如何分区,而由local关键字告诉Oralce根据表的分区方式为每一个分区建立一个独立的索引。由于每一个分区有一个索引,因此这些索引对于分区表来说是“局部”的。当然也可以建立“全局”索引。全局索引包含多个分区的值,只要把local关键字去掉或替换为global。局部索引比全局索引更容易管理,但是,全局索引进行唯一性检查的速度更快。六如何让优化器选择指定的索引首先介绍一下CBO的结构,CBO是基于成本的优化器,它根据可用的访问路径、对象的统计信息、嵌入的提示来选择一个成本最低的执行计划。CBO主要包含以下组件:l 查询转换器(Query Transformer)l 评估器(Estimator)l 计划生成器(Plan Generator)如下图所示:查询转换器评估器计划生成器数据字典执行计划查询语句+评估值统计信息分析过的查询转换过的查询图1计划生成器的作用就是生成大量的执行计划,然后选择其中总体成本最低的一个。由于不同的访问路径、连接方式和连接顺序可以任意组合生成不同的执行计划,因此一个SQL可能存在大量不同的执行计划。理论上,计划生成器应该试验所有的组合,分别统计成本,然后选择成本最低的;但实际上计划生成器很少会试验所有可能存在的执行计划,如果它发现当前执行计划的成本已经很低了,它将停止试验,相反当前计划的成本如果很高,它才继续试验其他执行计划,因此如果能使计划生成器一开始就找到成本很低的执行计划,则会大量减少试验所消耗的时间以及这段时间中查询性能的消耗,这也正是我们用提示来优化SQL的原因之一。本章以下部分主要是讨论数据库里检索数据的方式。1具有弱选择性的SQL语句数据库引擎不仅能通过索引抽取rowid列表,并将其作为指针读取表中的相应行,也可以直接读取组成索引键的列的值,避免同时访问rowid和表。当索引包含查询所需要的所有数据的时候,全表扫描或全分区扫描应当用全索引扫描(full index scan) 替换。同时由于索引数据段通常比表数据段小,这对减少逻辑I/O很有用的。例如,测试表n1列被索引,如下查询就可以利用全索引扫描。通过查看执行计划可以确认该查询不需要通过rowid来访问表的数据。 SQL select /*+index(t ix_t_n1)*/ n1 from t where n1 is not null; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=26 Card=4 Bytes=408) 1 0 INDEX (FULL SCAN) OF IX_T_N1 (NON-UNIQUE) (Cost=26 Card=4 Bytes=408) 对于index full scan 操作,可以通过指定前面例子中所用的index提示强制执行,根据其结构访问索引。其优点是获取数据是按照索引键排序的,缺点是如果索引块在高速缓存没找到,就要从数据文件中进行单块读取。因为全索引扫描要读取大量的数据,所以通常是低效的。要改善性能,可以使用快速全索引扫描( index fast full scan ),如下所示: SQL select /*+index_ffs(t ix_t_n1)*/ n1 from t where n1 is not null; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=4 Bytes=408) 1 0 INDEX (FAST FULL SCAN) OF IX_T_N1 (NON-UNIQUE) (Cost=4 Card=4 Bytes=408) 使用提示index_ffs可以强制执行index fast full scan。这个操作的特性是如果在高速缓存中没有找到索引块,可以从数据库文件中进行多块读取,就像对表的全表扫描一样。在这样的扫描中,不会访问索引结构,因而获取的数据是没有根据索引键排序的。如果查询需要按索引键排序,为了避免排序,我们只能使用index full scan,而不能使用index fast full scan,如下所示: SQL select /*+index(t)*/n1 from t where n1 is not null order by n1; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=26 Card=4 Bytes=408) 1 0 INDEX (FULL SCAN) OF IX_T_N1 (NON-UNIQUE) (Cost=26 Card=4 Bytes=408) 既然index full scan 比 index fast full scan 操作低效,因此前者只有在排序的时候才会使用。默然情况下,索引扫描和index提示都是按升序访问的,要指定扫描顺序,应该使用index_asc或index_desc 提示。如下所示: SQL select /*+index_desc(t)*/n1 from t where n1 is not null order by n1 desc; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=26 Card=4 Bytes=408) 1 0 INDEX (FULL SCAN DESCENDING) OF IX_T_N1 (NON-UNIQUE) (Cost=26 Card=4 Bytes=408)如果查询包含了count函数,优化器会尝试利用索引来避免全表扫描。如下例所示,其中sort aggregate操作用于执行count函数。 SQL select /*+index_ffs(t ix_t_userserv_servstatus)*/count(x) from t_userserviceinfo t; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1) 1 0 SORT (AGGREGATE) 2 1 INDEX (FAST FULL SCAN) OF IX_T_USERSERV_SERVSTATUS (NON-UNIQUE) (Cost=4 Card=15752805) 当对于一个可为空的列进行count查询的时候,优化器能够选择任何包含该列的索引。当count(*)或是针对非空列执行count查询的时候,优化器能够选择的索引至少需要包含一个非空列。 2具有强选择性的SQL语句 索引访问是迄今为止强选择性SQL最常用的访问路径。要利用索引,需要在where字句中至少应用一个包含索引列的限制条件。以下列出使用索引的常见场景。 (1)等值条件对于B索引,当等值条件的索引列具有唯一性时,可以使用索引唯一扫描(index union scan),否则只能使用索引范围扫描(index range scan)。如下两个查询分别是索引唯一扫描和索引范围扫描。 SQL select * from t_userserviceinfo t where t.userservid = 1; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF T_USERSERVICEINFO 2 1 INDEX (UNIQUE SCAN) OF IX_USERSERVICEINFO_ID (UNIQUE) SQL select * from t_userserviceinfo t where t.servstatus = 1; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 TABLE ACCESS (BY INDEX ROWID) OF T_USERSERVICEINFO 2 1 INDEX (RANGE SCAN) OF IX_T_USERSERV_SERVSTATUS (NON-UNIQUE) (2)IS NULL条件 对于B索引来说,当使用多个SQL条件并且其中至少一个不是is null或不等于条件,才可以选择is null条件上的复合索引。如下的查询对此进行了描述,其中i_n123为n1和n2上的复合索引,n1为先导列。执行计划通过操作2上的谓词确认n2上的条件使用了复合索引i_n123。 SQL select /*+index(t I_N123)*/* from t where t.n1 = 6 and t.n2 is null; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=2126) 1 0 TABLE ACCESS (BY INDEX ROWID) OF T (Cost=1 Card=1 Bytes= 2126) 2 1 INDEX (RANGE SCAN) OF I_N123 (NON-UNIQUE) (Cost=2 Card=1) 不过,单列索引不能使用is null条件,这是因为索引中不存储null值。因而在下面这个事例中,优化器无法使用索引,即使使用index提示也会走全表扫描。 SQL select /*+index(t I_N123)*/* from t where t.n2 is null; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=4 Bytes=8504) 1 0 TABLE ACCESS (FULL) OF T (Cost=2 Card=4 Bytes=8504)(3)范围条件 对于B索引来说,范围条件和非唯一索引上的等值条件的处理方式是一样的,都使用了index range scan操作。对于范围条件来说,无论是唯一索引还是普通索引,总能返回多个rowid,如下描述了通过主键列的范围查询: SQL select /*+index(t)*/* from t where id between 10 and 100; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=2126) 1 0 TABLE ACCESS (BY INDEX ROWID) OF T (Cost=1 Card=1 Bytes=2126) 2 1 INDEX (RANGE SCAN) OF T_PK (UNIQUE) (Cost=2 Card=1) (4) LIKE 条件 如果不是以通配符(下划线与百分号) 开头,那么LIKE 条件和范围条件用同样的方式处理。 SQL select /*+index(t)*/* from t where c1 like A%; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=4 Bytes=8504) 1 0 TABLE ACCESS (BY INDEX ROWID) OF T (Cost=1 Card=4 Bytes=8504) 2 1 INDEX (RANGE SCAN) OF I_C1 (NON-UNIQUE) (Cost=2 Card=1)如果使用了是全模糊查询的话,就会导致优化器使用全表扫描,如下使用 like %i% 条件: SQL select count(*) from t where c1 like %i%; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 SORT (AGGREGATE) 2 1 TABLE ACCESS (FULL) OF T (5) min/max函数 要高效地执行包含min与max函数的查询,对B索引来说有两个特定的操作可用。第一个是index full scan(min/max),当查询不指定范围条件使用。尽管执行计划显示为索引全扫描,不过,优化器实际上并不进行索引全扫描,仅查询最右或者最左的索引键。 SQL select max(t.userservid) from t_userserviceinfo t; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 SORT (AGGREGATE) 2 1 INDEX (FULL SCAN (MIN/MAX) OF IX_USERSERVICEINFO_ID (UNIQUE) 当查询条件中有指定范围的时候,一般情况下执行路径选择了index range scan,这样会消耗很大代价。比较下面两个例子的查询结果(用户信息表大概2500W左右的数据),如果执行计划走范围扫描需要11秒左右,如果加上提示强制优化器走index (range scan (min/max)索引,仅需要0.03秒左右,两者执行时间相差百倍。 SQL select max(t.userservid) from t_userserviceinfo t where t.userservid 200000; Execution Plan - 0 SELECT STATEMENT Optimizer=CHOOSE 1 0 SORT (AGGREGATE) 2 1 INDEX (RANGE SCAN) OF IX_USERSERV
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 钽铌化合物制取工设备维护与保养考核试卷及答案
- 新能源电厂新建工程质量控制措施
- 农业合作社土地转让协议书范文
- 餐厅疫情防控期间厨房操作安全措施
- 环保设施实测实量执行措施
- 七年级美术上册教学资源整合计划
- 新能源项目质量风险识别项清单及防控措施
- 教研组师德师风建设活动总结心得体会
- 办公楼装修工期承诺及保证工期措施
- 国际旅游服务合同
- 幼儿园教师考核评价量化表
- 台达DELTA变频器VFD-EL系列使用说明书和手册(完整中文版)VFD007EL23A
- 报名表的模板
- 《急性心肌梗死急诊》课件
- 工程力学专业就业能力展示
- 中控室培训课件
- 自考《01685动漫艺术概论》考试复习题库(含答案)
- 2025年中考语文复习之小题狂练300题(名著阅读):《艾青诗选》《水浒传》(10题)
- 除颤仪的使用方法及操作流程课件
- 环境卫生整治项目合同范例
- ARDS患者的观察与护理
评论
0/150
提交评论