sq网站推广,福州网站建设自助建站,网页升级紧急通知狼急通知,网络编程软件从这篇「执行一条 SQL 查询语句#xff0c;期间发生了什么#xff1f; (opens new window)」中#xff0c;我们知道了一条查询语句经历的过程#xff0c;这属于「读」一条记录的过程#xff0c;如下图#xff1a; 那么#xff0c;执行一条 update 语句#xff0c;期间发… 从这篇「执行一条 SQL 查询语句期间发生了什么 (opens new window)」中我们知道了一条查询语句经历的过程这属于「读」一条记录的过程如下图 那么执行一条 update 语句期间发生了什么比如这一条 update 语句
UPDATE t_user SET name xiaolin WHERE id 1;查询语句的那一套流程更新语句也是同样会走一遍
客户端先通过连接器建立连接连接器自会判断用户身份因为这是一条 update 语句所以不需要经过查询缓存但是表上有更新语句是会把整个表的查询缓存清空的所以说查询缓存很鸡肋在 MySQL 8.0 就被移除这个功能了解析器会通过词法分析识别出关键字 update表名等等构建出语法树接着还会做语法分析判断输入的语句是否符合 MySQL 语法预处理器会判断表和字段是否存在优化器确定执行计划因为 where 条件中的 id 是主键索引所以决定要使用 id 这个索引执行器负责具体执行找到这一行然后更新。
不过更新语句的流程会涉及到 undo log回滚日志、redo log重做日志 、binlog 归档日志这三种日志
undo log回滚日志是 Innodb 存储引擎层生成的日志实现了事务中的原子性主要用于事务回滚和 MVCC。redo log重做日志是 Innodb 存储引擎层生成的日志实现了事务中的持久性主要用于掉电等故障恢复binlog 归档日志是 Server 层生成的日志主要用于数据备份和主从复制
所以这次就带着这个问题看看这三种日志是怎么工作的。 为什么需要 undo log
我们在执行执行一条“增删改”语句的时候虽然没有输入 begin 开启事务和 commit 提交事务但是 MySQL 会隐式开启事务来执行“增删改”语句的执行完就自动提交事务的这样就保证了执行完“增删改”语句后我们可以及时在数据库表看到“增删改”的结果了。
执行一条语句是否自动提交事务是由 autocommit 参数决定的默认是开启。所以执行一条 update 语句也是会使用事务的。
那么考虑一个问题。一个事务在执行过程中在还没有提交事务之前如果 MySQL 发生了崩溃要怎么回滚到事务之前的数据呢
如果我们每次在事务执行过程中都记录下回滚时需要的信息到一个日志里那么在事务执行中途发生了 MySQL 崩溃后就不用担心无法回滚到事务之前的数据我们可以通过这个日志回滚到事务之前的数据。
实现这一机制就是 undo log回滚日志它保证了事务的 ACID 特性 (opens new window)中的原子性Atomicity。
undo log 是一种用于撤销回退的日志。在事务没提交之前MySQL 会先记录更新前的数据到 undo log 日志文件里面当事务回滚时可以利用 undo log 来进行回滚。如下图 每当 InnoDB 引擎对一条记录进行操作修改、删除、新增时要把回滚时需要的信息都记录到 undo log 里比如
在插入一条记录时要把这条记录的主键值记下来这样之后回滚时只需要把这个主键值对应的记录删掉就好了在删除一条记录时要把这条记录中的内容都记下来这样之后回滚时再把由这些内容组成的记录插入到表中就好了在更新一条记录时要把被更新的列的旧值记下来这样之后回滚时再把这些列更新为旧值就好了。
在发生回滚时就读取 undo log 里的数据然后做原先相反操作。比如当 delete 一条记录时undo log 中会把记录中的内容都记下来然后执行回滚操作的时候就读取 undo log 里的数据然后进行 insert 操作。
不同的操作需要记录的内容也是不同的所以不同类型的操作修改、删除、新增产生的 undo log 的格式也是不同的具体的每一个操作的 undo log 的格式我就不详细介绍了感兴趣的可以自己去查查。
一条记录的每一次更新操作产生的 undo log 格式都有一个 roll_pointer 指针和一个 trx_id 事务id
通过 trx_id 可以知道该记录是被哪个事务修改的通过 roll_pointer 指针可以将这些 undo log 串成一个链表这个链表就被称为版本链
版本链如下图 另外undo log 还有一个作用通过 ReadView undo log 实现 MVCC多版本并发控制。
对于「读提交」和「可重复读」隔离级别的事务来说它们的快照读普通 select 语句是通过 Read View undo log 来实现的它们的区别在于创建 Read View 的时机不同
「读提交」隔离级别是在每个 select 都会生成一个新的 Read View也意味着事务期间的多次读取同一条数据前后两次读的数据可能会出现不一致因为可能这期间另外一个事务修改了该记录并提交了事务。「可重复读」隔离级别是启动事务时生成一个 Read View然后整个事务期间都在用这个 Read View这样就保证了在事务期间读到的数据都是事务启动前的记录。
这两个隔离级别实现是通过「事务的 Read View 里的字段」和「记录中的两个隐藏列trx_id 和 roll_pointer」的比对如果不满足可见行就会顺着 undo log 版本链里找到满足其可见性的记录从而控制并发事务访问同一个记录时的行为这就叫 MVCC多版本并发控制。具体的实现可以看我这篇文章事务隔离级别是怎么实现的(opens new window)
因此undo log 两大作用
实现事务回滚保障事务的原子性。事务处理过程中如果出现了错误或者用户执 行了 ROLLBACK 语句MySQL 可以利用 undo log 中的历史数据将数据恢复到事务开始之前的状态。实现 MVCC多版本并发控制关键因素之一。MVCC 是通过 ReadView undo log 实现的。undo log 为每条记录保存多份历史数据MySQL 在执行快照读普通 select 语句的时候会根据事务的 Read View 里的信息顺着 undo log 的版本链找到满足其可见性的记录。
TIP
很多人疑问 undo log 是如何刷盘持久化到磁盘的
undo log 和数据页的刷盘策略是一样的都需要通过 redo log 保证持久化。
buffer pool 中有 undo 页对 undo 页的修改也都会记录到 redo log。redo log 会每秒刷盘提交事务时也会刷盘数据页和 undo 页都是靠这个机制保证持久化的。