本文是一篇比较散的总结,通过一系列的概念,将 MySQL 核心原理梳理出来,看文章的时候,我可以在脑海里面过一遍,速览并复习。

1、数据库连接池、SQL 接口、SQL 解析器、查询优化器、执行器、存储引擎

2、Buffer Pool、undo 日志文件、Redo Log Buffer、redo 日志文件、binlog

  • redo log 在存储引擎层,记录的是一种偏向物理性质的重做日志,类似于“对哪个数据页中的什么记录,做了个什么修改”
  • binlog 叫做归档日志,在 server 层,记录的是一种偏向于逻辑性的日志,类似于“对 user 表中的 id=10的一行数据做了更新操作,更新以后的值是什么”

小结:在一次数据更新流程中,InnoDB 存储引擎主要包含了一些 buffer pool、redo log buffer 等内存里的缓存数据,同时还包含了 undo 日志文件、redo 日志文件等,同时 mysql server 还有 binlog 日志文件。
每次执行更新的时候,每条 SQL 语句,都会对应修改 buffer pool 里的缓存数据、写 undo 日志、写 redo log buffer 几个步骤。
但是当你提交事务的时候,一定会把 redo log 刷入磁盘,binlog 刷入磁盘,完成 redo log中的事务 commit 标记,最后后台的 IO 线程会随机把 buffer pool 里的脏数据刷入磁盘里去。

3、Buffer Pool 默认大小(128 MB)、Buffer Pool 描述数据、free 链表、flush 链表、LRU 链表(MySQL 对冷热数据的存储优化)

4、使用多个Buffer Pool 优化数据库的并发性能、chunk 机制

5、MySQL 一行数据在磁盘上是如何存储的、行溢出
格式:变长字段的长度列表、null 值列表、数据头、隐藏字段、column01 的值、column02 的值、column03 的值
说明:

  • 变长字段的长度列表:以十六进制逆序存放,实例 0x09
  • null 值列表以二进制存放,以1表示非NULL,0表示NULL,逆序存储
  • 数据头:40 个 bit 位
    • 第 1 位和第 2 位:都是预留位,没有含义;
    • 第 3 位:delete_mask,他标识的是这行数据是否被删除了;
    • 第 4 位:min_rec_mask,B+ 树的非叶子节点里的最小值都有这个标记;
    • 5 到 8 共 4 位:n_owned,记录数
    • 9 到 21 位:heap_no,当前这行数据在记录堆里的位置
    • 22 到 24 位:record_type,这行数据的类型,0 代表的事普通类型,1 代表的是 B+ 树的非叶子节点,2 代表的是最小值数据,3 代表的是最大值数据。
    • 25 到 40 位:next_record,这个是指向他下一条数据的指针
  • 列值:编码之后再存储
  • 隐藏字段:
    • DB_ROW_ID 字段:行的唯一标识,数据库内部给定,不是主键。如果我们没有指定主键和唯一索引的时候,他就内部自动加一个 ROW_ID 做为主键。
    • DB_TRX_ID 字段:这是跟事务相关的事务ID
    • DB_ROLL_PTR 字段:回滚指针,用来进行事务回滚。
  • 行数据物理存储示例:
    0x09 0x04 00000101 0000000000000000000010000000000000011001 000000000094(DB_ROW_ID) 000000000032D(DB_TRX_ID) EA000010078E(DB_ROL_PTR) 616161 636320 6262626262

6、MySQL的存储模型:表空间有很多组数据区(extend),一组数据区是256个数据区,每个数据区占 1 Mb,包含了 64 个数据页,每个数据页 16 Kb;数据页的各个部分

  • 表空间的第一组数据区的第一个数据区的前 3 个数据页,都是存放特殊信息的;
  • 表空间的其他组数据区的第一个数据区的前两个数据页,也是存放特殊信息的;

7、数据库日志的顺序读写和数据文件的随机读写
8、Linux 存储系统层级:

IO 调度层默认使用 CFQ 公平调度算法,使用该算法事会导致饥饿,生产环境中,建议改为deadline IO,该算法的核心思想是:任何 IO 操作都不能一直不停地等待,在指定的时间范围内,都必须让他去执行。
最后 IO 完成调度之后,就会决定哪个IO 请求先执行,哪个 IO 请求后执行,此时可以执行的 IO 请求就会交给 Block 设备驱动层。
Block 设备驱动层把 IO 请求发送给真正的存储硬件,也就是 Block 设备层。
Block 设备层完成了 IO 读写操作之后,进行读写操作,最后把响应经过上面的层级反向依次返回, MySQL 就可以得到本次 IO 读写操作的结果。

9、Too many connections 故障
与 Linux 文件句柄数量有关,需要调大一点。

ulimit -HSn 65535

然后就可以用如下命令检查最大文件句柄数是否被修改了

cat /etc/security/limits.conf
cat /etc/rc.local

我们平时可以用ulimit命令来设置每个进程被限制使用的资源量,用ulimit -a就可以看到进程被限制使用的各种资源的量。

  • core file size 代表的进程崩溃时候的转储文件的大小限制
  • max locked memory就是最大锁定内存大小
  • open files就是最大可以打开的文件句柄数量
  • max user processes就是最多可以拥有的子进程数量。

设置之后,我们要确保变更落地到/etc/security/limits.conf文件里,永久性的设置进程的资源限制

10、redo log 底层存储格式

redo log 本质上记录的就是在对某个表空间的某个数据页的某个偏移量的地方修改了几个字节的值,具体修改的值是什么,他里面需要记录的就是表空间号+数据页号+偏移量+修改几个字节的值+具体的值.

根据你修改了数据页里的几个字节的值,redo log就划分为了不同的类型,MLOG_1BYTE类型的日志指的就是修改了1个字节的值,MLOG_2BYTE类型的日志指的就是修改了2个字节的值,以此类推,还有修改了4个字节的值的日志类型,修改了8个字节的值的日志类型。当然,如果你要是一下子修改了一大串的值,类型就是MLOG_WRITE_STRING,就是代表你一下子在那个数据页的某个偏移量的位置插入或者修改了一大串的值。

所以其实一条redo log看起来大致的结构如下所示:

  • 日志类型(就是类似MLOG_1BYTE之类的),表空间ID,数据页号,数据页中的偏移量,具体修改的数据

如果是MLOG_WRITE_STRING类型的日志,因为不知道具体修改了多少字节的数据,所以其实会多一个修改数据长度,就告诉你他这次修改了多少字节的数据,如下所示它的格式:

  • 日志类型(就是类似MLOG_1BYTE之类的),表空间ID,数据页号,数据页中的偏移量,修改数据长度,具体修改的数据

11、redo log block 数据结构、redo log buffer 中的缓冲日志何时写入磁盘