学习林晓斌老师的重学mysql第二课的笔记
更新语句的基本执行流程
当数据库连接成功之后,我们就可以进行我们的操作了,于是你撸起衣袖,霹雳吧啦,终于写出一条sql,通过mysql客户端将它传给mysql服务器。
服务器拿到sql之后
- 首先会对它进行词法分析和语法分析:
1、确定属性:知道他是查询sql,还是更像sql,还是其他什么玩意儿;
2、检查语法:你的sql有没有写错,里面的表啊,字段呀是不是对的; - 要是都没有问题就送给优化器去“优化”:
1、执行计划的生成
2、索引的选择 - 优化完事之后就会送到执行器去执行了。
整个更新sql的执行就是酱。
但是,相比查询SQL,更新SQL还涉及到对日志模块的操作
MySQL可以恢复到半个月内任意一秒的状态
这是怎么实现的呢?
这就要说到Mysql里面两个重要的日志了:binlog 和 redo log
redo log
redo log是记录某数据页做了什么改动。
问题:
如果每一次的更新操作都需要写进磁盘,然后磁盘需要找到你要更新的那条记录更新并重新写回,整个过程的IO成本、查找成本都非常高。
解决方案:
WAL技术:Write-ahead Logging,先写日志,再写磁盘。
当有一条记录需要更新的时候,它会直接更新到内存,然后InnoDB引擎会把操作记录(在某个数据页上做了什么修改)写到redo log里面,并将该记录状态置为prepare,等到commit提交事务后,会将此次事务中在redo log添加的记录的状态都置为commit状态,最后等到适当的时候(系统空闲...)再把redo log中状态为commit的记录都写入磁盘,这个时候对外更新就完成了。
特点:
redo log是固定大小的,可以自己配置,通过修改配置参数innodb_log_files_in_group和innodb_log_file_size配置日志文件数量和每个日志文件大小,它采用循环写的方式记录,当写到结尾时,会回到开头循环写。
有了redo log,Inno DB就可以保证即时数据库发生异常,之前的记录都不会丢失,这个能力就是crash-safe.
bin log
redo log是innoDB引擎特有的,其实mySQL服务层也有自己的日志:bin log(归档日志),bin log是逻辑日志,记录语句的原始执行逻辑,比如“给id为2的行的A字段加1”
它有两种模式:
statement模式:记录sql语句。
row模式:记录行的内容,记录两条,更新前的内容和更新后的内容。
redo log VS bin log
redo log | InnoDB引擎特有 | 固定空间,循环写入,逻辑上是环形空间,写满之后从头部开始覆盖写 | 物理日志,记录“在某个数据页上做了什么修改” |
---|---|---|---|
binlog | mysql服务层自带,适用于所有引擎 | 不固定空间,追加写入日志,写满则切换下一文件 | 逻辑日志,记录语句的原始执行逻辑,比如“给id为2的行的A字段加1” |
inno DB在执行更新sql时的流程图

其中这一部分被被称为两阶段提交:

两阶段提交
两阶段提交是为了让redolog和bin log的逻辑一致
假如没有两阶段提交的逻辑
以给id为2的行的A字段加1的更新sql为例(这一行A字段更新之前为0):
1、先写redo log,再写bin log.
写完redo log 之后,bin log还没有写完的时候,系统crash了,MySQL进程异常重启,由于redo log已经写完了,系统根据redo log 成功把数据恢复过来了,所以恢复之后A字段的值为1.
但是由于bin log还没有写完的时候,系统就crash了,bin log里面没有这条日志,之后备份日志的时候,存起来的bin log也就不会有这条日志,后续如果需要用这个备份来恢复数据的话,相应的数据就不会有这一条更新,恢复出来的数据就会是0,出现错误。
2、先写bin log,再写redo log.
写完bin log 之后,redo log还没有写完的时候,系统crash了,由于redo log没有写,因此My SQL服务恢复之后这个事务无效,所以更新失败,A字段的值为0,但是bin log已经成功记录了该次操作,所以之后用bin log恢复数据库的时候就会导致没有多更新了数据,出现错误。
总得来说:
使用InnoDB引擎的情况下,数据库crash之后是否依然更新成功取决于redolog是否写入成功;至于能否使用备份把数据库恢复依靠的是bin log.所以如果这两个日志的逻辑不一致,就可能出现数据库经过备份恢复之后出现数据错误的情况。
而目前我们往往采取搭建多个备库的方法来提升系统的读能力,而这些备库常见的实现方式是通过全量备份加上应用bin log来实现,所以如果bin log和 redo log 不一致就会导致你的主从数据库不一致。
建议
1、设置参数 innodb_flush_log_at_trx_commit = 1(每次事务的redo log都持久化到磁盘),保证mysql异常重启之后数据不会丢失。
2、设置参数 sync_binlog = 1(每次事务的bin log都持久化到磁盘),保证mysql异常重启之后bin log不会丢失。
Extra
1、为什么不直接去掉bin log,只使用redo log
答:
- redo log是inno DB引擎独有,而MySQL的默认存储引擎是MyISAM。
- 而其redo log是固定大小,并且循环写入的,不持久保存,而bin log是持久化到磁盘的。
2、定期全量备份的周期取决于系统重要性,有的一天一备,有的一周一备,什么场景下一天一备比一周一备更有优势呢?或者说它影响了数据库系统的哪一个指标?
答:
一天一备和一周一备影响的数据库系统指标是RTO(目标恢复时间)
一天一备最坏的情况,也只需要应用一天的binlog,如果你每天0点做一次全量备份,但是你现在需要恢复一个到昨天晚上23点的备份。
相应的一周一备就需要应用一周的bin log了。
所以一天一备比一周一备的“最长恢复时间”更短。
但是频繁全量备份需要消耗更多的存储空间,所以这个短的RTO是用成本换来的,需要根据业务重要性来评估选择。
网友评论