MySql索引类型和命中规则
约 1815 字大约 6 分钟
2024-11-17
参考:数据库索引详解
https://github.com/digoal/blog
1. 索引结构
mysql5.5以上的默认存储引擎innodb,只显式支持BTree( 事实上从数据结构上来讲是B+树,mysql称之为BTree)索引,对于频繁访问的表,innodb会建立自适应hash索引,
即在B树索引基础上建立hash索引,可以显著提高查找效率,用户是无法自己指定的,除此之外还有Hash索引和全文索引(fullText索引)。
1.1. BTree索引
BTree,顾名思义,就是所有的索引节点都按照balance tree的数据结构来存储。BTree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。 Btree中,每个结点包含:
- 本结点所含关键字的个数;
- 指向父结点的指针;
- 关键字;
- 指向子结点的指针;
更详细的B+树介绍参考https://www.cnblogs.com/yangecnu/p/Introduce-B-Tree-and-B-Plus-Tree.html
1.2. Hash索引
Hash 索引由于使用了hash表结构,其检索效率很高,索引的检索可以一次定位,不像B+树索引需要从根节点到枝节点, 所以 Hash 索引的查询效率要远高于 B+树 索引。但是,Hash索引的使用范围非常有限。
- 在MySQL中,只有HEAP/MEMORY引擎表才能支持哈希索引,InnoDB引擎的自适应哈希索引(adaptive hash index)不能在创建索引时指定。
- Hash索引仅支持=,>=,<=这几种条件运算,不支持排序,范围内查找,like等查询。
- Hash索引不支持组合索引中部分索引的查找。
- 当Hash值重复较多时,索引速度可能不如BTree索引。
1.3. FullText索引
Full-text索引就是我们常说的全文索引,他的存储结构也是Btree。主要是为了解决在我们需要用like查询的低效问题。
只能解决’xxx%’的like查询。如:字段数据为ABCDE,索引建立为A、AB、ABC、ABCD、ABCDE五个。
2. 索引类型
索引一般有:普通索引,唯一索引,复合索引这几种类型。
唯一索引:唯一索引要求字段中不会出现重复数据。
复合索引:将多个字段组合起来作为索引,必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。
3. 索引操作
3.1. MySql创建索引
建表的时候加索引
create table book
(
id bigint auto_increment primary key not null,
title varchar(100) comment '标题',
name varchar(100),
author varchar(100),
index index_1(name, author)
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '书';
建立索引:
create index 索引名 on 表名(索引字段名)
建立唯一索引:
create unique index 索引名 on 表名(索引字段名)
建立复合索引:
create index 索引名 on 表名(索引字段名1,索引字段名2...)
删除索引:
drop index 索引名 on 表名
查看表中索引:
show index from 表名
查看索引是否命中 Mysql:当结果的type为 index时索引命中
explain +sql语句
Oracle:
explain plan for+sql语句
3.2. PgSql创建索引
不管是单列索引还是组合索引,该索引必须是在 WHERE 子句的过滤条件中使用非常频繁的列。 如果只有一列被使用到,就选择单列索引,如果有多列就使用组合索引。
单列索引: 是一个只基于表的一个列上创建的索引,基本语法如下:
CREATE INDEX index_name ON table_name (column_name);
简化:
create index index_name2 on table_name using btree (id);
组合索引: 是基于表的多列上创建的索引,基本语法如下:
CREATE INDEX index_name ON table_name (column1_name, column2_name);
删除索引:
DROP INDEX index_name;
分析语句:
explain analyze SELECT name FROM test WHERE id = 10000;
唯一索引
使用唯一索引不仅是为了性能,同时也为了数据的完整性。唯一索引不允许任何重复的值插入到表中。基本语法如下:
CREATE UNIQUE INDEX index_name on table_name (column_name);
4. 命中索引
4.1. 避免判断null值
应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
4.2. 避免不等值判断
应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
4.3. 避免使用or逻辑
应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,
select id from t where num=1 or num=2
可以使用union all来进行查询来命中索引如:
select id from t where num=1 union all select id from t where num=2
4.4. 慎用in和not in逻辑
in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t1 where num in(select id from t2 where id > 10)
此时外层查询会全表扫描,不使用索引。可以修改为:
select id from t1,(select id from t1 where id > 10)t2 where t1.id = t2.id
此时索引被使用,可以明显提升查询效率。
4.5. 注意模糊查询
下面的查询也将导致全表扫描:
select id from t where name like '%abc%'
不要在like条件左边加'%',这样不会走索引模糊查询如果是必要条件时,可以使用
select id from t where name like 'abc%'
来实现模糊查询,此时索引将被使用。如果头匹配是必要的,可以使用elasticsearch等全文搜索引擎。
4.6. 避免字段计算
应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
4.7. 避免进行函数操作
应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)='abc'
name以abc开头的id
应改为:
select id from t where name like 'abc%'
4.8. “=”左边避免表达式运算
不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
4.9. 组合索引使用
在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
4.10. exists
很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)