MySQL笔记(二)一条SQL更新语句是如何执行的

一条SQL更新语句是如何执行的

执行语句前要先连接数据库,这是连接器的工作。因为查询缓存在大多数情况下使用,弊大于利。我们会经过一轮执行过程。

接下来,分析器会通过词法和语法解析知道这是一条更新语句。优化器决定要使用 ID 这个索引。
然后,执行器负责具体执行,找到这一行,然后更新。

这是查询流程的步骤,更新流程除了这些步骤还涉及两个重要的日志模块。

重要的日志模块: redo log

在MYSQL里,如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的记录再去更新,整个过程IO成本

和查找成本十分大。MySQL使用了WAL技术(Write Ahead Logging),具体就是先写日志,再写磁盘。

当有一条记录需要更新时,InnoDB引擎会先把记录写到redo log里,并更新内存,这个时候更新就算完成了。同时,将这个操作记录在系统空闲的时候更新到磁盘里面。同时InoDB的redo log是固定大小的,写完了再从头开始写。

有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前的记录都不会丢失(crash-safe).

重要的日志模块:binlog

redo log 是引擎层特有的日志,而Server层也有自己的日志,称为binlog(归档日志)。

因为最开始的时候MySQL里并没有InnoDB引擎,MySQL自带的引擎是MyISAM没有crash-sage的能力,

binlog日志只能用于归档。只依靠binlog是没有crash-safe能力的。redo log是InnoDB引擎为了实现crash-safe

能力而引入的。

这两种日志有几点不同:

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

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

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

有了对两个日志的理解,现在来看执行器和InnoDB在执行update语句时的内部流程,语句如下。

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

1.执行器先找引擎取ID=2这一行,ID是主键,引擎直接用树搜索找到这一行。如果ID=2这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。

2.执行器拿到引擎给的行数据,更新值,得到新的行数据,再调用引擎接口写入这行数据。

3.引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务。

4.执行器生成这个操作的binlog,并把binlog写入磁盘。

5.执行器调用引擎的提交事务接口,把刚刚写入的redo log改成commit状态,更新完成。

为什么要将redo log的写入拆成两步:prepare,commit?

为了防止系统崩溃,redo log和binlog先写和后写时的状态不一致。原数据库和恢复的临时数据库不一致。

innodb_flush_log_at_trx_commit

sync_binlog两个参数全部设置为1,使每次事务的日志文件都能持久化到磁盘上。异常重启后数据不丢失。

 

你可能感兴趣的:(MySQL学习笔记)