1.索引
首先查看查询条件是否命中某条索引,符合则通过索引查找相关数据,如果不符合则需要全表扫描,即需要一条一条地查找记录,直到找到与条件符合的记录。
磁盘I/O非常耗时,
- 磁盘磁头的寻道时间速度慢,费时。
1.1 索引优缺点
优点
- 提高检索效率,降低io成本;加速表和表之间的连接。
缺点 - 创建索引和维护索引要耗费时间。
- 索引占用磁盘空间
- 降低表更新的速度。
提示: 在 突发 插入频繁的情况下,由于索引可以提高查询的速度,但是会影响插入记录的速度。这种情况下,可以先先删除表中的索引,然后插入数据,插入完成后再创建索引。
2.索引的演进
2.1 compact行格式 简化
compact行格式来实际存储数据库每一行的数据。简化版本 compact行格式
record_type
- 记录头信息的一项属性 表示行格式记录类型
- 0 普通记录,2 最小记录 3最大记录 1 暂时还没用过
next_record
- 记录头信息的一项属性,表示下一条地址相对于本条记录的地址偏移量
用户记录 各个列的值
其他信息
2.2 记录放到页里的示意图
3. 简单设计
所以如果我们想快速的定位到需要查找的记录在哪些数据页中该咋办?
我们可以为快速定位记录所在的数据页而建立一个目录。 目录页
3.1 下一个id >上一页
下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值。
记录移动
另外,页10中用户记录最大的主键值是5,而页28中有一条记录的主键值是4,因为5>4,所以这就不符合下一个数据页中用户记录的主键值必须大于上一个页中用户记录的主键值的要求,所以在插入主键值为4的记录的时候需要伴随着一次记录移动,也就是把主键值为5的记录移动到页28中,然后再把主键值为4的记录插入到页10中,这个过程的示意图如下:
页分裂 两种情况
- 一条记录太大,一个页存不下,溢出的会存到下一页。这是页的分裂。
- 通过记录移动的操作来始终保证 下一页的用户记录主键 大于 上一页的主键值, 这个过程称为 页分裂。
3.2 给所有的页建立一个目录项。
所以如果想从这么多页中根据主键值快速定位某些记录所在的页,我们需要给它们做个目录,每个页对应一个目录项,每个目录项包括下边两个部分:
- key 页的用户记录中 最小的键值。
- 页号,page_no
4 innodb 索引设计方法
自己补充:在数据页中记录和记录之间是单向链表,在数据页之间是双向链表。
4.0 目录项目记录页 和 页目录 目录页
4.1 演进1 设计目录项目记录的页
灵活管理所有目录项的方式
目录项中的两个列是主键和页号而已,为了和用户记录做一下区分,我们把这些用来表示目录项的记录称为目录项记录
那InnoDB怎么区分一条记录是普通的用户记录还是目录项记录呢?
记录头信息里的record_type属性
- 0 普通用户记录
- 1 目录项记录
- 2 最小记录
- 3 最大记录
相同点
两者用的是一样的数据页,都会为主键值生成 Page Directory (页目录),从而在按照主键值进行查找时可以使用 二分法 来加快查询速度。
4.2 演进2 多个目录项记录的页
如果表中的数据太多,以至于一个数据页不足以存放所有的目录项记录,如何处理呢?
设一个存储目录项记录的页最多只能存放4条目录项记录
所以如果此时我们再向上图中插入一条主键值为320的用户记录的话,那就需要分配一个新的存储目录项记录的页:
4.3 演进3 目录项记录页的目录页
如图,我们生成了一个存储更高级目录项的 页33 ,这个页中的两条记录分别代表页30和页32,如果用户记录的主键值在 [1, 320) 之间,则到页30中查找更详细的目录项记录,如果主键值 不小于320 的话,就到页32中查找更详细的目录项记录。
5. 索引概念
5.1聚簇索引
所有的用户记录都存储在了叶子节点
索引即数据,数据即索引
所有完整的用户记录都存放在这个聚簇索引的叶子节点处。这种聚簇索引并不需要我们在MysQL语句中显式的使用·INDEX·语句去创建,InnoDB存储引擎会自动的为我们创建聚簇索引。
限制
- 对于MYSQL数据库目前只有InnoDB数据引擎支持聚簇索引,而MylSAM并不支持聚簇索引。
- 由于数据物理存储排序方式只能有一种,所以每个MysQL的表只能有一个聚簇索引。一般情况下就是该表的主键。
- 如果没有定义主键,Innodb会选择非空的唯一索引代替。如果没有这样的索引,Innodb会隐式的定义一个主键来作为聚簇索引。
- 为了充分利用聚簇索引的聚簇的特性,所以innodb表的主键列尽量 选用有序的顺序id
5.2 非聚簇索引
回表
回表 我们根据这个以c2列大小排序的B+树只能确定我们要查找记录的主键值
5.3 联合索引
比方说我们想让B+树按照 c2和c3列 的大小进行排序,这个包含两层含义:
- 把各个记录和页按照c2列进行排序
- 在记录的c2列相同的情况下,采用c3列进行排序
5.4 InnoDB的B+树索引的注意事项
1. 根页面位置万年不动
2.内节点中的目录项记录的唯一性
主键值也添加到二级索引内节点中的目录项记录了,能保证B+树每一层节点中各条目录项记录除页号这个字段外是唯一的,
问题
c2 都相同,无法确定插入的时那个页。
为了让新插入记录能找到自己在那个页里,我们需要保证在B+树的同一层内节点的目录项记录除页号这个字段以外是唯一的。所以对于二级索引的内节点的目录项记录的内容实际上是由三个部分构成的:
- 索引列的值
- 主键值
- 页号
这样我们再插入记录(9,1, ‘c’)时,由于页3中存储的目录项记录是由c2列+主键+页号的值构成的,可以先把新记录的c2列的值和页3中各目录项记录的c2列的值作比较,如果c2列的值相同的话,可以接着比较主键值,因为B+树同一层中不同目录项记录的c2列+主键的值肯定是不一样的,所以最后肯定能定位唯一的一条目录项记录,在本例中最后确定新记录应该被插入到页5中。
3. 一个页面最少存储2条记录
6. myisam 索引结构
这里设表一共有三列,假设我们以Col1为主键,上图是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MylISAM中,主键索引和二级索引(Secondary key)在结构上没有任何区别,只是主键索引要求key是唯一的,而二级索引的key可以重复。
,MyISAM中索引检索的算法为∶首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录
7.小问题
7.1 Hash结构效率高,那为什么索引结构要设计成树型呢?
- 原因1: Hash索引仅能满足(=) (<>)和IN查询。如果进行范围查询,哈希型的索引,时间复杂度会退化为o(n);而树型的“有序”特性,依然能够保持o(log2N)的高效率。
- 原因2: Hash索引还有一个缺陷,数据的存储是没有顺序的,在ORDER BY的情况下,使用Hash索引还需要对数据重新排序。
- 原因3∶对于等值查询来说,通常Hash索引的效率更高,不过也存在一种情况,就是索引列的重复值如果很多 效率就会降低。这是因为遇到Hash冲突时需要遍历桶中的行指针来进行比较,找到查询的关键字,非常耗时所以,Hash索引通常不会用到重复值多的列上,比如列为性别、年龄的情况等。
- 原因4 不支持联合索引的 最左匹配查询原则。
7.2 自适应hash
InnoDB本身不支持 Hash索引,但是提供自适应 Hash索引(Adaptive Hash lndex)。什么情况下才会使用自适应Hash索引呢?
如果某个数据经常被访问,当满足一定条件的时候,就会将这个数据页的地址存放到Hash表中。这样下次查询的时候,就可以直接找到这个页面的所在位置。这样让B+树也具备了Hash索引的优点。
采用自适应 Hash 索引目的是方便根据 SQL 的查询条件加速定位到叶子节点,
特别是当 B+ 树比较深的时候,通过自适应 Hash 索引可以明显提高数据的检索效率。
7.3 btree
,即叶子节点和非叶子节点都存放数据。搜索有可能在非叶子节点结束
7.4 b+tree btree
B+ 树和 B 树的差异:
- 有 k 个孩子的节点就有 k 个关键字(也就是)。也就是孩子数量 = 关键字数,而 B 树中,孩子数量 = 关键字数
+1。 - 非叶子节点的关键字也会同时存在在子节点中,并且是在子节点中所有关键字的最大(或最小)。
- 非叶子节点仅用于索引,不保存数据记录,跟记录有关的信息都放在叶子节点中。而 B 树中, 非 叶子节点既保存索引,也保存数据记录 。
- 所有关键字都在叶子节点出现,叶子节点构成一个有序链表,而且叶子节点本身按照关键字的大小从小到大顺序链接。
7.4 区别 B+树的中间节点并不直接存储数据。区别
- 查询效率更稳定,一定在叶子节点找到数据。
- b+tree 查询效率更高,b+tree 比b-tree 阶数更大,深度更低。
因为b+tree 非叶子节点只存储索引值,和页号。 那么16k一页,能存储更多目录项记录数据。 而b-tree非叶子节点 存储数据,那么一页存储的目录项记录少了,深度增加。 - 范围查询上,b+tree 比b-tree 效率高
因所有关键字都在 叶子节点上,叶子节点之间又指针,数据递增,快。
7.5 小问题
7.5.1 为什么说b+tree 一般查找只需要1~3次
一页16k,主键int 4B,bigint 8b指针一般8B,那么一页能存 16kB/16B=1000条数据
深度为3的b+tree 又 103 *103 *103 =10一条
实际情况每个节点不可能填满,因此在数据库中 又2~4层,又因为innodb存储引擎的根节点常驻内存, 2-4 -1就是 1—3层 1—3次io