Skip to content

Mysql数据库是如何实现持久化的

1. 磁盘 I/O 瓶颈与应对策略

数据库的数据最终存储在硬盘上。传统机械硬盘(HDD)的读写操作依赖于磁头的机械运动,包括寻道和旋转定位,这一过程远慢于 CPU 和内存的电子信号速度。因此,直接的磁盘读写,尤其是随机读写,会成为数据库性能的主要瓶颈。

  • 随机读写 vs. 顺序读写

    • 随机读写:当需要读写的数据在磁盘上不连续时,磁头需要频繁移动,耗费大量时间。数据库中的数据页在物理上往往是随机分布的,因此直接修改数据文件会导致大量的随机 I/O。

    • 顺序读写:如果数据是连续存储的,磁头可以一次性读取或写入大块数据,大大提高了效率。

为了解决磁盘 I/O 的瓶颈,现代计算系统引入了多级缓存:

  1. 操作系统缓存(Page Cache/Buffer Cache):操作系统在内存中开辟一块区域,用于缓存磁盘数据。写操作首先写入缓存,然后由操作系统根据特定策略(如定时、缓存区满)异步刷写到磁盘。这被称为“延迟写”(Delayed Write),可以合并多次小的写操作,提升性能。

  2. 数据库缓存(如 InnoDB Buffer Pool):MySQL 等数据库在用户空间内实现了自己的缓存池。所有的数据读写首先在 Buffer Pool 中进行,这极大地减少了与磁盘的直接交互。

然而,仅仅依靠缓存会带来新的问题:如果系统在缓存中的“脏数据”(已修改但未写入磁盘的数据)落盘前宕机,数据就会丢失。

2. 核心机制:预写日志 (Write-Ahead Logging - WAL)

为了在利用缓存提升性能的同时保证数据不丢失,数据库普遍采用 WAL 机制。其核心思想是:在修改数据之前,必须先将描述这些修改的日志记录写入到持久化的日志文件中。

以 MySQL 的 InnoDB 存储引擎为例,这一机制通过 redo log(重做日志)实现。

redo log 的工作流程如下:

  1. 当一个事务需要修改数据时,它首先在内存中的 Buffer Pool 里修改对应的数据页。

  2. 在修改数据页的同时,InnoDB 会生成一条或多条 redo log 记录,这些记录描述了“在哪个数据页的哪个位置做了什么修改”。

  3. 这些 redo log 记录首先被写入内存中的 redo log buffer。

  4. 在事务提交时,redo log buffer 中的内容必须被刷写到磁盘上的 redo log 文件中。一旦日志成功写入磁盘,事务就被认为是持久化的,即使 Buffer Pool 中的脏数据页尚未刷盘。

  5. 数据库后台线程会在合适的时机,将 Buffer Pool 中的脏数据页异步地刷写到磁盘的数据文件中。

为什么 redo log 能提升性能并保证持久化?

  • 变随机写为顺序写:直接刷写数据页是随机 I/O,而 redo log 是以追加的方式写入日志文件的,这是一种顺序 I/O,效率远高于随机 I/O。数据库将事务持久化的压力从昂贵的随机写数据页转移到了高效的顺序写日志上。

  • 保证数据不丢失:如果数据库在脏数据页刷盘前崩溃,重启后可以通过 redo log 进行恢复。它会检查日志,将那些已经提交但数据页尚未落盘的修改重新应用到数据页上,从而确保了数据的完整性。

3. redo log 的刷盘策略

InnoDB 提供了 innodb_flush_log_at_trx_commit 参数来控制事务提交时 redo log 的刷盘策略,允许用户在性能和数据一致性之间进行选择:

  • innodb_flush_log_at_trx_commit = 1(默认值)

    • 最高安全性:每次事务提交时,redo log 都会从 redo log buffer 同步写入到操作系统的缓存,并立即调用 fsync()强制刷写到磁盘。这种方式可以最大限度地保证已提交的事务不丢失数据。

    • 性能影响:每次提交都涉及一次磁盘 I/O,性能最差。适用于对数据一致性要求极高的场景,如支付、金融业务。

  • innodb_flush_log_at_trx_commit = 0

    • 最高性能:事务提交时,redo log 仅留在 redo log buffer 中。由后台主线程大约每秒将 redo log buffer 的内容刷写到磁盘。

    • 安全性最低:如果 MySQL 进程在事务提交后、日志刷盘前崩溃,会丢失最近一秒内所有已提交的事务数据。

  • innodb_flush_log_at_trx_commit = 2

    • 性能与安全的折中:每次事务提交时,redo log 会写入操作系统的页面缓存(OS Buffer Cache),但不会立即 fsync()到磁盘。而是依赖操作系统大约每秒一次的刷盘操作。

    • 较高安全性:只要操作系统不崩溃或服务器不断电,数据就不会丢失。相比设置 1,性能有显著提升。