说起来,InnoDB管理磁盘空间最小单位是页(Page),通常16 KB那么大。这个PAGE里包含很多类型,不过咱们最常打交道的就是那种存放表数据的Data Page。后续提到的数据页,默认都是这种类型。 那这些Data Page里面的布局是怎么回事呢? 当新页刚生成的时候,那是干干净净的。Free Space占据着整个空间,根本没有User Records这个区域。每当有新记录要插进来,系统就得从Free Space里划出一块地方给它。要是Free Space全都用光了,这就意味着PAGE满了,再要加新数据就得找新PAGE了。说白了,Free Space就是这个页面的生长线,填满了就必须再申请新页来扩容。 为了能在那么多数据里头快速找到最小或者最大的主键值,InnoDB在页面头部插入了两个特殊的记录:Infimum和Supremum。Infimum的下一个记录就是当前页面里主键最小的那条真记录;Supremum的上一个记录就是主键最大的那条真记录。这俩“哨兵”帮咱们在遍历的时候不至于跑飞了。 每条记录开头的信息里头都有一个叫next_record的字段,它指向的是下一条记录在页面内的偏移量。靠着这些next_record字段,所有记录就串成了一条单向链表。这样一来,按照主键顺序遍历整页的速度就很快了。要是有数据被删掉了,只是在delete_mask字段里打个标记,并不会立即回收空间。等到有新记录要插入的时候才会把那个旧槽位复用起来。 面对乱七八糟的User Records区域,InnoDB会把它们切成若干组。每组最后一个记录的位置会存进Page Directory这个目录里。要查数据的时候呢?先在Page Directory里头用二分法找出目标所在的组,然后顺着组内记录的next_record一条条往下找。这两步下来平均查找时间能降到对数级别。 再说数据页跟数据页之间的关系吧。File Header里的FIL_PAGE_PREV和FIL_PAGE_NEXT字段分别指向上一页和下一页的编号。所有类型是FIL_PAGE_INDEX的数据页就这样连成了一条双向链表。虽然物理硬盘上不一定挨着存放,但逻辑上是一环扣一环的。插入、删除、合并操作都可以在这链表里轻松移动。 最后为了确保内存里的东西能完整写到磁盘上,InnoDB在页面尾部追加了校验和和LSN值。写入前先算一遍校验和和目录校验和,写完后再核对一次。如果首尾校验和对不上或者LSN值不匹配,就说明写坏了得回滚对应范围的操作。双重保障能把物理损坏的概率压得极低。