前面讲解了一个查询语句的执行过程,一条查询语句的执行过程一般是经过连接器,解析器,优化器,执行器等模块,最后到存储引擎。
那么,一条更新语句的执行流程又是怎样的呢?
执行更新语句时,解析器会通过词法和语法解析知道这是一条更新语句。优化器决定是否可使用索引。然后,执行器负责具体执行,找到这一行,然后更新。
与查询流程不一样的是,更新流程还涉及两个重要的日志模块:redo log(重做日志),binlog(归档日志)。
如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程IO成本、查找成本都很高。
于是MySQL用到了WAL技术,WAL的全称是Write-Ahead Logging,它的关键点就是先写日志,再写磁盘。
具体来说,当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log里面,并更新内存,这个时候更新就算完成了。同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做。
有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
下面说一下2种日志的区别
redo log(重做日志)
redo log是InnoDB引擎特有的。
redo log是物理日志,记录的是在某个数据页上做了什么修改。
redo log是循环写的,空间固定会用完。
基于crash recovery,保证MySQL宕机后的数据恢复。
binlog(归档日志)
binlog是MySQL的Server层实现的,所有引擎都可以使用。
binlog是逻辑日志,记录所有的逻辑操作,记录的是这个语句的原始逻辑(有两种格式,statement 格式的话是记sql语句, row格式会记录行的内容,记两条,更新前和更新后都有)。
binlog是可以追加写入的。追加写是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
基于point-in-time recovery,保证服务器可以基于时间点对数据进行恢复,或者对数据进行备份。
有了对这两个日志的理解,再来看执行器和InnoDB引擎在执行update语句时的内部流程。
例:
create table t(id int primary key, f int);
update t set f=f+1 where id=1;
- 执行器先调引擎取id=1这一行。id是主键,引擎直接用树搜索找到这一行。如果id=1这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
- 执行器拿到引擎给的行数据,把这个值加上1,比如原来是n,现在就是n+1,得到新的一行数据,再调用引擎接口写入这行新数据。
- 引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务。
- 执行器生成这个操作的binlog,并把binlog写入磁盘。
- 执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成。
找回记录操作
当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:
- 首先,找到最近的一次全量备份,从这个备份恢复到临时库;
- 然后,从备份的时间点开始,将备份的binlog依次取出来,重放到中午误删表之前的那个时刻。
这样你的临时库就跟误删之前的线上库一样了,然后你可以把表数据从临时库取出来,按需要恢复到线上库去。
小结
redo log用于保证crash-safe能力。
innodb_flush_log_at_trx_commit参数
设置成1:每次事务的redo log都直接持久化到磁盘。这个参数建议设置成1,这样可以保证MySQL异常重启之后数据不丢失。
设置成0:提交事务的时候,不立即把 redo log buffer 里的数据刷入磁盘文件的,而是依靠 InnoDB 的主线程每秒执行一次刷新到磁盘。此时可能你提交事务了,结果 mysql 宕机了,然后此时内存里的数据全部丢失。
值为2:提交事务的时候,把 redo 日志写入磁盘文件对应的 os cache 缓存里去,而不是直接进入磁盘文件,可能 1 秒后才会把 os cache 里的数据写入到磁盘文件里去。
查询 select @@innodb_flush_log_at_trx_commit;
sync_binlog参数
1:每次事务的binlog都持久化到磁盘。这个参数建议设置成1,这样可以保证MySQL异常重启之后binlog不丢失。
0:事务提交后,将二进制日志从缓冲写入磁盘,但是不进行刷新操作(fsync()),此时只是写入了操作系统缓冲,若操作系统宕机则会丢失部分二进制日志。
N:每写N次操作系统缓冲就执行一次刷新操作。将这个参数设为1以上的数值会提高数据库的性能,但同时会伴随数据丢失的风险。