类似于查询语句,会经过上述过程,但是更新需要记录日志——redo(重做日志)和binlog(归档日志)
update user A set A.nickname='大聪明' where A.name=' 张三 ';
-
在InnoDB引擎下这条语句的执行流程简述如下:
先查询到张三这条数据(如果不在内存中需要先从磁盘加读入内存),然后将拿到的数据将nickname大聪明,然后调用引擎接口,将这行数据更新到内存中,同时将更新操作记录到redo log中,此时redo log进入预提交(prepare)状态,然后告知执行器已经执行完成,执行器将操作记录到binlog,并写入磁盘,执行器调用引擎的提交事务接口,将redo log更新为提交状态。
为什么需要两个日志呢,看完你就知道了
- Innodb引擎特有的日志,属于物理日志,记录了具体的更新操作后的记录,(“在某个数据页上做了什么修改”,我的理解是将具体位置修改记录下来,写到磁盘的时候只需要替换掉磁盘中原来的值就行了,和binlog做区分,binlog记录的是逻辑记录,也就是这次更新是怎么更新的,具体做了啥)。
- 大小固定,循环写,每次写到没有空的部分的时候就会找机会先更新到磁盘,然后擦除掉已经写回磁盘的记录,腾出一部分空间继续写日志,InnoDB就是通过redo log来支持事务的。
-
bin log是MySQL的Server层日志,主要用作归档,属于逻辑日志,记录SQL语句的操作逻辑。bin log是追加写入的,也就是说不会覆盖以前的日志。
-
一般在主从同步和误删恢复使用
既然可以用作误删恢复那为什么不能作为崩溃恢复呢??其实是可以用的,数据库系统会定期做整库备份,当一定需要恢复到某一个时刻的数据时,可以根据binlog将数据恢复出来。
但是速度会慢得多,bin log是记录的逻辑操作,需要一步一步解析日志直到想要的那条数据为止。
-
在InnoDB引擎的两阶段提交机制下,只要bin log没写完(不完整),都会回滚事务,因为bin log是用来同步从库的,如果宕机重启之后不回滚就会导致主库中已经有数据(因为 redo log已经写入,主库会根据redo log进行数据恢复),而bin log中没有写入,会导致主从不一致。
-
只要bin log写入完成都会提交事务,因为bin log中已经有数据了,所以从库会同步其中的数据,如果主库不进行事务提交,那么主库中就没有这部分数据(主库是根据redo log进行数据恢复的),会导致主从不一致。
-
事务提交之后是不能回滚的,违背了事务的持久性
- 最简单的保证持久性的做法就是在事务提交之前将更新的数据直接先更新到磁盘。但是这样的做法存在很大的性能问题,因为磁盘更新时可能是随机的,也就是更新的数据的位置是乱七八糟的,需要随机读写磁盘数据。因此这种实时更新的做法是不行的,于是乎可以先更新缓存,再异步写入磁盘,但是这种方式会存在数据丢失问题,如果在写入缓存之后事务提交了,但是数据未写入磁盘,此时宕机会使数据丢失。
- 那么redo log到底怎么高效又能保持数据一致性呢?这是因为redo log有两部分,一部分是在内存中—redo log buffer,一部分在磁盘上—redo log file,也就是说更新redo log的时候先更新redo log buffer,buffer再写入到file中,这个时候的写入磁盘只需要往file文件中追加就行了,顺序写磁盘当然比随机读写快很多。而事务的提交是在写入redo log file之后,那就可以保持持久性了。
- 而binlog也是需要写入磁盘的,当redo log进入预提交状态后将bin log写入磁盘,然后再提交事务。当然,是否每次事务都先持久化到磁盘也和innodb_flush_log_at_trx_commit、sync_binlog这些参数配置有关。
- 例1,事务1已经写入binlog并且提交事务完成,事务2已经写入binlog但还未提交,此时宕机重启后,事务2还未提交,引擎内部会根据事务2的bin log进行提交并恢复数据到磁盘,但是事务1已经提交完成,此时系统不会再恢复一遍事务1的数据,但是如果此时事务1的binlog并没有更新到磁盘对应的位置,那么就会丢失binlog 1的数据,而binlog1只是记录了逻辑操作,并没有记录数据页中的更新细节,也就是更新之后应该是多少,无法恢复此部分数据,因为系统无法确定应该从哪一步开始恢复(binlog中的数据都是事务了的提交的数据,但是系统无法确定哪些提交了的数据是已经刷盘的)。
- 但如果是redolog,那就可以直接刷盘,因为写入磁盘的数据都已经被擦除了,剩下的数据都是未写入磁盘的,同步起来速度很快。
如图所示:
将写日志的几步操作可以归结如下:
①先写入redo log,状态定为prepare状态
②写入binlog
③写入binlog完成以后,再修改redo log状态为commit状态,提交事务
无论在哪一步系统崩溃了都可以使数据保持一致性。如果在第①步结束的时候宕机,那就直接回滚,因为binlog还未写入,也就是说从库无法同步主库的数据,所以事务不提交,而是回滚。如果在第②步结束时宕机,也就是说binlog写完了,那么从库就会根据bin log同步,此时主库应该提交事务,根据redo进行数据恢复;如果在第②步中间宕机了,这个时候binlog并不完整,不会进行提交事务。在这样的机制下,不会出现数据不一致的问题。



