原文连接:blog.ouyangsihai.cn >> MySQL的B+树索引的概念、使用、优化及使用场景 php
这篇文章不会讲解索引的基础知识,主要是关于MySQL数据库的B+树索引的相关原理,里面的一些知识都参考了MySQL技术内幕这本书,也算对于这些知识的总结。对于B树和B+树相关的知识,能够参考个人这篇博客:面试官问你B树和B+树,就把这篇文章丢给他html
索引有不少中类型:普通索引、惟一索引、主键索引、组合索引、全文索引,下面咱们看看如何建立和删除下面这些类型的索引。mysql
索引的建立是能够在不少种状况下进行的。面试
CREATE [UNIQUE|FULLLTEXT] INDEX index_name ON table_name(column_name(length))
[UNIQUE|FULLLTEXT]
:表示可选择的索引类型,惟一索引仍是全文索引,不加话就是普通索引。 table_name
:表的名称,表示为哪一个表添加索引。 column_name(length)
:column_name是表的列名,length表示为这一列的前length行记录添加索引。正则表达式
ALTER TABLE table_name ADD [UNIQUE|FULLLTEXT] INDEX index_name (column(length))
CREATE TABLE `table` ( `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER NOT NULL , PRIMARY KEY (`id`), [UNIQUE|FULLLTEXT] INDEX index_name (title(length)) )
前面讲的都是普通索引、惟一索引和全文索引建立的方式,可是,主键索引和组合索引建立的方式倒是有点不同的,因此单独拿出来说一下。算法
组合索引建立方式sql
CREATE TABLE `table` ( `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER NOT NULL , PRIMARY KEY (`id`), INDEX index_name(id,title) )
ALTER TABLE table_name ADD INDEX name_city_age (name,city,age);
主键索引建立方式 主键索引是一种特殊的惟一索引,一个表只能有一个主键,不容许有空值。通常是在建表的时候同时建立主键索引。数据库
CREATE TABLE `table` ( `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER NOT NULL , PRIMARY KEY (`id`) )
删除索引可利用ALTER TABLE
或DROP INDEX
语句来删除索引。相似于CREATE INDEX
语句,DROP INDEX
能够在ALTER TABLE
内部做为一条语句处理,语法以下。segmentfault
(1)DROP INDEX index_name ON talbe_name
(2)ALTER TABLE table_name DROP INDEX index_name
(3)ALTER TABLE table_name DROP PRIMARY KEY
并发
第3条语句只在删除PRIMARY KEY
索引时使用,由于一个表只可能有一个PRIMARY KEY
索引,所以不须要指定索引名。
上面讲了一下基本的知识,接下来,仍是经过一个具体的例子来体会一下。
create table table_index( id int(11) not null auto_increment, title char(255) not null, primary key(id) );
首先,咱们使用直接添加索引的方式添加一个普通索引。
CREATE INDEX idx_a ON table_index(title);
接着,咱们用修改表结构的时候添加索引。
ALTER TABLE table_index ADD UNIQUE INDEX idx_b (title(100));
最后,咱们再添加一个组合索引。
ALTER TABLE table_index ADD INDEX idx_id_title (id,title);
这样,咱们就把前面索引的方式都用上一遍了,我相信你也熟悉这些操做了。
SHOW INDEX
命令查看索引信息若是想要查看表中的索引信息,可使用命令SHOW INDEX
,下面的例子,咱们查看表table_index
的索引信息。
SHOW INDEX FROM table_index\G;
获得上面的信息,上面的信息什么意思呢?咱们逐一介绍!
字段 | 解释 |
---|---|
Table | 索引所在的表 |
Non_unique | 非惟一索引,若是是0,表明惟一的,也就是说若是该列索引中不包括重复的值则为0 不然为1 |
Key_name | 索引的名字,若是是主键的话 则为PRIMARY |
Seq_in_index | 索引中该列的位置,从1开始,若是是组合索引 那么按照字段在创建索引时的顺序排列 |
Collation | 列是以什么方式存储在索引中的。能够是A或者NULL,B+树索引老是A,排序的, |
Sub_part | 是否列的部分被索引,若是只是前100行索引,就显示100,若是是整列,就显示NULL |
Packed | 关键字是否被压缩,若是没有,为NULL |
Index_type | 索引的类型,对于InnoDB只支持B+树索引,因此都是显示BTREE |
直接删除索引方式
DROP INDEX idx_a ON table_index;
修改表结构时删除索引
ALTER TABLE table_index DROP INDEX idx_b;
在上面介绍了那么多个关键字的意思,可是Cardinality
这个关键字很是的关键,优化器会根据这个值来判断是否使用这个索引。在B+树索引中,只有高选择性的字段才是有意义的,高选择性就是这个字段的取值范围很广,好比姓名字段,会有不少的名字,可选择性就高了。
通常来讲,判断是否须要使用索引,就能够经过Cardinality
关键字来判断,若是很是接近1,说明有必要使用,若是很是小,那么就要考虑是否使用索引了。
须要注意的一个问题时,这个关键字不是及时更新的,须要更新的话,须要使用ANALYZE TABLE
,例如。
analyze table table_index;
由于目前没有数据,因此,你会发现,这个值一直都是0,没有变化。
在InnoDB存储引擎中,这个关键字的更新发生在两个操做中:insert和update。可是,并非每次都会更新,这样会增长负荷,因此,对于这个关键字的更新有它的策略:
1/16
的数据发生变化stat_modified_conter
>2000000000默认InnoDB存储引擎会对8个叶子节点进行采样,采样过程以下:
A
8
个叶子节点。统计每一个页不一样的记录个数,分别为p1-p8(p1+p2+p3+...+p8)*A/8
由于随机采样,因此,每次的Cardinality值都是不同的,只有一种状况会同样的,就是表中的叶子节点小于或者等于8,这时候,怎么随机采样都是这8个,因此也就同样的。
在MySQL 5.5以前,对于索引的添加或者删除,每次都须要建立一张临时表,而后导入数据到临时表,接着删除原表,若是一张大表进行这样的操做,会很是的耗时,这是一个很大的缺陷。
InnoDB存储引擎从1.0.x版本开始加入了一种Fast Index Creation(快速索引建立)的索引建立方式。
这种方式的策略为:每次为建立索引的表加上一个S锁(共享锁),在建立的时候,不须要从新建表,删除辅助索引只须要更新内部视图,并将辅助索引空间标记为可用,因此,这种效率就大大提升了。
MySQL5.6开始支持的在线数据定义操做就是:容许辅助索引建立的同时,还容许其余insert、update、delete这类DM操做,这就极大提升了数据库的可用性。
因此,咱们可使用新的语法进行建立索引:
ALTER TABLE table_name ADD [UNIQUE|FULLLTEXT] INDEX index_name (column(length)) [ALGORITHM = {DEFAULT|INPLACE|COPY}] [LOCK = {DEFAULT|NONE|SHARED|EXLUSIVE}]
ALGORITHM
指定建立或者删除索引的算法
old_alter_table
参数判断,若是是OFF
,采用INPLACE
的方式LOCK表示对表添加锁的状况
NONE
,如不能,判断是否可使用SHARE
,如不能,再判断是否可使用EXCLUSIVE
模式。联合索引是指对表上的多个列进行索引,这一部分咱们将经过几个例子来说解联合索引的相关知识点。
首先,咱们先建立一张表以及为这张表建立联合索引。
create table t_index( a char(2) not null default '', b char(2) not null default '', c char(2) not null default '', d char(2) not null default '' )engine myisam charset utf8;
建立联合索引
alter table t_index add index abcd(a,b,c,d);
插入几条测试数据
insert into t_index values('a','b','c','d'), ('a2','b2','c2','d2'), ('a3','b3','c3','d3'), ('a4','b4','c4','d4'), ('a5','b5','c5','d5'), ('a6','b6','c6','d6');
到这一步,咱们已经基本准备好了须要的数据,咱们能够进行更深一步的联合索引的探讨。
索引创建的主要目的就是为了提升查询的效率,那么联合索引的目的也是相似的,联合索引的目的就是为了提升存在多个查询条件的状况下的效率,就如上面创建的表同样,有多个字段,当咱们须要利用多个字段进行查询的时候,咱们就须要利用到联合索引了。
有时候,咱们会用联合索引,可是,咱们并不清楚其原理,不知道何时联合索引会起到做用,何时又是会失效的?
带着这个问题,咱们了解一下联合索引的最左匹配原则。
最左匹配原则:这个原则的意思就是建立组合索引,以最左边的为准,只要查询条件中带有最左边的列,那么查询就会使用到索引。
下面,咱们用几个例子来看看这个原则。
EXPLAIN SELECT * FROM t_index WHERE a = 'a' \G;
咱们看看这条语句的结果,首先,咱们看到使用了索引,由于查询条件中带有最左边的列a,那么利用了几个索引呢?这个咱们须要看key_len
这个字段,咱们知道utf8编码的一个字符3个字节,而咱们使用的数据类型是char(2)
,占两个字节,索引就是2*3等于6个字节,因此只有一个索引发到了做用。
EXPLAIN SELECT * FROM t_index WHERE b = 'b2' \G;
这个语句咱们能够看出,这个没有使用索引,由于possible_keys
为空,并且,从查询的行数rows
能够看出为6(咱们测试数据总共6条),说明进行了全盘扫描的,说明这种状况是不符合最左匹配原则,因此不会使用索引查询。
EXPLAIN SELECT * FROM t_index WHERE a = 'a2' AND b = 'b2' ORDER BY d \G;
这种状况又有点不同了,咱们使用了一个排序,能够看出使用了索引,经过key_len
为12能够获得使用了2个索引a、b
,另外在Extra选项中能够看到使用了Using filesort
,也就是文件排序,这里使用文件排序的缘由是这样的:上面的查询使用了a、b索引,可是当咱们用d字段来排序时,(a,d)或者(b,d)这两个索引是没有排序的,联合索引的使用有一个好处,就是索引的下一个字段是会自动排序的,在这里的这种状况来讲,c字段就是排序的,可是d是不会,若是咱们用c来排序就会获得不同的结果。
EXPLAIN SELECT * FROM t_index WHERE a = 'a2' AND b = 'b2' ORDER BY c \G;
是否是能够看到,当咱们用c进行排序的时候,由于使用了a、b索引,因此c就自动排序了,因此也就不用filesort了。
讲到这里,我相信经过上面的几个例子,对于联合索引的相关知识已经很是的透彻清晰了,最后,咱们再来聊几个常见的问题。
第一,建立索引和维护索引要耗费时间,这种时间随着数据量的增长而增长。 第二,索引须要占物理空间,除了数据表占数据空间以外,每个索引还要占必定的物理空间,若是要创建聚簇索引,那么须要的空间就会更大。 第三,当对表中的数据进行增长、删除和修改的时候,索引也要动态的维护,这样就下降了数据的维护速度。
减小开销。建一个联合索引(col1,col2,col3),实际至关于建了(col1),(col1,col2),(col1,col2,col3)三个索引。每多一个索引,都会增长写操做的开销和磁盘空间的开销。对于大量数据的表,使用联合索引会大大的减小开销!
覆盖索引。对联合索引(col1,col2,col3),若是有以下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那么MySQL能够直接经过遍历索引取得数据,而无需回表,这减小了不少的随机io操做。减小io操做,特别的随机io实际上是dba主要的优化策略。因此,在真正的实际应用中,覆盖索引是主要的提高性能的优化手段之一。
效率高。索引列越多,经过索引筛选出的数据越少。有1000W条数据的表,有以下sql:select from table where col1=1 and col2=2 and col3=3,假设假设每一个条件能够筛选出10%的数据,若是只有单值索引,那么经过该索引能筛选出1000W10%=100w条数据,而后再回表从100w条数据中找到符合col2=2 and col3= 3的数据,而后再排序,再分页;若是是联合索引,经过索引筛选出1000w10% 10% *10%=1w,效率提高可想而知!
覆盖索引 覆盖索引是一种从辅助索引中就能够获得查询的记录,而不须要查询汇集索引中的记录,使用覆盖索引的一个好处是辅助索引不包含整行记录的全部信息,因此大小远小于汇集索引,所以能够大大减小IO操做。覆盖索引的另一个好处就是对于统计问题有优化,咱们看下面的一个例子。
explain select count(*) from t_index \G;
若是是myisam引擎,Extra列会输出Select tables optimized away
语句,myisam引擎已经保存了记录的总数,直接返回结果,就不须要覆盖索引优化了。
若是是InnoDB引擎,Extra列会输出Using index
语句,说明InnoDB引擎优化器使用了覆盖索引操做。
MySQL数据库支持索引提示功能,索引提示功能就是咱们能够显示的告诉优化器使用哪一个索引,通常有下面两种状况可能使用到索引提示功能(INDEX HINT):
这里咱们接着上面的例子来说解,首先,咱们先为上面的t_index
表添加几个索引;
alter table t_index add index a (a); alter table t_index add index b (b); alter table t_index add index c (c);
接着,咱们执行下面的语句;
EXPLAIN SELECT * FROM t_index WHERE a = 'a' AND b = 'b' AND c = 'c' \G;
你会发现这条语句就可使用三个索引,这个时候,咱们能够显示的使用索引提示来使用a这个索引,以下:
EXPLAIN SELECT * FROM t_index USE INDEX(a) WHERE a = 'a' AND b = 'b' AND c = 'c' \G;
这样就显示的使用索引a了,若是这种方式有时候优化器仍是没有选择你想要的索引,那么,咱们能够另一种方式FORCE INDEX
。
EXPLAIN SELECT * FROM t_index FORCE INDEX(a) WHERE a = 'a' AND b = 'b' AND c = 'c' \G;
这种方式则必定会选择你想要的索引。
MySQL5.6开始支持,这种优化的目的是为了减小磁盘的随机访问,而且将随机访问转化为较为顺序的数据访问,这种优化适用于range、ref、eq_ref类型的查询。
Multi-Range Read 优化的好处:
咱们可使用参数optimizer_switch
中的标记来控制是否开启Multi-Range Read 优化。下面的方式将设置为老是开启状态:
SET @@optimizer_switch='mrr=on,mrr_cost_based=off';
这种优化方式也是从MySQL5.6开始支持的,不支持这种方式以前,当进行索引查询时,首先咱们先根据索引查找记录,而后再根据where条件来过滤记录。然而,当支持ICP优化后,MySQL数据库会在取出索引的同时,判断是否能够进行where条件过滤,也就是将where过滤部分放在了存储引擎层,大大减小了上层SQL对记录的索取。
ICP支持range、ref、eq_ref、ref_or_null类型的查询,当前支持MyISAM和InnoDB存储引擎。
咱们可使用下面语句开启ICP:
set @@optimizer_switch = "index_condition_pushdown=on"
或者关闭:
set @@optimizer_switch = "index_condition_pushdown=off"
当开启了ICP以后,在执行计划Extra能够看到Using index condition
提示。
对索引中全部列都指定具体值,便是对索引中的全部列都有等值匹配的条件。
对索引的值可以进行范围查找。
仅仅使用索引中的最左边列进行查询,好比在 col1 + col2 + col3 字段上的联合索引可以被包含 col一、(col1 + col2)、(col1 + col2 + col3)的等值查询利用到,但是不可以被 col二、(col二、col3)的等值查询利用到。 最左匹配原则能够算是 MySQL 中 B-Tree 索引使用的首要原则。
当查询的列都在索引的字段中时,查询的效率更高,因此应该尽可能避免使用 select *,须要哪些字段,就只查哪些字段。
仅仅使用索引中的第一列,而且只包含索引第一列的开头一部分进行查找。
explain select * from t_index where a is null \G
where 'age'+10=30