我们登上|

navyum

园龄:4个月粉丝:0关注:0

09.索引结构

基础:

  1. 索引中的数据页的编号不一定连续,也就是说这些页在存储空间里可能并不挨着
  2. B+树中相同层次的数据页通过页结构的File Header 中的FIL_PAGE_PREV和FIL_PAGE_NEXT,组成双向循环链表
  3. 每个数据页内部会维护页目录,通过分组的方式将所有用户记录分组
  4. 每个数据页内部记录之间通过next_record连接,next_record记录的是下一个记录的偏移位置
  5. 存储用户记录的页和存储索引的页结构FIL_PAGE_TYPE都是”FIL_PAGE_INDEX”
  6. 存储索引的页里面的记录叫做目录项记录,即索引的非叶子结点
  7. 存储用户记录的页里面的记录做用户记录,即索引的叶子节点
  8. 存储目录项记录页中的主键值最小的那个记录的min_rec_mask值为1

索引页的组织结构:

图片
  • 从图中可以看出:
    • 每个索引页最多存储4个,叶子节点最多存储3个记录(不包含infimum、supremum)
    • 索引页比数据页存的记录肯定要多很多
    • 数据插入:
      • 先找到最底层的复合条件的数据页,如果有空间,直接插入;如果没有空间,此时会发生页分裂。
    • 页分裂过程:
      • 创建新页:InnoDB会创建一个新的叶子节点页。
      • 重新分配记录:将当前页中的一半记录移动到新页中,确保两个页的空间使用均衡。
      • 更新双向循环指针:同层级前后页的指针进行调整
      • 更新父节点:在父节点中插入一个新的目录项记录,指向新创建的页,并更新原有页的目录项记录。
      • 递归处理:如果父节点也已满,页分裂会递归向上进行,直到根节点。如果根节点也需要分裂,会创建一个新的根节点,树的高度增加。
    • 页分裂举例:
      • 插入(101,0,x)
      • 定位到应该放在页9 中,但页9 满了,发生页分裂,将(100,9,x)和(101,0,x)放到新申请的页38中(同时需要更新页9 的NEXT和页20 的PREV指针)
      • 更新父节点页30,插入一条目录项记录(100,38),因为父节点也满了,此时父节点也进行页分裂,申请新 页49,将一半的目录项记录移动到新页中
      • 新的页49此时含有(100,38)、(209,20),更新页30的NEXT指针和页32的PREV指针

叶子结点的页内结构:

  • record_type=0
  • 存储:索引列、用户数据,构成一条用户记录 图片

非叶子结点的页内结构:

  • record_type = 1
  • 存储:索引列、页中最小记录的主键值、对应主键所在页号,构成一条目录项记录 图片

聚簇索引:

Img - 横向:根据主键值的大小进行页内记录排序、页间排序 1. 页内的记录是按照主键大小顺序排成单向链表 2. 存放用户记录的页根据页中用户记录的主键大小顺序排成一个双向链表 3. 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的主键大小顺序排成一个双向链表 - 竖向: 1. 目录项记录页中记录下一层级的目录项记录页号或者用户记录的页号 - 聚簇索引的叶子节点存储的是完整的用户记录,包括隐藏列 - 聚簇索引的非叶子结点存储的是主键+页号 - 聚簇索引就是innodb数据的存储方式,如果没有定义主键,则会使用其他的唯一非空索引或者row_id创建聚簇索引

二级索引(索引列c2):

Img - 横向:根据索引列的值大小进行页内记录排序、页间排序 1. 页内的记录是按照索引列c2顺序排成单向链表 2. 存放用户记录的页根据页内用户记录的索引列c2顺序排成一个双向链表 3. 存放目录项记录的页分为不同的层次,在同一层次中的页也是根据页中目录项记录的索引列c2顺序排成一个双向链表 - 竖向: 1. 目录项记录页中记录下一层级的目录项记录页号或者含有主键信息的页号 - 二级索引的叶子节点存储的是索引列c2主键值 - 二级索引的非叶子结点中存储的是索引列c2+页号(实际上如果是非唯一索引,为了保证每个目录项记录的唯一性,还会存储主键值) - 通过二级索引找到符合条件的主键值,需要再到聚簇索引中继续查找用户记录,这个过程称为回表

