innodb 数据页的事,这16 kb的小盒子里藏着不少门道

咱们聊聊InnoDB数据页的事,这16 KB的小盒子里藏着不少门道。首先得搞懂InnoDB的管理方式,它把磁盘空间分成一个个16 KB的Page(页),这就是最基本的存储单位了。虽然有好几种不同的页,但我们平时打交道最多的还是那种存放表行记录的Data Page。 新页刚生下来的时候,其实里面啥都没有,全是Free Space。每次往里面塞一条记录,系统就会从Free Space里切出一块地方来放User Records。要是Free Space被填得满满当当了,说明这页已经写满了,这时候再往里加记录就得去申请新页了。说白了,Free Space就像个生长线,一旦它被填满,扩容就得靠分配新页。 为了在一堆记录里快速找到最小和最大的主键,InnoDB在页头设置了两个虚拟的“哨兵”——Infimum和Supremum。Infimum的下一条就是这页里主键最小的用户记录,Supremum的上一条就是主键最大的记录。这两个哨兵让遍历操作有了边界,不用傻乎乎地把整页都翻一遍。 每条记录的头部都有个next_record字段,里面存的是下一条记录在页内的位置偏移。所有记录就通过这个字段连成了一串单向链表。想按主键顺序扫描整页?顺着这个链表走一圈就行。至于删除操作嘛,系统其实是先在delete_mask字段打个标记(0代表活着,1代表已经死了),真正把空间腾出来要等到下次插入的时候才能进行。旧的坑位可以被新记录直接填上用,空间回收细到了单条记录这么细的粒度。 面对“乱糟糟”的User Records,InnoDB先把它们分成了好几组,每组最后那个记录的位置偏移都记在Page Directory里。查东西的时候,先去Page Directory用二分法定位到大概的槽位(位置),再顺着槽里的next_record遍历这一组。这么两步走下来,平均查找时间就降到了对数级别。就算页里的记录翻一倍,性能下降也没多少。 至于页和页之间是怎么连起来的呢?靠的是File Header里的FIL_PAGE_PREV和FIL_PAGE_NEXT字段。这俩字段分别指向上一页和下一页的页号。所有的Data Page通过这两个字段连成了一个双向链表。在物理磁盘上它们不一定挨着住,但在逻辑上却是一环扣一环。插入、删除、合并操作都能在链表上顺畅地移动,不用把所有数据都重新排列一遍。 最后为了保证数据能准确地从内存写到磁盘上不丢不损,InnoDB在页尾加了个File Trailer做校验。写之前算一遍页面内容和目录的校验和,写完再算一遍比对一下;要是首部和尾部的校验和对不上号,或者LSN值不一致,就说明同步失败了,系统会把这部分范围给回滚掉。有了这双重校验把关,物理损坏的概率被压得特别低。