前沿:

我们知道mysql数据和结构都存储在磁盘,这样才能保证数据的持久性,那么数据和结构是如何存储的呢?这就是今天我们要讨论的话题。

 正文:

 数据库有俩个文件,一个是以.frm结尾的文件,另外一个是以.ibd结尾。而表的定义存储在.frm文件中,索引数据存储在.ibd中。那么.ibd文件中的结构又是如何的呢?

我们从大到小说,.ibd文件也被成为表空间,表空间是由多个数据区组(也有叫segment段)组成,数据区组由256个数据区(extent)组成,一个数据区的大小最小为 1MB,那么数据区组是256M。一个区又由64个数据页(page)组成,一个数据页的大小是16kb,16kb * 64正好是1M的大小。最后数据页是由文件头、数据页头、最大最小记录、数据区、数据页目录、文件尾部,其中数据区里才是我们保存的一条一条数据。

那么数据页中的一条数据又包括那些东西呢?他包括变长字段列表、null值列表、数据头、实际数据。所以这也能解释数据库规范中,强烈建议表字段设置为非空的原因。

接下来我们聊聊另外一个话题,当我们修改一条数据的时候,整个过程是咋样的呢?

  1. 首先我们要知道待修改的那条数据在哪里?如果条件中包含索引,那么先根据索引结构找到主键ID,再由ID到主键索引中找到该条数据。
  2. 为了效率,会将该条数据所在的整个数据页加载到缓冲池Buffer Pool中。
  3. 这个时候再该页上加独占锁
  4. 将更新前的数据保存到undo log(回滚日志)中,防止事务回滚。
  5. 更新数据
  6. 将redo log(重做日志) 写入redo log buffer中,防止服务器崩溃,数据丢失。
  7. 将修改后的数据页刷回磁盘
  8. 释放锁
  9. 将redo log buffer 日志刷回磁盘,这个时候该条修改的日志不会丢失。

上面有一个问题,redo log buffer啥时候刷磁盘呢?数据库是这样设计的:

  1. 如果一次性redo log buffer的内存超过一半,默认超过8M,会直接刷盘。
  2. 提交事物的情况下会刷盘。
  3. 后台线程定时将redo log buffer刷盘,顺序写入磁盘。
  4. mysql 停止服务触发刷盘。