MySQL之InnoDB引擎底层存储结构

InnoDB 记录存储结构和索引页结构

InnoDB 采取的方式是:将数据划分为若干个页,以页作为磁盘和内存之间交
互的基本单位,InnoDB 中页的大小一般为 16 KB。也就是在一般情况下,一次最
少从磁盘中读取 16KB 的内容到内存中,一次最少把内存中的 16KB 内容刷新到
磁盘中。
我们平时是以记录为单位来向表中插入数据的,这些记录在磁盘上的存放方
式也被称为行格式或者记录格式。InnoDB 存储引擎设计了 4 种不同类型的行格
式,分别是 Compact、Redundant、Dynamic 和 Compressed 行格式。

行格式

我们可以在创建或修改表的语句中指定行格式:
CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名称

  • COMPACT

    我们知道MySQL支持一些变长的数据类型,比如VARCHAR(M)、VARBINARY(M)、
    各种 TEXT 类型,各种 BLOB 类型,我们也可以把拥有这些数据类型的列称为变
    长字段,变长字段中存储多少字节的数据是不固定的,所以我们在存储真实数据
    的时候需要顺便把这些数据占用的字节数也存起来。如果该可变字段允许存储的
    最大字节数超过 255 字节并且真实存储的字节数超过 127 字节,则使用 2 个字节,
    否则使用 1 个字节。

    • InnoDB 表对主键的生成策略是

      优先使用用户自定义主键作为主键,如果用 户没有定义主键,则选取一个 Unique 键作为主键,如果表中连 Unique 键都没有 定义的话,则 InnoDB 会为表默认添加一个名为 row_id 的隐藏列作为主键。 DB_TRX_ID(也可以称为 trx_id) 和 DB_ROLL_PTR(也可以称为 roll_ptr) 这两 个列是必有的,但是 row_id 是可选的(在没有自定义主键以及 Unique 键的情 况下才会添加该列)。

  • Redundant

    Redundant 行格式是 MySQL5.0 之前用的一种行格式,不予深究。

  • Dynamic 和 Compressed

    MySQL5.7 的默认行格式就是 Dynamic,Dynamic 和 Compressed 行格式和
    Compact 行格式挺像,只不过在处理行溢出数据时有所不同。Compressed 行格式和 Dynamic 不同的一点是,Compressed 行格式会采用压缩算法对页面进行压缩,以节省空间。

数据溢出

如果我们定义一个表,表中只有一个 VARCHAR 字段,如CREATE TABLE test_varchar( c VARCHAR(60000) ),然后往这个字段插入 60000 个字符,会发生什么?

  • 在 Compact 和 Redundant 行格式中,对于占用存储空间非常大的列,在记录 的真实数据处只会存储该列的该列的前 768 个字节的数据,然后把剩余的数据分 散存储在几个其他的页中,记录的真实数据处用 20 个字节存储指向这些页的地 址。这个过程也叫做行溢出,存储超出 768 字节的那些页面也被称为溢出页。
  • Dynamic 和 Compressed 行格式,不会在记录的真实数据处存储字段真实数据 的前 768 个字节,而是把所有的字节都存储到其他页面中,只在记录的真实数据 处存储其他页面的地址。

索引页格式

页它是 InnoDB 管理存储空间的基本单位,一个页的大小一般是 16KB。

  • InnoDB 为了不同的目的而设计了许多种不同类型的页,存放我们表中记录的 那种类型的页自然也是其中的一员,官方称这种存放记录的页为索引(INDEX) 页,不过要理解成数据页也没问题,毕竟存在着聚簇索引这种索引和数据混合的 东西

储存空间

InnoDB 引擎底层事务的原理

事务应该具有 4 个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID 特性。

  • 原子性指的是一个事务中的操作要么全部成功,要么全部失败。
  • 一致性指的是数据库总是从一个一致性的状态转换到另外一个一致性的状态。比如 A 转账给 B100 块钱,假设中间 sql 执行过程中系统崩溃 A 也不会损失100 块,因为事务没有提交,修改也就不会保存到数据库。
  • 隔离性指的是一个事务的修改在最终提交前,对其他事务是不可见的。
  • 持久性指的是一旦事务提交,所做的修改就会永久保存到数据库中。

在事务的具体实现机制上,MySQL 采用的是 WAL(Write-ahead logging,预写式日志)机制来实现的。这也是是当今的主流方案。

  • 在使用 WAL 的系统中,所有的修改都先被写入到日志中,然后再被应用到系统中。通常包含 redo 和 undo 两部分信息。
  • redo log 称为重做日志,每当有操作时,在数据变更之前将操作写入 redo log,这样当发生掉电之类的情况时系统可以在重启后继续操作。
  • undo log 称为撤销日志,当一些变更执行到一半无法完成时,可以根据撤销日志恢复到变更之间的状态。

