索引有不少种,hash索引,B树索引,B+树索引,全文索引等。Mysql支持多种存储引擎,多种存储引擎对索引的支持也各不相同。本文探究Mysql为何使用B+树来做为索引的数据结构,索引的原理已经Sql中索引的优化。
Mysql官方对索引的定义是:索引(Index)是帮助Mysql高效获取数据的数据结构。提取句子主干就是:索引是数据结构。mysql
索引的目的在于提升查询或检索效率。例如咱们要在字典中查询“mysql”这个单词,是否是先要查询m开头的单词表,而后在查询第二个字母为y的单词,而后缩小范围继续找,知道找到“mysql”这个单词为止或者查无此词。这就好像咱们沿着一个树从树根开始找,沿着主干,树干,到最后的末梢,走了其中的一条路径。这比一个查询一个链表的结构,从头找到尾,在大多数状况下,效率要高得多。算法
为何不用普通的二叉树,这里就没必要多说了,由于对于大的数据量,二叉树的高度过高,索引的效率低下。这里主要说明为何不用B树(B-树就是B树),而是用B+树。sql
咱们都知道二叉树查询的时间复杂度为O(logN),查询效率已经够高了,但为何还要有B树和B+树呢?答案是磁盘IO。咱们都知道,IO操做的效率很低,当有存储的有很大的数据量,查询的时候,咱们不可能把所有数据都加载到内存中,只能逐一加载磁盘页,每一个磁盘页对应树的节点,形成大量的磁盘IO操做(最坏状况下,磁盘IO操做次数是树的高度),平衡二叉树因为树的高度太大形成磁盘IO读写过于频繁,从而致使效率低下,因此多路查找树-B树/B+树应运而生。
下面是一个三阶的B树(实际中节点元素不少)数据库
B+树有如下特色:缓存
咱们知道Mysql有两种经常使用的存储引擎,MyISAM和InnoDB,这两种存储引擎对索引的实现方式是不一样的。安全
MyISAM使用B+树做为索引的结构,叶子结点的data域存放的是数据记录的地址。 bash
InnoDB的索引实现方式与MyISAM的索引实现方式的区别有两个:
第一,InnoDB的数据文件自己就是索引文件。在InnoDB中,数据文件自己就是按B+树组织的一个索引结构,并且是主索引结构。数据和索引在一块儿,叶子结点保存了完整的数据记录,这种索引叫作汇集索引。由于InnoDB的数据文件自己要按主键汇集,因此InnoDB要求表必须有主键(MyISAM能够没有),若是没有显式指定,则MySQL系统会自动选择一个能够惟一标识数据记录的列做为主键,若是不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段做为主键,这个字段长度为6个字节,类型为长整形。服务器
在使用InnoDB做为存储引擎时,若是没有特殊须要,请永远是用一个与业务无关的自增字段做为主键,并且这个字段长度不宜过大。为何?InnoDB使用汇集索引,数据记录自己存放在主索引(B+树)的叶子结点上,这就要求同一个叶子结点(大小为一个内存页或磁盘页)的数据记录按主键顺序存放,每当一条新的记录插入时,mysql会根据其主键将其插入适当的节点和位置,若是页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)。若是使用自增主键,那么每次插入新的记录,记录就会顺序插入到当前节点的下一个位置。这样就会造成一个紧凑的索引结构,每次插入不须要移动已有数据,所以效率很高。以下图:网络
若是使用非自增主键(例如身份证号或学号这种无序字符串),每次插入主键近似随机,每次记录都要插入到现有索引页的中间的某个位置,这时不得不移动元素来完成插入,增长了开销。以下图:数据结构
联合索引:mysql能够将多个列按照顺序做为一个索引,这种索引叫作联合索引。
索引的最左匹配原则是:假如索引列分别为A,B,C,顺序也是A,B,C,那么:
这个原则能够结合索引的原理来理解:Mysql索引是B+树这种复合结构,当索引是联合索引,好比【name,age,sex】时,B+树是按照从左到右的顺序创建索引树的。当(张三,20,M)这样的数据来检索时,B+树会优先根据name来肯定下一步的搜索方向,若是name相同再比较name和sex,最后获得检索的数据。但当(20,M)这样的数据来的时候,mysql就不知道该查哪一个节点,由于创建索引的时候,name就是第一个比较因子,必须先根据name去肯定下一步去哪里搜索。当(张三,M)这样的数据来时,能够根据name是“张三”,来肯定下一步的搜索,而后再去匹配性别是“M”的数据,所以只能用到联合索引中name这个索引。
一、尽可能选择区分度高的列做为索引,区分度公式:count(distinct col)/count(*),表示字段不重复的比例,比例越大,咱们扫描的记录数就越少,惟一性的列的区分度为1。这就是为何不建议在状态,性别这样区分度很小的列上创建索引的缘由。
二、索引列在sql语句中不能参与运算,不然会致使索引失效。例如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,缘由很简单,b+树中存的都是数据表中的字段值,但进行检索时,须要把全部元素都应用函数才能比较,显然成本太大。应该改为create_time = unix_timestamp(’2014-05-29’);
三、联合索引比单个索引的性价比更高。例如,创建【A,B,C】这个联合索引,至关于创建了【A】,【A,B】,【A,B,C】这三个索引。这就要求咱们尽可能的扩展索引而不是新建索引,具体状况还需具体分析。
四、频繁进行查询的字段应该新建索引,与其余表进行关联的字段能够考虑新建索引,查询中排序的字段能够考虑创建索引以提升排序的效率(这里举个例子,不少时候查询记录但愿按照建立时间倒序返回,一般有人会这样作order by create_time desc,可是若是create_time不是索引,而这个表有自增主键id,那么order by id desc返回结果同样,可是效率会提升)。
一、硬件问题:如网络速度慢,内存不足,I/O吞吐量小,磁盘空间满了等。
二、没有使用索引或者索引失效。
三、数据过多(分库分表)。
四、服务器或参数设置不当。
一、先观察,开启慢查询日志,设置相应的阈值(好比超过3秒就是慢sql),再生产环境跑个一天,看看哪些sql比较慢。
二、explain和慢sql分析,好比sql语句写的很差,没有使用索引或者索引失效,或者sql语句太过复杂,关联查询和嵌套子查询太多等等。
三、Show Profile是比explain更近一步的执行细节,能够查询到执行每个SQL都干了什么事,这些事分别花了多少秒。
四、找DBA或者运维对Mysql进行服务器的参数调优。
CREATE TABLE `user_info` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL DEFAULT '',
`age` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `name_index` (`name`)
)ENGINE = InnoDB DEFAULT CHARSET = utf8;
INSERT INTO user_info (name, age) VALUES ('xys', 20);
INSERT INTO user_info (name, age) VALUES ('a', 21);
INSERT INTO user_info (name, age) VALUES ('b', 23);
INSERT INTO user_info (name, age) VALUES ('c', 50);
INSERT INTO user_info (name, age) VALUES ('d', 15);
INSERT INTO user_info (name, age) VALUES ('e', 20);
INSERT INTO user_info (name, age) VALUES ('f', 21);
INSERT INTO user_info (name, age) VALUES ('g', 23);
INSERT INTO user_info (name, age) VALUES ('h', 50);
INSERT INTO user_info (name, age) VALUES ('i', 15);
CREATE TABLE `order_info` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`user_id` BIGINT(20) DEFAULT NULL,
`product_name` VARCHAR(50) NOT NULL DEFAULT '',
`productor` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)
)ENGINE = InnoDB DEFAULT CHARSET = utf8;
INSERT INTO order_info (user_id, product_name, productor) VALUES (1, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (1, 'p2', 'WL');
INSERT INTO order_info (user_id, product_name, productor) VALUES (1, 'p1', 'DX');
INSERT INTO order_info (user_id, product_name, productor) VALUES (2, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (2, 'p5', 'WL');
INSERT INTO order_info (user_id, product_name, productor) VALUES (3, 'p3', 'MA');
INSERT INTO order_info (user_id, product_name, productor) VALUES (4, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (6, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (9, 'p8', 'TE');
复制代码
执行explain看看,索引使用状况在possible_keys、key和key_len这三列。
select_type总共有如下几种类型:
一、SIMPLE:表示查询不使用UNION或子查询
二、PRIMARY:表示此查询是最外层的查询
三、SUBQUERY:表示此查询是第一个查询
四、UNION:表示此查询是UNION第二或随后的查询 五、DEPENDENT UNION:UNION中的第二个或后面的查询语句,取决于外面的查询 六、UNION RESULT:UNION的结果 七、DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询,即子查询依赖于外面查询的结果 八、DERIVED:衍生,表示导出表的SELECT
id=1的table derived2表示是由id=2的u和o衍生出来的
常见的索引原则: