专栏持续更新中:MySQL详解
周末没啥计划,把大佬的<MySQL是怎样运行的>又翻了出来,重新学习一下页的概念。
页这个东西,看起来不怎么显眼,但是深层的东西都会碰到他,又爱又恨,逼着人必须弄懂。
高并发里面有一种提高性能的思路是 :通过批处理一次性处理大量数据,避免频繁的网络流量和IO。
MySQL 的页就是基于这种概念,磁盘是存放数据的载体,而数据处理会发生了内存中,所以流程大致分为:
页的好处
这里比较模糊的是为什么要衍生出一个页,而不是通过行级别进行处理。
页的概念与索引关联的概中主要包括 :
插入数据带来的结构变化
其中主要的参数是 :
我们或多或少都接触过数组或者集合,对于数组的查询方式有很多,正序或者逆序,或者效率更高的二分法
前提 : MySQL 的数据按照行记录进行存储,在一个表中,行的数据是有序的
目录 :但是不论多么优良的算法,在大数据量的场景下,还是会有很高的性能损耗,而 MySQL 为了解决这种场景,采取的是目录的方式。 目录中通过槽和分组,得到了一个数据的精简模型,通过精简的数据快速查询对应的分组,再在分组里面进行循环查找
槽和分组
有个资料里面说的是一个数据行就对应一个槽,也有说多个记录一个槽,我这里倾向于后一种说法,即稀疏目录。
页目录存放了记录的相对位置,每个相对位置即为一个槽,在InnoDB 里面是使用稀疏目录 (sparse directory), 即一个槽会属于多个记录 (4-8条)
- 查询数据时,首先通过二分法在页目录中进行查询
- 当查询到分组范围后,再通过分组里面的 next\_record 查询具体的数据
除了这些页,InnoDB 中还有存放表空间头部信息的页,Buffer 页等。
CREATE TABLE my_table (...) ENGINE = InnoDB ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8 PAGE_COMPRESSED=1 PAGE_SIZE=64K;
总结 :提高效率,但是增加了系统负载。
一个列里面的数据行之间通过 next_record 形成的单向链表
上文说到了每个数据行上面会有个 next_record 参数,该参数记录了真实数据达到下一条记录的真实数据的偏移量,这里有几点值得注意 :
不同数据页之间组成的双向链表
上面的结构图看过了,每个页里面都会包含 File Header 和 Page Header 两个对象。
而双向成方式不言而喻,都知道上页 (FIL_PAGE_PREV) 和 下页(FIL_PAGE_NEXT)的页号了,那访问完全没问题了 , 由于都只存了上一个和下一个,也就形成了标准的链表结构。 补充 : 上面看到的这种通常是指 LRU 链表,还有一个双向链表是 Flush List (刷新链表),这个链表是在数据页发生修改后,使用刷新链表可以让数据按照一定的顺序刷新到磁盘上
在这种场景下,会触发页分裂 ,此时 InnoDB 会执行下列操作 :
这里由于页是双向链表进行的关联,所以插入并不会对数据结构进行大的破坏,只需要对应的上下页进行更新就行了。
既然会有页分裂,那就有可能会出现分裂的页不均衡的情况,长时间下去,就会形成很多空闲块,这样的结构也是不合理的,不仅会占用不必要的空间,还会导致查询性能降低。
为了避免这些问题,InnoDB 会有页合并的功能 , 原理和上面的类型。相邻页尝试合并,然后重新更新引用和索引。
之前看到了数据被删除后,其目录数据里面的 delete_mask 会被置为已删除。
此时的数据处在逻辑删除的状态,通过上面说的 next_record (下一记录的相对位置)指向后续存在的正常数据。
这样做的目的主要是避免碎片,提高删除的性能(只需要修改标识和引用),同时保证了删除的事务。
但是长此以往就会有大量的删除数据占用空间,为了避免这种情况,InnoDB 会定期的进行清理,同时重新整理数据页。
- 当获得槽号后,就直接通过槽号读取想要的数据,并且返回
页和索引是相辅相成的,如果没有索引,页就需要在单向链表里面向下寻找,直到找到对应的数据
页是存储的基础,也是索引的基础,了解了页后面就可以深入的了解索引了。
这一块没了解太深,毕竟这东西其实我应用的场景几乎没有,主要是不弄清楚后面读起来很难受。
尽量做到了自己去输出东西,整理了一些问题,但是毕竟站在别人修好的路上面,有些东西不能保证一定是对的,也有可能是我理解有误,如果有问题建议去看原文或者官方文档。
头部信息对于我们日常业务中几乎是没太大用的,这里只记录几个我认为和上文有一定关联的参数 :
- PAGE\_N\_DIR\_SLOTS : 页目录中的槽数量
- PAGE\_N\_HEAP : 本页中的记录数量
- PAGE\_GARBAGE : 已删除记录中的字节数
- PAGE\_LAST\_INSERT :最后插入记录的位置
- PAGE\_DIRECTION :记录插入的方向
- PAGE\_N\_RECS : 该页中记录的数量
- PAGE\_LEVEL : 当前页在 B+树中所处的层级
- PAGE\_INDEX\_ID : 索引ID文件头部信息
- FIL\_PAGE\_OFFSET : 页号
- FIL\_PAGE\_PREV : 上一个页的页号
- FIL\_PAGE\_NEXT : 下一个页的页号
- FIL\_PAGE\_ARCH\_LOG\_NO\_OR\_SPACE\_ID : 页属于哪个表空间
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!