通过上一篇博文,我们简要了解了MySQL的运行逻辑,从用户请求到最终将数据写入磁盘的整个过程。
当数据写入磁盘时,存储引擎扮演着关键的角色,它负责实际的数据存储和检索。
在MySQL中,有多个存储引擎可供选择,每个存储引擎都具有不同的特性和适用场景,但目前最常用的存储引擎之一是InnoDB。
今天,我们将学习InnoDB存储引擎的架构设计和核心特点,以及InnoDB事务提交过程深度解析。
UPDATE users set name='张老三' WHERE id = 1
如果我们执行上述update语句,它在整个执行过程中会发生什么?
接下来,我们将围绕这个update语句,熟悉整个的执行过程。
一、InnoDB存储引擎的特性:
1. 缓冲池(Buffer Pool)
缓冲池是InnoDB内部的一个重要内存结构,用于高效管理数据库表的数据和索引的缓存。
它在内存中存储了常用的数据页,以加速数据的读取和写入。
我们要更新上述sql
这个时候,它会先找 id = 1 这行数据是否在缓冲池中,如果不在,就将这条数据从磁盘加载到缓存池中。
当我们此时update的时候,也是先将新的数据更新到缓冲池,再写入到磁盘。
2. undo log(回滚日志)
我们知道,事务的所有操作要么全部成功(提交),要么全部失败(回滚)。
那么事务如何回滚呢?我们就要介绍到undo log
unod log文件顾名思义,就是回滚日志。
Undo Log 记录了事务对数据所做的修改,以便在需要回滚时能够撤销或者反向执行这些修改。
就是说我们写入新的数据到内存(缓冲池之前),会把更新之前的数据,也就是说原来的数据写入undo log文件,方便回滚的时候将数据恢复到事务开始之前的状态。
当我们把要更新的那行记录从磁盘文件加载到缓冲池,同时把更新前的旧值写入undo日志文件之后,就可以正式开始更新这行记录了。
根据上图我们看到,InnoDB收到更新请求后,查看缓冲池中是否有要更新这条数据的缓存页,如果没有则从磁盘文件中加载到缓冲池,再将更新前的数据写入undo log,最后进行数据的更新(更新到内存)
3. Redo Log(重做日志)
现在我们已经把内存里(缓冲池)的数据进行了修改,但是磁盘上的数据还没修改。
如果这时候MySQL机器宕机了,导致内存里修改过的数据丢失,应该怎么办呢?
为了防止这种情况发生,InnoDB 存储引擎在处理事务时采用了一种称为"write-ahead logging" (WAL) 的机制。
这种机制在更内存(缓冲池)时,会同时更新 Redo Log(重做日志)。
Redo Log是用于记录数据库引擎在执行事务期间对数据所做的所有修改操作,它记录了对数据页的物理修改操作,例如在磁盘上某个位置上的某个字节被修改成什么值。
当我们提交事务时,会把操作记录到 Redo Log 中,然后再更新到磁盘。这样做的目的是在系统崩溃时,可以通过重新执行 Redo Log 中的操作来还原已提交的事务,确保数据的一致性和完整性。
通过上图第5步,我们看到,当事务提交时,会将Redo Log写入到磁盘中,这是InnoDB的默认策略,也有其他方式可以自己配置
它由innodb_flush_log_at_trx_commit 参数进行控制:
3.1 innodb_flush_log_at_trx_commit = 0:
事务提交时,并不会立即将事务日志刷新到磁盘,而是每秒执行一次日志刷新操作。
也就是说,提交事务之后,mysql宕机,那么此时redo日志没有刷盘,导致内存(MySQL内存)里的redo日志丢失,我们提交的事务更新的数据就丢失了。
可能会丢失最多一秒钟的事务。
3.2 innodb_flush_log_at_trx_commit = 1(默认值):
每次事务提交时,都会将事务日志立即刷新到磁盘。
这种配置提供了 最高的事务持久性 ,因为在事务提交后,日志已经被写入磁盘,即使系统崩溃,也能够最小程度地丢失数据。
3.3 innodb_flush_log_at_trx_commit = 2:
每次事务提交时,事务日志会被写入到操作系统的缓存,而不是直接刷新到磁盘。
然后,日志会被每秒钟刷新到磁盘。这样可以提高性能,同时在机器崩溃时可能会丢失最多一秒钟的事务。
所以对于数据库这样严格的系统而言,一般建议redo日志刷盘策略设置为1,保证事务提交之后,数据绝对不能丢失。
当然,我们应该根据业务需要适当调整配置。
在整个事务提交的过程中,还有一点不得不提,那就是Binlog
根据我的经历,其实在实际工作中,我们更频繁地听到的是 Binlog(归档日志)。
例如,在数据库备份、数据恢复以及通过 CDC 同步数据等方面,Binlog 扮演着至关重要的角色。
二、 Binlog()
MySQL Binlog(二进制日志)是一种记录数据库中修改操作的日志。
与上面介绍的Redo Log偏向于物理性质的重做日志不同,Binlog更倾向于逻辑性的日志。
它记录了诸如“对users表中id = 1的一行数据进行更新,更新后的值是什么”之类的逻辑操作。
Binlog不是InnoDB存储引擎特有的日志文件,而是属于MySQL服务器自身的日志文件。
在事务提交时,MySQL会将修改操作先写入Redo log,以确保这些修改操作在系统崩溃或故障时不会丢失。
然后,再将事务写入binlog,以记录详细的修改操作信息,并用于数据复制和恢复。
这种顺序的执行方式是为了确保数据的一致性和可恢复性。
通过先写redo log,可以确保事务的持久性,避免数据丢失。
然后,通过写binlog可以记录详细的修改操作信息,并用于数据复制和恢复。
1. binlog 刷盘策略
当然,与Redo Log一样,对于Binlog日志,也有不同的同步策略。
它是通过sync_binlog参数可以控制Binlog的刷盘策略
1.1 sync_binlog=0:
sync_binlog=0是MySQL的默认值。
在提交事务时,将Binlog写入磁盘时先进入操作系统(OS)的Cache内存缓存。
这意味着Binlog首先被写入内存,而不是直接持久化到磁盘。
这样的方式在性能上更高,但在机器宕机时可能导致数据丢失。
1.2 sync_binlog=1
sync_binlog=1,在提交事务时,强制将Binlog直接写入磁盘文件,而不经过操作系统的Cache内存缓存。
这样做可以提高数据持久性,即使机器宕机,也不会丢失Binlog日志。
但是,这可能会对性能产生一定影响。
通过设置sync_binlog的不同取值,可以在数据安全性和性能之间进行权衡。
如果对数据的持久性要求较高,可以将其设置为1,确保在提交事务时Binlog立即写入磁盘。
如果更注重性能,可以使用默认值0,利用操作系统的Cache提高写入速度。
在提交了事务之后,MySQL有一个后台的IO线程,会不定期随机的把内存buffer pool中的修改后的脏数据写到磁盘中。