mysql(2):索引

索引基础

索引介绍

定义

索引是满足某种特定查找算法的数据结构。这些数据结构会以某种方式指向数据,从而实现高效查找。

优势

提高了查询速度

劣势

降低更新表的速度,因为更新表时,MySQL不仅要保存数据,还要保存索引文件。

建立索引会占用磁盘空间的索引文件。

索引分类

主键索引

根据主键pk_column(length)建立索引,不允许重复,不允许空值

ALTER TABLE 'table_name' ADD PRIMARY KEY pk_index('col');

唯一索引UNIQUE

用来建立索引的列的值必须是唯一的,允许空值

ALTER TABLE 'table_name' ADD UNIQUE INDEX index_name('col');

普通索引

普通列构建的索引,没有任何限制。

ALTER TABLE 'table_name' ADD INDEX index_name('col');

组合索引

用多个列组合构建的索引,这多个列中的值不允许有空值。

ALTER TABLE 'table_name' ADD INDEX index_name('col1','col2','col3');

遵循“最左前缀”原则,把最常用作为检索或排序的列放在最左,依次递减,组合索引相当于建立了col1,[col1,col2],[col1,col2,col3]三个索引,而col2或者col3是不能使用索引的。

全文索引FULLTEXT

大文本对象的列构建的索引。

ALTER TABLE 'table_name' ADD FULLTEXT INDEX ft_index('col');

空间索引SPATIAL

支持空间数据格式。

ALTER TABLE 'table_name' ADD SPATIAL KEY index_name('col');

索引使用

索引创建

CREATE INDEX indexName ON tableName(columnName(length));
ALTER table tableName ADD INDEX indexName(columnName)
CREATE TABLE tableName(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
INDEX [indexName] (username(length))
);

索引删除

DROP INDEX [indexName] ON tableName; 

索引查看

SHOW INDEX FROM table_name;

索引原理分析

索引存储结构

B树与B+树

平衡多叉树。

B树非叶节点和叶节点都存有数据。

B+树叶子节点存放着所有的数据。

B+树索引在数据库中有一个特点是高扇出性,因此,在数据库中,B+树的高度一般都在24层,也就是说查找某一键值的行记录时,最多只需要24次IO。

B+树索引可以分为聚集索引(clustered index)和非聚集索引。

非聚集索引(MyISAM)

MyISAM存储引擎表是堆表,行数据的存储按照插入顺序存放。

非聚集索引叶子节点并不包含行记录的全部数据,而是键值和书签。书签指向数据行的位置。

非聚集索引的存在不影响数据的组织,因此每张表上可以有多个非聚集索引。

聚集索引(InnoDB)

InnoDB存储引擎表是索引组织表,即表中数据按照主键顺序存放。(否则会频繁的引起页分裂,严重影响性能。)

聚集索引就是按照每张表的主键构造一棵B+树,因此每张表只能由一个聚集索引。

数据页上存放的是完整的每行的记录,非数据页中存放的仅仅是键值和偏移量。由于聚集索引存储的是数据本身,因此聚集索引会占用更多的空间。

聚集索引的顺序就是数据的物理存储顺序。

