02 | 日志系统:一条SQL更新语句是如何执行的?

Posted lakeslove

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了02 | 日志系统:一条SQL更新语句是如何执行的?相关的知识,希望对你有一定的参考价值。

redo log 和 bin log

redo log 是innodb引擎特有,当有一条记录需要更新时,innodb先把记录写到redo log中,并更新内存,此时更新完成,

同时,innodb会在适当的时候把这个操作记录更新到磁盘中。

binlog(归档日志)是server层的日志

这两种日志有以下三点不同:

redo log 是 InnoDB 引擎特有的;binlog 是 mysql 的 Server 层实现的,所有引擎都可以使用。

redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。

redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

 

mysql> update T set c=c+1 where ID=2;

1 prepare阶段 2 写binlog 3 commit
当在2之前崩溃时
重启恢复:后发现没有commit,回滚。备份恢复:没有binlog 。
一致
当在3之前崩溃
重启恢复:虽没有commit,但满足prepare和binlog完整,所以重启后会自动commit。备份:有binlog. 一致

 

 

 

 

1.⾸先客户端通过tcp/ip发送⼀条sql语句到server层的SQL interface

2.SQL interface接到该请求后,先对该条语句进⾏解析,验证权限是否匹配

3.验证通过以后,分析器会对该语句分析,是否语法有错误等

4.接下来是优化器器⽣成相应的执⾏计划,选择最优的执⾏计划

5.之后会是执⾏器根据执⾏计划执⾏这条语句。在这⼀步会去open table,如果该table上有MDL,则等待。 如果没有,则加在该表上加短暂的MDL(S) (如果opend_table太⼤,表明open_table_cache太⼩。需要不停的去打开frm⽂件)

6.进⼊到引擎层,⾸先会去innodb_buffer_pool⾥的data dictionary(元数据信息)得到表信息

7.通过元数据信息,去lock info⾥查出是否会有相关的锁信息,并把这条update语句需要的 锁信息写⼊到lock info⾥(锁这⾥还有待补充)

8.然后涉及到的⽼数据通过快照的⽅式存储到innodb_buffer_pool⾥的undo page⾥,并且记录undo log修改的redo (如果data page⾥有就直接载⼊到undo page⾥,如果没有,则需要去磁盘⾥取出相应page的数据,载⼊到undo page⾥)

9.在innodb_buffer_pool的data page做update操作。并把操作的物理数据⻚修改记录到redo log buffer⾥ 由于update这个事务会涉及到多个⻚⾯的修改,所以redo log buffer⾥会记录多条⻚⾯的修改信息。 因为group commit的原因,这次事务所产⽣的redo log buffer可能会跟随其它事务⼀同flush并且sync到磁盘上

10.同时修改的信息,会按照event的格式,记录到binlog_cache中。(这⾥注意binlog_cache_size是transaction级别 的,不是session级别的参数, ⼀旦commit之后,dump线程会从binlog_cache⾥把event主动发送给slave的I/O线程)

11.之后把这条sql,需要在⼆级索引上做的修改,写⼊到change buffer page,等到下次有其他sql需要读取该⼆级索 引时,再去与⼆级索引做merge (随机I/O变为顺序I/O,但是由于现在的磁盘都是SSD,所以对于寻址来说,随机I/O和顺序I/O差距不⼤)

12.此时update语句已经完成,需要commit或者rollback。这⾥讨论commit的情况,并且双1

13.commit操作,由于存储引擎层与server层之间采⽤的是内部XA(保证两个事务的⼀致性,这⾥主要保证redo log 和binlog的原⼦性), 所以提交分为prepare阶段与commit阶段

14.prepare阶段,将事务的xid写⼊,将binlog_cache⾥的进⾏flush以及sync操作(⼤事务的话这步⾮常耗时)

15.commit阶段,由于之前该事务产⽣的redo log已经sync到磁盘了。所以这步只是在redo log⾥标记commit

16.当binlog和redo log都已经落盘以后,如果触发了刷新脏⻚的操作,先把该脏⻚复制到doublewrite buffer⾥, 把doublewrite buffer⾥的刷新到共享表空间,然后才是通过page cleaner线程把脏⻚写⼊到磁盘中 ⽼师,你看我的步骤中有什么问题嘛?我感觉第6步那⾥有点问题,因为第5步已经去open table了,第6步还有没有 必要去buffer⾥查找元数据呢?这元数据是表示的系统的元数据嘛,还是所有表的?

 

以上是关于02 | 日志系统:一条SQL更新语句是如何执行的?的主要内容,如果未能解决你的问题,请参考以下文章

日志系统:一条SQL更新语句是如何执行的

日志系统:一条SQL更新语句是如何执行的

从头开始搞懂 MySQL(02)如何执行一条 SQL 更新语句

从头开始搞懂 MySQL(02)如何执行一条 SQL 更新语句

日志系统:一条SQL更新语句是如何执行的?

讲日志系统:一条SQL更新语句是如何执行的