初步了解InnoDB存储引擎的架构设计

tech2026-03-02  3

初步了解InnoDB存储引擎的架构设计

更新语句在MySQL中是如何执行的?

首先假设我们有一条SQL语句是这样的:update users set name='xxx' where id = 10;这条SQL语句是如何执行的?

首先肯定是系统通过一个数据库连接发送到了MySQL上,然后肯定会经过SQL接口、解析器、优化器、执行器几个环节,解析SQL语句,生成执行计划,接着去由执行器负责这个计划的执行,调用InnoDB存储引擎的接口去执行。大致还是会走下图的这个流程:

现在就来探索一下这个存储引擎里的架构设计,以及如何基于存储引擎完成一条更新语句的执行。

InnoDB的重要内存结构:缓冲池

InnoDB存储引擎中有一个非常重要的放在内存里的组件,就是缓冲池(Buffer Pool),这里面会缓存很多的数据,以便于以后在查询的时候,万一你要是内存缓冲池里有数据,就可以不用去查磁盘了,如下图:

当 InnoDB 存储引擎要执行更新语句的时候,比如对“id=10”这一行数据,他其实会先将“id=10”这一行数据看看是否在缓冲池里,如果不在的话,那么会直接从磁盘里加载到缓冲池里来,而且接着还会对这行记录加独占锁。因为,在更新“id=10”这一行数据的时候,肯定是不允许别人同时更新的,所以必须要对这行记录加独占锁。

undo日志文件:如何让你更新的数据可以回滚?

假设“id=10”这行数据的name原来是“zhangsan”,现在要更新为“xxx”,那么此时得先把要更新的原来的值“zhangsan”和“id=10”这些信息,写入到undo日志文件中去。对数据库稍微知道点的都应该知道,如果执行一个更新语句,要是它是在一个事务里的话,那么事务提交之前都是可以对数据进行回滚的,也就是把更新为“xxx”的值回滚到之前的“zhangsan”去。

为了考虑到未来可能要回滚数据的需要,这里会把更新前的值写入undo日志文件,具体如下图:

更新buffer pool中的缓存数据

当把要更新的那行记录从磁盘文件加载到缓冲池,同时对它加锁之后,而且还把更新前的旧值写入undo日志文件之后,就可以正式开始更新这行记录了,更新的时候,先是会更新缓冲池中的记录,此时这个数据就是脏数据了。

这里所谓的更新内存缓冲池里的数据,意思就是把内存里的“id=10”这行数据的name字段修改为“xxx”。这个时候磁盘上“id=10”这行数据的name字段还是“zhangsan”,但是内存里这行数据已经被修改了,所以就会叫它是脏数据。具体如下图所示:

Redo Log Buffer:万一系统宕机,如何避免数据丢失?

按照上图的说明,现在已经把内存里的数据进行了修改,但是磁盘上的数据还没修改。那么此时万一MySQL所在的机器宕机了,必然会导致内存里修改过的数据丢失,这可怎么办呢?

这个时候,就必须要把对内存所做的修改写入到一个Redo Log Buffer里去,这也是内存里的一个缓冲区,是用来存放redo日志的。redo日志,就是记录下来对数据做了什么修改,比如对“id=10这行记录修改了name字段的值为xxx”,这就是一个日志。具体如下图所示:

这个redo日志其实是用来在MySQL突然宕机的时候,用来恢复更新过的数据的,但是现在还不解释redo是如何使用的,毕竟现在redo日志还仅仅停留在内存缓冲里。

如果还没提交事务,MySQL宕机了怎么办?

到目前为止,其实还没有提交事务,那么此时如果MySQL崩溃,必然导致内存里Buffer Pool中的修改过的数据都丢失,同时写入Redo Log Buffer中的redo日志也会丢失。

那么此时数据丢失要紧吗?

其实是不要紧的,因为一条更新语句,没提交事务,就代表它没执行成功,此时MySQL宕机虽然导致内存里的数据都丢失了,但是会发现,磁盘上的数据依然还停留在原样子。

也就是说,“id=1”的那行数据的name字段的值还是老的值,“zhangsan”,所以此时的这个事务就是执行失败了,没能成功完成更新,会收到一个数据库的异常。然后当mysql重启之后,会发现数据并没有任何变化。所以此时如果mysql宕机,不会有任何的问题。

提交事务的时候将redo日志写入磁盘中

接着要提交一个事务了,此时就会根据一定的策略把redo日志从redo log buffer里刷入到磁盘文件里去。

此时这个策略是通过innodb_flush_log_at_trx_commit来配置的,它有几个选项:

当这个参数的值为0的时候,那么提交事务的时候,不会把redo log buffer里的数据刷入磁盘文件的,此时可能都提交事务了,结果mysql宕机了,然后此时内存里的数据全部丢失。相当于提交事务成功了,但是由于MySQL突然宕机,导致内存中的数据和redo日志都丢失了。当这个参数的值为1的时候,提交事务的时候,就必须把redo log从内存刷入到磁盘文件里去,只要事务提交成功,那么redo log就必然在磁盘里了。那么只要提交事务成功之后,redo日志一定在磁盘文件里,此时肯定会有一条redo日志说了,“对哪个数据做了一个什么修改,比如name字段修改为xxx了”。当这个参数的值为2的时候,提交事务的时候,把redo日志写入磁盘文件对应的os cache缓存里去,而不是直接进入磁盘文件,可能1秒后才会把os cache里的数据写入到磁盘文件里去。这种模式下,redo log可能仅仅停留在os cache内存缓存里,没实际进入磁盘文件,万一此时要是机器宕机了,那么os cache里的redo log就会丢失,同样会感觉提交事务了,结果数据丢了。

对于这三种模式怎么选择的问题:

innodb_flush_log_at_trx_commit为0,宕机后数据会丢失,但带来的写入速度是最佳。 innodb_flush_log_at_trx_commit为1,redo日志刷盘操作是同步的,刷盘成功才能提交事务,但写入速度是这三者效率最慢。优点是能保证缓冲池的数据0丢失。 innodb_flush_log_at_trx_commit为2时,redo日志写入到os cache中即可提交事务,如果宕机,会丢失1s内的数据。当写入速度也是很快的。优点是速度快,也能保证缓冲池中大部分修改后的数据保存下来。 选择这三者的权衡自身系统的功能,如果要保证缓冲池的数据0丢失,则值为1; 如果高效的写入操作,不考虑数据的一致性,则值为0; 如果即想高速写入又保证数据丢失少,则值为2;

最新回复(0)