一条 update 语句的生命历程

2020/11/04 10:30
阅读数 30
作者简介
无为,多年 MySQL DBA 工作经验,现就职于某知名互联网公司,对 MySQL、 Redis、PostgrepSQL 等主流数据库有一定了解,拥有丰富的一线运维经验。

在有关 MySQL 的面试时,是不是曾经有被问到过:一条 update 语句的生命历程是怎样的?今天就来聊聊这一面试题。
这篇文章通过这条语句进行讲解:

   
   
update test set a=5 where id = 10;
一条 SQL 语句在的执行,总的来说可以分为:Server 层和存储引擎层(本节只聊 InnoDB),下面来仔细聊聊这些过程。

1 Server 层

1.1 连接层

负责跟客户端建立连接、账号密码验证、获取权限、维持和管理连接。

1.2 分析器

在通过验证以后,分析器会对该语句分析,判断是否语法有错误等。

1.3 优化器

选择索引,生成执行计划。

1.5 执行器

根据优化器生成的执行计划,调用存储引擎 API 执行 SQL。

二 、InnoDB 引擎层

2.1 事务执行

  1. 读取数据页面
    进入 InnoDB 引擎层后,首先会判断该 SQL 涉及到的数据页是否存在于 BP(buffer pool)中;
    如果不存在则通过 B+Tree 读取到磁盘的数据页,然后加载到 BP:
    通过二分法查找该页对应的记录
    • 通过 space id 和 page no 哈希计算之后把 索引页加载到指定的 buffer pool instance 中
    • 判断 free list 是否存在可用空闲页( Innodb_buffer_pool_pages_free、 Innodb_buffer_pool_wait_free ),没有则淘汰脏页或者 lru list 的 old 页
    • 把数据页 copy 到 free list 中,然后加载到 lru list 的 old 区的 midpoint(头部)
  2. 加锁
    尝试给对应行记录加上排他锁,过程如下:
    • 对应行记录的行锁是否被其他事务占用,占用则进入锁等待;
    • 进入锁等待之后,同时判断会不会由于自己的加入导致了死锁;
    • 检测到没有锁等待和不会造成死锁之后,行记录加上排他锁;
  3. 写逻辑 undo log
    将修改前的记录写入undo中;
    修改当前行的值,填写事务编号;
    使用回滚指针指向 undo log 中的修改前的行,构建回滚段,用于回滚数据和实现 MVCC 的多版本。
  4. 写 redo log buffer
    先判断 redo log buffer 是否够用,不够用则等待,可通过 Innodb_log_waits 值查看;
    对应行记录的字段值做更新操作,并把修改操作记录到 redo log buffer 中;
    对应数据页面加入 flush list 链表中。
  5. 写 binlog cache
    修改的信息会以对应 event 格式写入 binlog cache 中。
  6. 写 change buffer
    如果此次 update 操作涉及到二级索引的修改,则写入 change buffer page 。

2.2 事务提交

InnoDB 存储引擎事务提交分为 prepare、commit 两阶段提交
  1. redo log prepare
    将 redo log buffer 刷新到磁盘文件中,用于崩溃恢复;刷盘的方式由 innodb_flush_log_at_trx_commit 决定(未标记commit),存储引擎层处于 prepare 状态.
  2. binlog write & fsync
    执行器把 binlog cache 里的完整事务和 redo log prepare 中的 XID event 写入到 binlog 中;
    发送 binlog_cache 里的 event 到 slave 并等待(异步模式不等待) slave ack ;
    执行 fsync 刷盘(大事务的话这步非常耗时),并清空 binlog cache;# binlog 刷盘的方式由  sync_binlog 决定。
  3. redo log commit
    commit 阶段,由于之前该事务产生的 redo log 已经 sync 到磁盘了,所以这步只是在 redo log 里标记 commit,表明事务提交成功。半同步模式下如果收不到 slave ack 此步骤会处于等待状态
  4. 事务提交成功,释放行记录持有的排他锁。
  5. 刷新脏页
    数据库按照一定的策略执行刷脏页的操作。

2.3 事务回滚

如果事务因为异常或者被显式的回滚了,则借助 undo log 中的数据来进行恢复:
  • 对于 in-place(原地)更新,将数据回滚到最老版本;
  • 对于 delete + insert 方式进行的,标记删除的记录清理删除标记,同时把插入的聚集索引和二级索引记录也会被直接删除。

本文分享自微信公众号 - MySQL技术(mysqljishu)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部