MySQL45讲——日志系统:一条SQL更新语句是如何执行的 学习笔记

数据库备份:MySQL可以恢复到半个月内任意一秒的状态。
MySQL查询语句和更新语句的异同?
MySQL45讲——日志系统:一条SQL更新语句是如何执行的 学习笔记_第1张图片
查询语句的执行流程更新语句也会走一遍
执行语句前要先连接数据库,这是连接器的工作。
在一个表有更新时,跟这个表有关的查询缓存会失效,更新语句会把表上的所有缓存结果清空。因此,不建议使用查询缓存
分析器会通过词法和语法解析知道这是一条更新语句。
优化器决定使用什么索引。
执行器负责具体执行。
与查询流程不同的是,更新流程还涉及两个重要的日志模块:redo log和binlog(归档日志)。

redo log
比喻:赊账的小黑板 & 账簿
赊账记录的两种方式:
①直接把账本翻出来,把这次赊的账加上去或扣掉;
②先在小黑板上记下这次的账,有空时再把账本翻出来核算。
方式一操作麻烦,效率太低,在账本上找名字很耗时,同时还需要计算、修改。方式二更加便利。
MySQL的执行一条更新语句就像一次赊账/销账过程。
每次更新操作都要写进磁盘就像直接在账本上修改(方式一),写入磁盘本身IO成本高,磁盘也要找到对应的那条记录(查找成本很高),然后再更新。由此看,如果每次更新操作都写进磁盘,IO成本和查找成本都很高。MySQL设计者采用方式二来提升效率,引入redo log作为小黑板。先将更新语句写入日志,等不忙的时候再将更新语句写入磁盘、查找、更新。上述过程就是MySQL常说的WAL(write-ahead logging)技术
个人的理解:就像在小黑板上记录没有真正完成赊账/销账(因为没有历史额,所以只是记录),在账本上查找、修改才真正完成赊账/销账。将更新语句写入redo log并没有执行该语句,将其写入磁盘,查找到对应的记录,然后更新才是执行该更新语句。 是在执行完更新操作之后,才把更新语句写入redo log的。

如果出现某天赊账/销账的特别多,小黑板写满了,怎么办呢?
掌柜只能先放下手中的活儿,把小黑板中的一部分赊账记录更新到账本中。然后把这些记录从粉板上擦掉,为记新账腾出空间。
小黑板的大小有限,redo log也是固定大小,容量有限的。从头开始写,写到末尾就又回到开头循环写。
MySQL45讲——日志系统:一条SQL更新语句是如何执行的 学习笔记_第2张图片
write pos是当前记录的位置,一边写一边后移,写到末尾后又回到开头。checkpoint是当前要擦除的位置,也是往后推移并循环的,插除记录前要将数据更新到磁盘。write pos和checkpoint之间的是小黑板上空着的部分,可以用来记录新的操作。如果write pos追上checkpoint,表示小黑板满了,得停下来将一些记录写入磁盘,然后擦掉已经写入磁盘的记录,把checkpoint推进一下。
只要赊账记录记在了小黑板上或者账本上了,之后即使掌柜忘记了,比如突然停业几天,恢复生意后仍然可以通过账本和小黑板上的数据明确赊账账目。类似的,有了redo log和磁盘的结合使用,即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe

binlog
redo log是InnoDB引擎特有的日志。
binlog是server层的日志。
看看执行器和InnoDB引擎在执行下面的update语句时的内部流程:

创建表T:

create table T(ID int primary key,c int);

执行一条更新语句:

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

①执行器获取要修改的行:执行器先找引擎取ID=2这一行(如何找:ID是主键,引擎直接到主键索引树上搜索找到这一行)。如果ID=2这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
②执行器拿到引擎给的行数据,把这个值加上1,得到新的一行数据,再调用引擎接口写入这行新数据。
③引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务。
④执行器生成这个操作的binlog,并把binlog写入磁盘。
⑤执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成commit状态,更新完成。
下面是update语句的执行流程图,浅色框表示在InnoDB引擎内部执行的,深色框表示是在执行器中执行的。
MySQL45讲——日志系统:一条SQL更新语句是如何执行的 学习笔记_第3张图片

上述过程将redo log的写入拆成了两个步骤:prepare和commit。两阶段提交:为了保证redo log和binlog逻辑上的一致。

小结:
redo log是物理日志,binlog是逻辑日志。
redo log保证crash safe的能力。
将innodb_flush_at_trx_commit这个参数设置成1,表示每次事务的redo log都直接持久化到磁盘,可以保证MySQL异常重启后数据不丢失。
将sync_binlog这个参数设置为1,表示每次事务的binlog都持久化到磁盘,可以保证MySQL异常重启后binlog不丢失。
两阶段提交是跨系统维持数据库逻辑一致性时常用的一个方案。

你可能感兴趣的:(Mysql)