索引是mysql数据库中的一种数据结构,就是一种数据的组织方式,这种数据结构又称为key
表中的一行行数据按照索引规定的结构组织成了一种树型结构,该树叫B+树
优化查询速度
注意:只能加速索引字段
错误的认知
项目上线之后,运行了一段是时间,发现项目运行极卡
索引越多越好
索引的根本原理就是把硬盘的io次数降下来
一次磁盘io带来的影响
7200转/分钟
120转/s
慢在找的过程,读的过程是快的,(统称延迟时间)
一次io的延迟时间=平均寻道时间(大概需要5ms)+平均延迟时间(4ms)---》9ms
9ms对于一个人来说是很慢的,但是对于计算机来说是很长的,比如一台500 -MIPS 的机器每秒可以执行5亿条指令,应为指令是靠电的性质,换句话说就是执行一次io的时间可以执行450万条指令,数据动则十万百万乃至千万条数据,每次9ms的时间显然是一个灾难。
磁盘的预读
crate index xxx on user(id);
创建索引最好是以占空间小,重复度低的字段创建索引
innodb存储引擎默认的索引结构为B+ 树,而B+树是由二叉树、平衡二叉树、B树演变而来的。
二叉树——》平衡二叉树——》B 树 ——》B + 树
基本概念:
leaf node:叶子节点
non-leaf node:根节点、树枝节点
二叉树:
二叉树有一个特点,它的左节点的 key 值小于当前节点的 key 值,而它的右节点 key 值大于当前节点 key 值。
创建二叉树索引
取每一条记录的id值作为key值,value为本行完整记录,构建了树型结构后,查找的速度根树的高度有关系。
平衡二叉树:
平衡二叉树又称为AVL树,指的就是左子树的高度与右子树的高度相差不超过1,如下图所示;
平衡二叉树相比于二叉树来说,查找效率更稳定,总体的查找速度也更快,但是并不是基于平衡二叉树构建索引就可以的。因为每个磁盘块只放一个节点,每个节点只放一组键值对,当数据过大的时候,二叉树的节点就会非常多,树的高度也会变高,查找的效率也会变低!
B 树:
就是构建一个单节点可以存储多个键值对的平衡树,就是B树。
B树相对于平衡二叉树,每个节点存储了更多的键值和数据,每个节点有更多的子节点,子节点的个数称为阶,上图就是一个3阶B树,高度也会很低,这样B树的查找磁盘次数也会很少,这样数据的查找效率就会比平衡二叉树高。
B+ 树:
B+ 树是对B树的进一步优化。
B+ 树和B 树有什么不同
B+ 树非叶子节点non-leaf node 上是不存储数据的,仅存储键,而B 树的非叶子节点中不仅存储键,也会存储数据。B + 树之所以这么做的意义在于;树一个节点就是一个页,而数据库中页的大小是固定的,innodb 存储引擎默认一页为16kb,所以在页大小固定的前提下,能往一个页中放入更多的节点,相应的树的阶数就会更大,那么树的高度必然更矮更胖,如此一来我们查找数据进行磁盘io次数又会再次减少,数据查询的效率也会更快。
B+ 树的阶数是等于键的数量的,列如我们的B+ 树中每个节点可以存储3个键,3层B+ 树可以存储3*3*3=9
个数据。所以如果我们的B+ 树一个节点可以存储1000个键值,那么3层B+ 树可以存储1000*1000*1000=10亿
个数据。而一般节点是常驻内存的,所以一般我们查找出10亿数据,只需要2次磁盘IO。
因为B+ 树索引的所有数据均存储在叶子节点leaf node ,而且数据是按照顺序排列的。那么B+ 树使得范围查找,排序查找,分组查找以及去重查找变得异常简单。而B 树因为数据分散在各个节点,要实现这一点是很不容易的。
而且B+ 树中各个页之间也是通过双向链表连接的,叶子节点找那个的数据是通过单向链表连接的。其实在B 树中我们也可以多各个节点加上链表。其实这些不是它们之间的区别,是因为在mysql的innodb存储引擎中,索引及时这样存储的。也就是说B+ 树索引就是innodb中 B+ 树索引真正的实现方式,准确的说应该是聚集索引。
在innodb中,我们通过数据页之间通过双向链表连接以及叶子节点中数据之间通过单向链表连接的方式可以找到表中所有的数据。
聚集索引、聚簇索引、主键索引:
以主键字段值为key构建的B+ 树,改B+ 树的叶子节点放的是主键值与本行完整的记录。
即:表中的数据都聚聚在叶子节点中,所以称之为聚集索引。
非聚集索引、非聚簇索引、辅助索引、二级索引:
以非主键字段值为key构成的B+ 树,改B+ 树的叶子节点放的是key与其主键对应的字段值。
补充:一张innodb 存储引擎中有且只能有一张聚集索引,但是可以有多个非聚集索引(辅助索引)
聚集索引负责聚集整张表的所有的数据,而辅助索引是专门提速来用
覆盖了索引:在命中了索引的基础上,只在本索引树的叶子节点就找到了我们想要的数据。
回表操作:在命中了辅助索引的基础上,在辅助索引的叶子节点并没有找想要的数据,需要拿到对应的主键字段值去聚集索引去找。
举例:假设我有一张表,我们这张表是以ID字段为基础创建的主键索引,我是以name字段创建的辅助索引
主键索引——》id字段
辅助索引——》name字段
select name, age, gender from user where name='yang'; 这个命中了辅助索引,那么就顺着索引树从根节点一路找下去,找到叶子节点后,叶子节点放的是 ‘yang’ 这个人名以及它的主键字段值 但是我要的是name age gender ,我要的不是id值和主键值,如果我要的是主键值和id值那么我就不需要去其他地方找了——》这就叫覆盖了索引。 因为在自己本索引树的,本索引树的就能找到自己想要的数据,就不需要去别的地方找 但是这并不是我想要的,那么我要拿着我 ‘yang’的主键字段值假如是3,就回过头继续去主键索引树根节点继续找到我想要的叶子节点的数据,那里面所有数据都有——》这就叫回表操作
提问:
1、命中了辅助索引的前提下能不能覆盖了索引?
可能是!向这种情况下就是select name, id from user where name='yang'; '
这就不是select name, age, gender from user where name='yang';
2、如果命中了主键索引是否覆盖了索引?
一定是!·select name, age, gender from user where id=3;
所以一个SQL语句查询的字段尽量不要想写什么就写什么甚至是 * ,要尽量覆盖了索引。
MySQL常用的索引分类
聚集索引:即主键索引,primary key 用途: 1.加速查找 2.约束(不为空,不能重复) 辅助索引: 唯一索引:unique 用途: 1.加速查找 2.约束(不能重复) 普通索引:index 用途: 1.加速查找
创建聚集索引
alter table 表名 add prmary key 表名(字段名); alter table 表名 drop primary key;——>删除
创建唯一索引
alter table 表名 add unique key 表名(字段名); 没有写索引名 show create table 表名; 查看 alter table 表名 drop index 索引名;——>删除
创建普通索引
创建表时 create table 表名( id int primary key auto_increment, class_name varchar(10) unique, name varchar(16), age int ); 创建表后 create index 索引名 on 表名(字段名); drop index 索引名 on 表名;——>删除
create index zz on t1(id, name,age) 例如:有这样的数据 id name age gender email 1 yang1 18 male yy@qq.com 2 yang2 28 female yy@qq.com 3 yang3 38 male yy@qq.com 4 yang4 48 female yy@qq.com 我现在对它们建联合索引,那么我每条记录提取的key就是 id,name,value对应的是该条记录,列如: 1,yang1,18 ————>对应 1 yang1 18 male yy@qq.com 2,yang2, 28 ————>对应 2 yang2 28 female yy@qq.com
那么,当初只有一个字段的时候比大小的时候很好比,那两个字段值怎么比大小?
1,yang1,18
2,yang2,28
注意这个和字符串比大小一样,从左到右一个一个比,第一个如果分胜负了,就没有必要在比联合的了第二个字段了
在查询条件中出现了id name,age字段那么就肯定能命中这个联合索引
但是必须带着id、例如:id name、id age、id age name
最核心的原理就是每次从最左边第一个位置开始比大小,通过最左边就能够缩小范围,这就是最左前缀匹配原则。
什么时候建立联合索引?
联合索引就只有一棵树,当不用联合索引聚集索引和辅助索引就需要建三棵树,这样就浪费了空间。
如果要平凡的用这几个字段来一起查询,就可以考虑建立联合索引,就一棵树。
但是注意在查询条件中一定要带有最左边的字段
总结:如果查询条件中涉及到多个字段值,这多个字段值有一个同性,大家都会带着某一个字段,那这时候就可以键联合索引。