redo 日志

redo 日志的作用

InnoDB 存储引擎是以页为单位来管理存储空间的,我们进行的增删改查操作 其实本质上都是在访问页面(包括读页面、写页面、创建新页面等操作)。在 Buffer Pool 的时候说过,在真正访问页面之前,需要把在磁盘上的页缓存到内存 中的 Buffer Pool 之后才可以访问。但是在事务的时候又强调过一个称之为持久性 的特性,就是说对于一个已经提交的事务,在事务提交后即使系统发生了崩溃, 这个事务对数据库中所做的更改也不能丢失。
如果我们只在内存的 Buffer Pool 中修改了页面,假设在事务提交后突然发生 了某个故障,导致内存中的数据都失效了,那么这个已经提交了的事务对数据库 中所做的更改也就跟着丢失了,这是我们所不能忍受的。那么如何保证这个持久 性呢?一个很简单的做法就是在事务提交完成之前把该事务所修改的所有页面 都刷新到磁盘,但是这个简单粗暴的做法有些问题:

  • 刷新一个完整的数据页太浪费了

    有时候我们仅仅修改了某个页面中的一个字节,但是我们知道在 InnoDB 中是以页为单位来进行磁盘 IO 的,也就是说我们在该事务提交时不得不将一个完整的页面从内存中刷新到磁盘,我们又知道一个页面默认是 16KB 大小,只修改一个字节就要刷新 16KB 的数据到磁盘上显然是太浪费了。

  • 随机 IO 刷起来比较慢

    一个事务可能包含很多语句,即使是一条语句也可能修改许多页面,该事务修改的这些页面可能并不相邻,这就意味着在将某个事务修改的 Buffer Pool 中的页面刷新到磁盘时,需要进行很多的随机 IO,随机 IO 比顺序 IO 要慢,尤其对于传统的机械硬盘来说。

怎么办呢?我们只是想让已经提交了的事务对数据库中数据所做的修改永久 生效,即使后来系统崩溃,在重启后也能把这种修改恢复出来。所以我们其实没 有必要在每次事务提交时就把该事务在内存中修改过的全部页面刷新到磁盘,只 需要把修改了哪些东西记录一下就好,比方说某个事务将系统表空间中的第 100 号页面中偏移量为 1000 处的那个字节的值 1 改成 2 我们只需要记录一下

将第 0 号表空间的 100 号页面的偏移量为 1000 处的值更新为 2。

这样我们在事务提交时,把上述内容刷新到磁盘中,即使之后系统崩溃了, 重启之后只要按照上述内容所记录的步骤重新更新一下数据页,那么该事务对数 据库中所做的修改又可以被恢复出来,也就意味着满足持久性的要求。因为在系 统崩溃重启时需要按照上述内容所记录的步骤重新更新数据页,所以上述内容也 被称之为重做日志,英文名为 redo log,也可以称之为 redo 日志。与在事务提交 时将所有修改过的内存中的页面刷新到磁盘中相比,只将该事务执行过程中产生 的 redo 日志刷新到磁盘的好处如下:

  • redo 日志占用的空间非常小
    • 存储表空间 ID、页号、偏移量以及需要更新的值所需的存储空间是很小的。
  • redo 日志是顺序写入磁盘的
    • 在执行事务的过程中,每执行一条语句,就可能产生若干条 redo 日志,这些日志是按照产生的顺序写入磁盘的,也就是使用顺序 IO。

redo 日志格式

通过上边的内容我们知道,redo 日志本质上只是记录了一下事务对数据库做 了哪些修改。 InnoDB 们针对事务对数据库的不同修改场景定义了多种类型的 redo 日志,但是绝大部分类型的 redo 日志都有下边这种通用的结构:

  • type:该条 redo 日志的类型,redo 日志设计大约有 53 种不同的类型日志。
  • space ID:表空间 ID。
  • page number:页号。
  • data:该条 redo 日志的具体内容。

redo 日志的写入过程

  • redo log block 和日志缓冲区
  • redo 日志刷盘时机
  • redo 日志文件组
  • redo 日志文件格式
  • Log Sequence Number
  • flushed_to_disk_lsn
  • 查看系统中的各种 LSN 值
  • innodb_flush_log_at_trx_commit 的用法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值