原来知道有一些索引失效的条件,最近看了看mysql底层数据结构,明白了为何会失效 ,记录之。众所周知,经常使用的mysql数据引擎有两种,今天全是以InnoDB为基础开启探索之旅的,另外一种有时间再说吧。
咱们都知道,数据库数据是存在磁盘中的,不过真正处理数据是在内存中进行的。这就须要从硬盘上不断地把数据读到内存中,因为内存和磁盘速度差了好几个数量级,因此为了不频繁交互带来的性能问题,mysql一次会多读取一些,是多少呢?读一页。一页有16KB,也就是说一次读取通常都是16KB的倍数。页是硬盘内存交互的基本单位。mysql
咱们平时所说的一条记录叫数据行,InnoDB有四种不一样类型的数据行,Compact、Redundant、Dynamic和Compressed。主要介绍下Compactsql
为了方便后面说明,建个表:数据库
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL, `age` smallint(3) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8;
为了方便说明举例:数据结构
id name age 99 haha (null)
1 表示 这个属性为null,0表示这个属性不为null。因此name对应着0,age对应着1。因为是倒叙存放的,因此 Null值列表 这个地方存放的是 10(age,name);性能
数据有不少,关键的:
delete_mask:标记该记录是否被删除
record_type:表示当前记录的类型,0表示普通记录,1表示B+树非叶子节点记录,2表示最小记录,3表示最大记录。搜索引擎
接下来就是真实数据了,值得一提的是,还有三个隐藏项:spa
如上图所示,是一张数据页内部结构。一个16KB的页,内部存放着不少行,好比说那3条记录,除此以外,内部存放着两个特殊的记录,最小记录和最大记录。数据页内部记录之间是以单链表的形式存放的,头尾分别是那两个特殊的记录。在内存中有不少页,页和页之间是用双链表链接的。这样方便快速定位到数据在哪一页上。指针
ok,如今知道了数据页、数据行,和它们之间的数据结构以后,就能够看看咱们所谓的索引了。正如开头所说的,这边只介绍InnoDB的聚簇索引,另外一种搜索引擎,先不提它(嗯,如今甚至连名字都不提)。code
聚簇索引,就是说有一颗树,叶子节点就是真实数据行所构成的数据页。blog
通常为了搜索快一点,咱们主键都是自动生成的(例如我们的User表),因此最下面那层是根据id排序生成的。最底下那层的叶子节点是真实的数据,有4页,每页里面有一个单链表,就是咱们的真实数据行。第二行有两页,每页中也是有个数据行构成的单链表,这是的数据行只包含了页码(最底下那层某页)、某页最大id,因而可知,第二行比最底下那行页数少了不少不少。就这样,一层一层的抽取,必定会有一个所谓的跟页。咱们搜索数据就是从跟页开始的,一层一层往下找的。因为一个数据页能够存放16KB数据,因此三四层的树状图就已经能存放不少不少数据了,因此不要担忧树会很深。再强调一下,页内是单链表,同层的页和页之间是双链表。
上面那是以主键为搜索条件的索引,通常这棵树是自动生成的。
咱们每每还会本身创建索引,好比给age添加索引。与聚簇索引相似,只不过叶子节点存的不是全部数据(而且根据age大小排序),而是存的该age属性和主键id,非叶子节点寸的是页码和下面那层某页最大的age值。这样,你肯定了要搜的是哪些主键,还要回表(拿着这些主键回去聚簇索引找)去查询真实的数据。这边脑洞一下,即便你给age建立了索引,真正执行的时候,也不必定是经过查看二级索引,再回表的方式查数据(好比说经过二级索引搜索出来的是全部的id,再回表查询,得不偿失啊,还不如直接从聚簇索引直接去搜呢)。也可能根据聚簇索引直接搜索。具体采用哪一种方式mysql本身会评估。
还有种特殊的二级索引,联合索引,好比说给(name、age)添加联合索引,底层数据结构和普通二级索引没什么区别,只不过叶子节点存的不是全部数据(而且先根据name大小排序,name相同的状况下再根据age排序),而是存的该name、age属性和主键id,非叶子节点寸的是页码和下面那层某页最大的name值。因此若是搜索条件只有age,没有name的话,联合索引会失效,因此要遵循最左原则。