使用索引是提高数据库查询效率的主要方式,下面从索引结构,索引类型,索引操作,命中索引几个方面来介绍索引。
一、索引结构
mysql5.5以上的默认存储引擎innodb,只显式支持BTree( 事实上从数据结构上来讲是B+树,mysql称之为BTree)索引,对于频繁访问的表,innodb会建立自适应hash索引,
即在B树索引基础上建立hash索引,可以显著提高查找效率,用户是无法自己指定的,除此之外还有Hash索引和全文索引(fullText索引)。
**B****Tree索引 **
BTree,顾名思义,就是所有的索引节点都按照balance tree的数据结构来存储。BTree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。
Btree中,每个结点包含:
1、本结点所含关键字的个数;
2、指向父结点的指针;
3、关键字;
4、指向子结点的指针;
**Hash索引 **
Hash 索引由于使用了hash表结构,其检索效率很高,索引的检索可以一次定位,不像B+树索引需要从根节点到枝节点,
所以 Hash 索引的查询效率要远高于 B+树 索引。但是,Hash索引的使用范围非常有限。
1.在MySQL中,只有HEAP/MEMORY引擎表才能支持哈希索引,InnoDB引擎的自适应哈希索引(adaptive hash index)不
能在创建索引时指定。
2.Hash索引仅支持=,>=,<=这几种条件运算,不支持排序,范围内查找,like等查询。
3.Hash索引不支持组合索引中部分索引的查找。
4.当Hash值重复较多时,索引速度可能不如BTree索引。
FullText索引
Full-text索引就是我们常说的全文索引,他的存储结构也是Btree。主要是为了解决在我们需要用like查询的低效问题。
只能解决’xxx%’的like查询。如:字段数据为ABCDE,索引建立为A、AB、ABC、ABCD、ABCDE五个。
二、索引类型
索引一般有:普通索引,唯一索引,复合索引这几种类型。
唯一索引:唯一索引要求字段中不会出现重复数据。
复合索引:将多个字段组合起来作为索引,必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。
三、索引操作
建立索引:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;"> create index 索引名 on 表名(索引字段名)</pre>
建立唯一索引:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;"> create unique index 索引名 on 表名(索引字段名)</pre>
建立复合索引:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">create index 索引名 on 表名(索引字段名1,索引字段名2...)</pre>
查看表中索引:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">show index from 表名</pre>
删除索引:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">drop index 索引名 on 表名</pre>
查看索引是否命中
Mysql:当结果的type为 index时索引命中
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">explain +sql语句</pre>
Oracle:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">explain plan for+sql语句</pre>
四、命中索引
1. 避免判断null值
应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t where num is null</pre>
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t where num=0</pre>
2. 避免不等值判断
应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
3. 避免使用or逻辑
应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t where num=1 or num=2</pre>
可以使用union all来进行查询来命中索引如:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t where num=1 union all select id from t where num=2</pre>
4 . 慎用in和not in逻辑
in 和 not in 也要慎用,否则会导致全表扫描,如:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t1 where num in(select id from t2 where id > 10)</pre>
此时外层查询会全表扫描,不使用索引。可以修改为:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t1,(select id from t1 where id > 10)t2 where t1.id = t2.id</pre>
此时索引被使用,可以明显提升查询效率。
5. 注意模糊查询
下面的查询也将导致全表扫描:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t where name like '%abc%'</pre>
不要在like条件左边加'%',这样不会走索引模糊查询如果是必要条件时,可以使用
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t where name like 'abc%'</pre>
来实现模糊查询,此时索引将被使用。如果头匹配是必要的,可以使用elasticsearch等全文搜索引擎。
6. 避免查询条件中字段计算
应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t where num/2=100</pre>
应改为:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t where num=100*2</pre>
7. 避免查询条件中对字段进行函数操作
应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t where substring(name,1,3)='abc'</pre>
name以abc开头的id
应改为:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select id from t where name like 'abc%'</pre>
8. “=”左边避免表达式运算
不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
9. 组合索引使用
在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
10 exists
很多时候用 exists 代替 in 是一个好的选择:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select num from a where num in(select num from b)</pre>
用下面的语句替换:
<pre style="margin: 0px; padding: 0px; overflow: auto; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important; white-space: pre-wrap;">select num from a where exists(select 1 from b where num=a.num)</pre>