5). 行
InnoDB 存储引擎是面向行的(row-oriented),也就是说数据是按行进行存放的,每个页存放的行记录也是有硬性定义的,最多允许存放 16KB/2-200 行,即 7992 行记录。
1). 介绍
由于日常的DML语句操作时,首先操作的是缓冲池,并没有直接写入到磁盘,这有可能会导致内存中的数据与磁盘中的数据产生不一致的情况,而与磁盘中数据不一致的页我们成为"脏页"。 而checkpoint的工作,就是将内存中的脏页,在一定条件下刷新到磁盘。
如果在从缓冲池将页数据刷新到磁盘的过程中发生宕机,那么数据就无法恢复了;为了避免这种情况的发生,采用了Write Ahead Log(WAL)策略,即当事务提交时,先写重做日志(redo log),再修改缓冲池数据页,最后通过Checkpoint刷新到磁盘(事务提交会触发checkpoint)。这样正在执行的事务,因为存在日志都可以被恢复,没有日志的事务还没有执行也不会丢失数据。
2). 作用
A. 缩短数据恢复时间
当数据库发生宕机时,数据库不用重做所有的日志,因为Checkpoint之前的页都已经刷新会磁盘了,故数据库只需要重做Checkpoint之后的日志就好,这样就大大缩短了恢复时间。
B. 缓冲池不够用时,需要先将脏页数据刷新到磁盘中;
当缓冲池不够用时, 根据LRU算法溢出最近最少使用的页, 如果此页是脏页,则强制执行Checkpoint, 刷新脏页到磁盘。
C. 重做日志不可用时,刷新脏页到磁盘;
redo log大小是固定的, 当前的InnoDB引擎中, 重做日志的设计都是循环使用的,并不是无限增大的。重做日志可以被重用的部分是已经不再需要的, 数据库发生宕机也不需要这部分的重做日志,因此可以被覆盖使用, 如果此时重做日志还需要使用,那么必须强制执行Checkpoint,将缓冲池中的页至少刷新磁盘, checkpoint移动到当前重做日志的位置。
3). 分类
A. Sharp Checkpoint
Sharp Checkpoint 发生在数据库关闭时,将所有的脏页都刷新回磁盘,这是默认的工作方式,参数:innodb_fast_shutdown=1。
B. Fuzzy Checkpoint
在InnoDB存储引擎运行时,使用Fuzzy Checkpoint进行页刷新,只刷新一部分脏页。
插入缓冲是InnoDB存储引擎关键特性中最令人激动的。
主键是行唯一的标识符,在应用程序中行记录的插入顺序一般是按照主键递增的顺序进行插入的。因此,插入聚集索引一般是顺序的,不需要磁盘的随机读取。因此,在这样的情况下,插入操作一般很快就能完成。
但是,不可能每张表上只有一个聚集索引,在更多的情况下,一张表上有多个非聚集的辅助索引(secondary index)。比如,我们还需要按照name这个字段进行查找,并且name这个字段不是唯一的, 这样的情况下产生了一个非聚集的并且不是唯一的索引。在进行插入操作时,数据页的存放还是按主键id的执行顺序存放,但是对于非聚集索引,叶子节点的插入不再是顺序的了。这时就需要离散地访问非聚集索引页,插入性能在这里变低了。然而这并不是这个name字段上索引的错误,因为B+树的特性决定了非聚集索引插入的离散性。
InnoDB存储引擎开创性地设计了插入缓冲,对于非聚集索引的插入或更新操作,不是每一次直接插入索引页中,而是先判断插入的非聚集索引页是否在缓冲池中。如果在,则直接插入;如果不在,则先放入一个插入缓冲区中,好似欺骗数据库这个非聚集的索引已经插到叶子节点了,然后再以一定的频率执行插入缓冲和非聚集索引叶子节点的合并操作,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对非聚集索引执行插入和修改操作的性能。
两次写当数据库写物理页时,如果宕机了,那么可能会导致物理页的一致性被破坏。
可能有人会说,重做日志不是可以恢复物理页吗?实际上是的,但是要求是在物理页一致的情况下。
也就是说,如果物理页完全是未写之前的状态,则可以用重做日志恢复。如果物理页已经完全写完了,那么也可以用重做日志恢复。但是如果物理页前面2K写了新的数据,但是后面2K还是旧的数据,则种情况下就无法使用重做日志恢复了。
这里的两次写就是保证了物理页的一致性,使得即使宕机,也可以用重做日志恢复。
在写物理页时,并不是直接写到真正的物理页上去,而是先写到一个临时页上去,临时页写完后,再写物理页。这样一来:
A. 如果写临时页时宕机了,物理页还是完全未写之前的状态,可以用重做日志恢复
B. 如果写物理页时宕机了,则可以使用临时页来恢复物理页
InnoDB中共享表空间中划了2M的空间,叫做double write,专门存放临时页。
InnoDB还从内存中划出了2M的缓存空间,叫做double write buffer,专门缓存临时页。
每次写物理页时,先写到double write buffer中,然后从double write buffer写到double write上去。最后再从double write buffer写到物理页上去。