聚集索引对于主键的排序查找和范围查找速度非常快。(添加指向相邻叶节点的指针

MyISAM vs. InnoDB
MyISAM InnoDB
索引类型 非聚簇 聚簇
支持事务
支持表锁
支持行锁 是(默认)
支持外键
支持全文索引 是(5.6以后支持)
适用操作类型 大量select下使用 大量insert、delete和update下使用

InnoDB:主索引和辅助索引

MyISAM(不支持聚集索引):主索引和辅助索引

索引使用场景

使用索引情况

  • 主键自动建立唯一索引;
  • 经常作为查询条件在WHERE或者ORDER BY 语句中出现的列要建立索引;
  • 查询中与其他表关联的字段,外键关系建立索引;
  • 高并发条件下倾向组合索引;
  • 用于聚合函数的列可以建立索引。

不使用索引情况

  • 经常增删改的列不要建立索引;
  • 有大量重复的列不建立索引;
  • 表记录太少不要建立索引。

索引失效分析

  • 在组合索引中不能有列的值为NULL,如果有,那么这一列对组合索引就是无效的。
  • 在一个SELECT语句中,索引只能使用一次,如果在WHERE中使用了,那么在ORDER BY中就不要用了。
  • LIKE操作中,'%aaa%'不会使用索引,也就是索引会失效,但是‘aaa%’可以使用索引。
  • 在索引的列上使用表达式或者函数会使索引失效,例如:select * from users where YEAR(add_date)<2007,将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成:select * from users where add_date<’2007-01-01′。其它通配符同样,也就是说,在查询条件中使用正则表达式时,只有在搜索模板的第一个字符不是通配符的情况下才能使用索引。
  • 在查询条件中使用不等于,包括<、>和!=会导致索引失效。特别的是如果对主键索引使用!=则不会使索引失效,如果对主键索引或者整数类型的索引使用<或者>不会使索引失效。(如果占总记录的比例很小的话,也不会失效)
  • 在查询条件中使用IS NOT NULL会导致索引失效(IS NULL不会导致索引失效)
  • 字符串不加单引号会导致索引失效。更准确的说是类型不一致会导致失效。
  • 在查询条件中使用OR连接多个条件会导致索引失效,除非OR链接的每个条件都加上索引,这时应该改为两次查询,然后用UNION ALL连接起来。
  • 如果排序的字段使用了索引,那么select的字段也要是索引字段,否则索引失效。特别的是如果排序的是主键索引则select * 也不会导致索引失效。
  • 尽量不要包括多列排序,如果一定要,最好为这队列构建组合索引;

查看执行计划

explain查看执行计划
EXPLAIN SELECT * FROM `user`
  JOIN `post` ON `user`.id = `post`.uid
WHERE user.`created` < '2018-10-01 00:00:00' AND `post`.status = 1;

结果:

id select _type table type possible _keys key key _len ref rows Extra
1 SIMPLE user range PRIMARY, idx_created idx_created 7 null 19440 Using index condition; Using where; Using temporary; Using filesort
1 SIMPLE post ref idx_uid, idx_status idx_uid 8 user.id 1 Using where
参数说明
id: 查询的唯一标识
table: 查询的表

可能是数据库中的表/视图,也可能是 FROM 中的子查询

possible_keys: 可能使用的索引
key: 最终使用的key
key_len: 查询索引使用的字节数。通常越少越好。
ref: 查询的列或常量
rows: 需要扫描的行数,估计值。通常越少越好。
select_type参数分析:查询的类型
  1. simple:简单查询,不包含子查询和union
  2. primary:包含子查询时的最外层查询;使用union时的第一个查询
  3. subquery:子查询
  4. dependent subquery:依赖外层查询的子查询
  5. union:包含union的查询中非第一个查询
  6. dependent union:依赖外层查询的union
  7. derived:用于from中的子查询
type参数分析:访问类型/搜索数据的方法
  1. null:优化过程中分解语句,执行时不用访问表或索引。[从一个索引列里选取最小值可以通过单独索引查找完成]
  2. system:表只有一行记录(等于系统表)。[const特例]
  3. const:查询优化,并转换为一个常量时,使用这些类型访问。[将主键置于where列表中,MySQL就能将该查询转换为一个常量]
  4. eq_ref:使用唯一索引,对于每个索引键值,表中只有一条记录匹配。[多表连接中使用primary key或者 unique key作为关联条件]
  5. ref:使用非唯一索引扫描、唯一索引的前缀扫描,返回匹配某个单独值的记录行
  6. range:索引范围扫描,返回匹配值域的行。[①between;②where子句里带有<, >查询;③in()]
  7. index:全索引扫描,只遍历索引树
  8. all:全表扫描, 遍历全表以找到匹配的行
extra参数分析:额外的信息
  1. using index:无需回表查询,从索引中即可获得信息。[可以加快查询速度]
  2. using where:服务器层对存储引擎返回的数据进行了过滤。
  3. using index condition:新特性,索引条件推送。服务器层将不能直接使用索引的查询条件(如like)推送给存储引擎,从而避免在服务器层进行过滤。[减少了不必要的IO操作,只能用在二级索引上]
    • ICP的理解:将原来在server层进行的table filter中可以进行index filter的部分,在引擎层面使用index filter进行处理,不再需要回表进行table filter。[见下文where条件的提取]
  4. using filesort:查询时执行了排序操作而无法使用索引排序。虽然名称为'file'但操作可能是在内存中执行的,取决是否有足够的内存进行排序。[可能是order by和group by语句的结果,考虑优化查询]
  5. using temporary:用临时表保存中间结果,常用于group by和order by操作中。[说明查询需要优化]
  6. not exists:优化left join,一旦找到了匹配left join标准的行就停止搜索。
  7. using join buffer:使用了连接缓存。
    • Block Nested Loop块嵌套循环连接
    • Batched Key Access批量索引连接
  8. select tables optimized away:在没有group by子句的情况下,使用某些聚合函数(min, max)来访问存在索引的某个字段时,或者对于MyISAM存储引擎优化count(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
  9. distinct:优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作。

where条件提取

链接

create table t1 (a int primary key, b int, c int, d int, e varchar(20));
create index idx_t1_bcd on t1(b, c, d);
insert into t1 values (4,3,1,1,’d’);
insert into t1 values (1,1,1,1,’a’);
insert into t1 values (8,8,8,8,’h’):
insert into t1 values (2,2,2,2,’b’);
insert into t1 values (5,2,3,5,’e’);
insert into t1 values (3,3,2,2,’c’);
insert into t1 values (7,4,5,5,’g’);
insert into t1 values (6,6,4,4,’f’);

idx_t1_bcd索引,首先按照b字段排序,b字段相同,则按照c字段排序,以此类推。记录在索引中按照[b,c,d]排序,但是在堆表上是乱序的,不按照任何字段排序。

考虑以下的一条SQL:

select * from t1 where b >= 2 and b < 8 and c > 1 and d != 4 and e != 'a';

1,此SQL,覆盖索引idx_t1_bcd上的哪个范围?
起始范围:记录[2,2,2]是第一个需要检查的索引项。b >= 2,c > 1
终止范围:记录[8,8,8]是第一个不需要检查的记录,而之前的记录均需要判断。b < 8

2,在确定了查询的起始、终止范围之后,SQL中还有哪些条件可以使用索引idx_t1_bcd过滤?
c > 1 and d != 4

3,在确定了索引中最终能够过滤掉的条件之后,还有哪些条件是索引无法过滤的?
e != 'a'

总结出一套放置于所有SQL语句而皆准的where查询条件的提取规则:

  1. Index Key (First Key & Last Key):定位索引的起始范围和终止范围
  2. Index Filter:过滤索引查询范围中不满足查询条件的记录
  3. Table Filter:回表读取满足Table Filter中查询条件的完整记录

MySQL 5.6中引入的ICP(Index Condition Pushdown),是将Index Filter Push Down到索引层面进行过滤的。

在MySQL 5.6之前,并不区分Index Filter与Table Filter,统统将Index First Key与Index Last Key范围内的索引记录,回表读取完整记录,然后返回给MySQL Server层进行过滤。

而在MySQL 5.6之后,Index Filter与Table Filter分离,Index Filter下降到InnoDB的索引层面进行过滤,减少了回表与返回MySQL Server层的记录交互开销,提高了SQL的执行效率。

posted @ 2019-08-05 15:29  白芷呀  阅读(189)  评论(0编辑  收藏  举报