开门见山,直接上图,下面的思惟导图便是如今要讲的内容,能够先有个印象~ java
首先不谈Mysql怎么实现索引的,先马后炮一下,若是让咱们来设计数据库的索引,该怎么设计?mysql
咱们首先思考一下索引到底想达到什么效果?其实就是想可以实现快速查找数据的策略,因此索引的实现本质上就是一个查找算法。算法
可是跟普通的查找有所不一样,由于咱们的数据有一下特征:sql
1.存储的数据是很是很是多的
2.而且还不断的动态变化数据库
因此实现索引时须要考虑到这两个特色。咱们须要找一个最合适的数据结构算法来实现查找功能。缓存
下面一块儿看下常见的查找策略,以下图:bash
因为前面说的两个特色咱们首先排除静态查找的算法。数据结构
至于查找树,咱们有二叉树和多叉树两种选择:函数
二叉树:若是选择二叉树的话,因为咱们的数据量庞大,二叉树的深度会变得很是大,咱们的索引树会变成参天大树,每次查询会致使不少磁盘IO。性能
多叉树:多叉树解决了了树的深度大的问题,那么咱们到底选择B树仍是B+树呢?
B树 摘自维基百科 zh.wikipedia.org/wiki/B%2B树
B+树 摘自维基百科 zh.wikipedia.org/wiki/B%2B树
从上面图可知B+树的叶子节点存放了全部的索引值,而且叶子结点之间以链表的形式相互关联,因此咱们只需从最左的链表遍历的话便可查找全部的值,最多见的用途就是范围查找,而B树则不知足这范围查找,又或者说实现特别复杂,因此Mysql最终选择了使用B+树实现这一功能。
先说明一下,虽然叫在Mysql官方叫作B-Tree索引,但采用的是B+树数据结构。
B-tree索引可以加快访问数据的速度,不须要进行全表扫描,而是从索引树的根节点层层往下搜索,在根节点存放了索引值和指向下一个节点的指针。
下面看下单列索引的数据怎么组织的。
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
key(`uid`)
);
复制代码
上面User 表给uid列建立了一个索引,那么往表里插入uid(96~102)的时候存储引擎是怎么管理索引的呢?看下面的索引树
1.在叶子节点存放全部的索引值,非叶子节点值是为了更快定位包含目标值的叶子节点
2.叶子节点的值是有序的
3.叶子节点之间以链表形式关联
下面在看一下多列(联合)索引的数据怎么组织的。
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
key(`uid`,`name`)
);
复制代码
给User 表建立了联合索引 key(uid
,name
) 这种状况下他的索引树是以下图所示。
特色跟单列索引同样,不一样之处在于他的排序,若是第一个字段相同时会按第二个索引字段排序
如何经过B-tree快速查找数据?
对于InnoDb 存储引擎的B-tree索引,会按一下步骤经过索引找到行数据
对于MyISAM 存储引擎的B-tree索引,会按一下步骤经过索引找到行数据
哈希索引是基于哈希表来实现的,只有精确匹配全部的全部列才能生效。
也就是说假设有个hash索引 key (col1,col2) 那么每次只有 col1和col2两个字段都用才可以生效。由于生成hash索引的时候是根据一个hash函数对全部的索引列取hash值来实现的。
以下方图,有个hash索引key(name)
当咱们执行 mysql> select * from User where name='张三';
时怎么利用hash索引快速查找的?
主键索引
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
primary key(`uid`)
);
复制代码
主键索引是惟一的,一般以表的ID设置为主键索引,一个表只能有一个主键索引,这是他跟惟一索引的区别。
惟一索引
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
unique key(`name`)
);
复制代码
惟一索引主要用于业务上的惟一约束,他跟主键索引的区别是,一个表能够有多个惟一索引
单列索引
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
key(`name`)
);
复制代码
以某一个字段为索引
联合索引
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
key(`name`,`uid`)
);
复制代码
两个或两个以上字段联合组成一个索引。使用时须要注意知足最左匹配原则!
还有其余不经常使用的就不介绍了~
什么是聚簇索引?
聚簇索引指的是他的 索引和行数据 在一块儿存储。也就是在一颗B+树的叶子结点上存储的不只是他的索引值,还有对应的某一行的数据。待会儿看图便知。
聚簇索引不是一种索引,而是一种数据存储组织方式 !!!
crreate table test(
col1 int not null,
col2 int not null,
PRIMARY KEY(col1),
KEY(col2)
);
复制代码
如上所示,表test 由两个索引,分别是主键 col1 和 普通索引 col2。那么这俩索引跟聚簇非聚簇有啥关系呢?
会生成一个聚簇索引和一个非聚簇索引(二级索引),也就是说会组织两个索引树。主键索引会生成聚簇索引的树 以及以col2为索引的非聚簇索引的树。
InnoDb 将经过主键来实现聚簇索引 ,若是没有主键则会选选一个惟一非空索引来实现。若是没有惟一非空索引则会隐式生成一个主键。
下面看下聚簇索引和非聚簇索引在索引树上数据是怎么分布的,图片摘自《高性能Nysql》
下图是聚簇索引的数据组织方式。 col1为主键索引的聚簇索引树
索引列是主键 col1
下图是非聚簇索引(二级索引)的数据组织方式。
索引列是 col2
与聚簇索引不一样的是非聚簇索引在索引树叶子节点上除了索引值以外只存了主键值。而聚簇索引则存了一行数据。
假若有一条sql 语句 select * from test where col2=93;
上面这条语句会经历两次从索引树查找过程
1.第一步从非聚簇索引的索引树上找到包含col2=93的叶子节点,并定位到行的主键 3
2.第二步 根据主键 3 在从聚簇索引定位包含 主键=3的叶子节点并返回所有行数据。
以上说的都是基于InnoDb存储引擎的,MyISAM是不支持聚簇索引的,由于他的数据文件和索引文件是相互独立存储的 MyISAM存储引擎的索引树的叶子节点不会寸主键值,而存一个指向对应行的地址或者说是指针,而后再从表数据文件里去找,以下面图所示。
结论:
聚簇索引:
一般由主键或者非空惟一索引实现的,叶子节点存储了一整行数据
非聚簇索引:
又称二级索引,就是咱们经常使用的普通索引,叶子节点存了索引值和主键值,在根据主键从聚簇索引查
覆盖索引就是指索引包含了全部须要查询的字段。
create table User(
`name` varchar(50) not null,
`uid` int(4) not null,
`gender` int(2) not null,
key(`uid`,`name`)
);
复制代码
假如表 User有三个字段 User (name,uid,gender),且有个联合索引 key(name,uid)那么 执行以下面这条sql查询时就用到了 覆盖索引。
select name,uid from User where name in ('a','b') and uid >= 98 and uid <=100 ;
上面这条sql语句使用了联合索引 key(name,uid),而且只需查找 name,uid两个字段,因此使用了覆盖索引。覆盖索引有什么好处呢?先看一下下面这个图
覆盖索引好处
1.避免了对主键索引(聚簇)的二次查询
2.因为不须要回表查询(从表数据文件)因此大大提高了Mysql缓存的负载
总之大大提高了读取数据的性能
最后在讲讲使用索引过程当中的避坑指南
独立的列
独立的列不是指单列索引,而是指索引列不能是表达式的一部分或者是函数的一部分。
select * FROM test where col1 + 1 =100; // 不能是表达式一部分
select * FROM test where ABS(col1) =100; // 不能是函数一部分
最左匹配原则
假若有个联合索引 key (col1,col2)。那么如下查询是索引无效的
select * from test where col2 = 3;
select * from test where col1 like '%3';
对于最左匹配原则,你们想一下B+树的叶子节点的关联就差很少知道为啥须要最左匹配原则了,由于B+的叶子结点,从左到右以链表的形式关联的,索引咱们查询的时候要么范围查询,要么有明确的左边一个开始的索引值,不能跳过或者不明确如 like '%XYZ'这种查询。
索引值不能是null值
单列索引有null值会致使索引无效
多列索引只要有个列有null值会致使索引无效
使用聚簇索引和覆盖索引大大提高读取性能
由于聚簇索引和覆盖索引的索引树上就有了须要的字段,因此不须要回表文件查询,因此提高了查询速度
使用短索引
若是很长的字符串进行查询,只需匹配一个前缀长度,这样可以节省大量索引空间
关注公众号每周更新2篇文章,追求的是质量而不是数量
欢迎加群一块儿讨论MySQL,Spring , java等。