MySQL-日志系统:一条SQL更新语句是怎样执行的?

b9bbb1c25be22f2ff530e3a67e30d7ad.png

1、更新语句经过解析与优化,生成执行计划,交由执行器调用存储引擎接口
        执行器会多次调用存储引擎接口,并不是一次完成

2、查询旧值,如果这个值不在内存缓冲区则需要查找旧值,从磁盘数据页加载到内存缓冲区

3、先将旧值写入undo log日志文件中,用于回滚数据

4、更新内存中的数据

5、向更新操作写入内存中redo log buffer中

6、redo log buffer里的日志每隔一秒会自动刷新到磁盘
        将redo log buffer中跟本事务相关的redo log日志刷新到磁盘,并向磁盘redo log文件中写入一个prepare redo log

7、准备提交事务,将binlog日志写入磁盘binlog文件中(binlog的写入逻辑:在事务执行过程中,先把日志写到binlog cache,事务提交的时候再把binlog cache写到binlog文件中,并清空bin log cache)

8、把本次更新对应的binlog文件名称和这些更新的binlog日志在磁盘文件的位置都写入到redo log日志文件中,同时在redo log 文件中写入一个commit标志

9、事务处理完成

10、mysql 后台线程会负责将数据刷入到磁盘

MySQL中的WAL技术:先写日志,再将数据更新到磁盘。

1、redo log 和 binlog

MySQL整体来看分为Server层和引擎层,Server层主要做MySQL功能层面的事情,引擎层负责存储具体的相关事宜。redo log是InnoDB引擎特有的日志,而Server层也有自己的日志称为binlog。

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

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

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

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

redo log:

write pos是当前记录的位置(最新的更新记录的位置,chekpoint到write pos是需要更新到磁盘的记录),一边写一边往后移,写到第3号文件末尾后就回到0号文件开头。

checkpoint是当前要擦除的位置(表示这条记录以及之后的记录还未将更新的内存数据写到磁盘),也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。

从write pos 到 checkpoint之间的部分,可以用来记录新的操作,如果check point表示不能再执行新的更新,得停下来擦除一些记录把checkpoint推进一下。

redo 日志缓冲刷新到磁盘中的redo 日志文件的时机

  1. Master Thread 每一秒将redo 日志缓冲刷新到redo 日志文件
  2. 每个事务提交时会将redo 日志缓冲刷新到redo 日志文件
  3. 当redo 日志缓冲剩余空间小于1 / 2时,redo 日志缓冲刷新到redo 日志文件
     

2、两阶段提交

将redo log日志写入拆成了两个步骤:prepare和commit。

为什么要有两阶段提交?

redo log和binlog都可以用于表示事务提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。

如果没有两阶段提交?这里的写redo log是从redo log buffer写到磁盘的redo log文件。

以字段值c从0更新为1为例:

1、先写redo log后写binlog:假设在redo log写完,binlog还没写完的之后,MySQL进程一场重启。redo log写完之后,系统即使崩溃(系统崩溃时内存的值还未更新到磁盘),仍然能把数据c恢复为1(redo log记录了数据变化之后的值,实现成功执行update的效果)。但由于binlog没写完就崩溃了,binlog没有这个记录, 恢复临时库的话,由于这个语句的binlog丢失,临时库就会少了这一次更新,恢复出来的数据还是0,数据并未更新。

2、先写binlog后写redo log:如果在binlog之后崩溃,由于redo log还没写,崩溃恢复以后redolog中没有commit,所以认为事务无效,这一行的c的值还是0。但binlog以及记录了“c从0变为1”这个日志,所以之后用binlog恢复时候多出一个事务出来,恢复c的值为1。

可以看到如果不使用两阶段提交,那么数据库的状态就有可能和用它的日志恢复出来的库状态不一致。

3、更新数据时不同时刻崩溃的处理

单独执行一个更新语句时,InnoDB会自己启动一个事物,在语句执行完成的时候提交。

如果在时刻A出现MySQL重启,也就是写入redo log处于prepare阶段后,写binlog之前发生了崩溃。由于此时binlog还没写,redo log还没提交,所以崩溃恢复的时候,这个事务会回滚(撤销这次事务的更新记录),binlog也还没写,所以两个日志的状态信息是一样的。

如果在时刻B出现MySQL重启,也就是binlog写完,redo log还没commit前发生崩溃,那么MySQL如何处理?

  • 如果redo log里面的事务是完整的,也就是有了commit标识,则直接提交。
  • 如果redo log里面的事物只有完整的prepare,则判断对应的事务binlog是否存在并完整:
    • 如果是,则提交事务、
    • 否则,回滚事务

MySQL如何知道binlog是完整的?

一个事务的binlog是有完整格式的

  • statement格式的binlog,最后会有COMMIT
  • row格式的binlog,最后会有一个XID event

4、redo log 和 binlog是怎么关联的?

它们有一个共同的是数据字段XID。崩溃回复的时候,会按顺序扫描redo log:

如果碰到既有prepare,又有commit的redo log,就直接提交

如果碰到只有prepare,而又没有commit的redo log,就拿着XID去binlog找对应的事务

5、能否只用redo log

redo log 这样的历史日志没法保留,也就起不到归档的作用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值