联合索引(索引列c2、c3):

  • Img
  • 联合索引也是二级索引
  • 横向:根据索引列的值大小进行页内记录排序、页间排序
    1. 记录先按照c2列的值进行排序,如果记录的c2列相同,则按照c3列的值进行排序
    2. 其他同二级索引
  • 竖向:
    1. 目录项记录页中记录下一层级的目录项记录页号或者含主键信息的页号
  • 连接规则同二级索引
  • 叶子节点节点存储的是c2c3和主键c1
  • 非叶子结点中存储的是索引列c2、c3 + 页号

InnoDB的B+树索引的注意事项:

  1. 根页面创建后万年不变:
    • B+树创建过程:
      1. 每当为某个表创建一个B+树索引(聚簇索引不一定需要人为创建)的时候,都会为这个索引创建一个根节点
      2. 最开始表中没有数据的时候,每个B+树索引对应的根节点页既没有用户记录,也没有目录项记录
      3. 随后向表中插入用户记录时,先把用户记录存储到这个根节点
      4. 根节点中的可用空间Free space用完,如果继续插入记录,此时会发生页分裂,将根节点中的所有记录复制到一个新分配的页。这时新插入的记录根据索引列的大小(聚簇索引的主键,二级索引的索引列)就会被分配到新的页中,而根节点便升级为存储目录项记录的页
      5. 特别注意的是:一个B+树索引的根节点自诞生之日起,便不会再移动。只要我们对某个表建立一个索引,那么它的根节点的页号便会被记录数据字典,然后凡是InnoDB存储引擎需要用到这个索引的时候,都会从数据字典取出根节点的页号,从而来访问这个索引。
  2. 内节点(非叶子结点)中的目录项记录需要唯一性保证:
    • 为了确保去除页号后的唯一性,非唯一索引的目录项记录存储了索引列、主键值、页号
    • 如果不唯一,则可能会存在插入记录时多个页都可选: 图片
    • 页3实际存储的是(1,1,4)、(1,7,5)
  3. 一个页面最少存储2条记录:

关于MyISAM:

  1. 索引特点:
    • MyISAM的索引方案也使用B+树,但是却将索引和数据分开存储,属于非聚簇索引
    • 把用户记录按照记录的插入顺序单独存储在一个文件中,称之为数据文件,文件包含行号+用户数据
      • 数据文件都是时间序,区别于innodb按照主键排序
    • 把索引信息存储到另一个称为索引文件的文件中,文件包含主键值 + 行号/数据偏移位置
  2. 查找过程:
    • 先通过索引文件中的索引找到对应的行号,再通过行号数据文件找对应的记录
    • MyISAM中建立的索引相当于全部都是二级索引
  3. MyISAM 数据文件: 图片
  4. 对比:
    1. 回表:
      • Innodb从二级索引找到主键,再通过聚簇索引找到对应用户记录
      • MyISAM从索引文件找到记录的偏移位置,再通过数据文件找到对应用户记录
    2. 数据:
      • InnoDB中的索引即数据,数据即索引(聚簇索引
      • 而MyISAM中却是索引是索引、数据是数据
  5. 适用于读多写少的场合、全文索引支持、支持压缩(偏向于ck)

其他存储引擎:

引擎 特点 适用场景
Innodb 支持事务 最常用
Myisam 支持压缩和全文索引 读多写少的大数据场合,更新效率低
Memory
RocksDB 高吞吐量和低延迟的读写操作,适合高并发和高性能 高性能键值存储的场景
TokuDB 高效的写入和压缩性能 适用于大数据量、高写入负载的应用
NDB 用于MySQL Cluster,提供高可用性和高可扩展性 高可用性和高扩展性的分布式系统
Archive

本文作者:navyum

本文链接:https://www.cnblogs.com/navyum/p/18509416

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   navyum  阅读(7)  评论(0编辑  收藏  举报
//自己上传到博客园的js
点击右上角即可分享
微信分享提示