MySql基础架构如下:
一条更新语句要经过 连接器 分析器 优化器 执行器,比如我们执行 update T set a = a + 1 where id = 1,会将缓存中的T表数据全部清空,这也是为什么不建议使用缓存的原因。与查询流程不一样的是,更新操作还会设计二个很重要的模块 redo log(重做日志) 和 binglog(归档日志)
redo log
redo log是innodb特有的日志,更新一条数据,首先我们得从磁盘找到这条数据,然后进行更新,最后再写到磁盘中,整个过程的IO成本还有查询成本是很高的,如果数据
库的连接特别多的话,那数据库连接往往就不够用了,所以更新的时候先把更新的语句写到redo log日志文件中,等系统空闲的时候再刷新
到磁盘中,但是redo log的大小的有限的,当被写满了的时候,就需要将一些数据刷新到磁盘中去,然后将redo log空闲出来,然后继续
写。所以有了redo log即使数据库发生异常重启,数据也不会丢失,这个能力被称为crash-safe
binlog
binlog是Server层特有的日志,为什么有了redo log还会有binlog呢??再MySql没有Innodb存储引擎的时候MySql自带的MyISAM,但是
MyISAM是没有crash-safe能力的,binlog日志只能用于归档,而Innodb是另外一个公司以插件形式提供的,因为binlog没有crash-safe
能力。所以就有了redo log日志。
二种日志的区别
1:redo log是Innodb特有的,binlog是Server层的,所有引擎都可以使用
2:redo log是物理日志。记录的是在某个数据页上做了什么修改。binlog是逻辑日志,记录的是这个语句的原始逻辑,比如给ID=2这一
行的c字段加上1
3:redo log是循环写的,而binlog是追加写的,即使写满了,也会切换到下一个文件,而且不会覆盖以前的文件
现在来看看update语句的执行流程
1:执行器首先根据条件查找这一行数据,如果这行数据本来就是在内存中的就直接返回给执行器,否则就要从磁盘上查找读入内存,然后再返回
2:执行器拿到这条数据之后进行修改
3:执行器将这条新数据更新到内存中去,同时将这个更新操作添加到redo do日志文件中,此时redo log处于prepare状态,然后告知执
行器它已经执行完成了,可以提交事务
4:执行器生成这个操作的binlog,并把binlog写入到磁盘
5:执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(Commit)状态,更新完成
我们可以发现这里使用而俩阶段提交,也就是写入redo log和binlog是要求都成功的,为什么要这样做呢??
一:先写redo log 再写binlog
如果写入redo log成功,这时候数据宕机了,那么数据库恢复之后还是能将这条数据更新成功,比如 a = a + 1
,但是binlog是没有写成功的,如果那一条我们需要做数据恢复,binlog是没有这一条更新操作的,那么数据就不一致了
一:先写binlog,再写redo log
如果写入binlog成功,写入redo log失败,那么数据库恢复之后这个事务无效,a的值是不会加1的,但是binlog是记录了加1的这个操
作的,那么在以后数据恢复过程中,a的值就会被加1,与原来的值就会不同的
可以发现如果不使用俩阶段提交,那么数据库的状态跟它恢复过来的状态很可能会不一致
总结
1:redo log 用于保证 crash-safe 能力。innodb_flush_log_at_trx_commit 这个参数设置成1 的时候,
表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。
2:sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参
数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。