1.两个重要的日志
1.1 一条SQL语句是如何更新的?
更新的SQL语句和查找的SQL语句在一些流程上是一致的。
更新操作也需要连接数据库,进行身份验证以及权限获取,再由分析器进行词法分析和语法分析,生成语法树,知道这是一条SQL语句。再由优化器进行索引的选择以及决定表的连接顺序,生成执行计划。再由执行器调用存储引擎提供的接口,更新数据库中的数据。
但是,如果我们每一条更新操作都写到到磁盘中,那么先从磁盘中找到这条记录,再进行修改,最后更新到磁盘中。这样IO成本和查询成本都很高。
1.1.1怎么解决这样的问题呢?
使用WAL技术,即Write Ahead Logging,这个技术的关键点是先做日志,再写磁盘。先把这样的更新操作保存到redo log中,在MySQL数据库空闲的时候,再把这些信息更新到磁盘中。
1.2 Redo Log(重做日志)
redo日志是固定大小的。如果redo log写完了,就会循环的从头开始写,覆盖掉原来的内容。
- 如上图,write pos向右移动,边移动边把对数据库的更新、删除或者修改操作保存下来。
- checkpoint同样向右移动,并且是循环的,边移动边擦除数据,在擦除前把擦除的记录都更新到磁盘上。
- write pos与checkpoint之间的空白区域就是可以写的区域。如果write pos追上了checkpoint,则停止更新,让checkpoint向右移动一段空间,擦除一些数据。
有了redo log,即使MySQL数据库因为某些原因导致重启,也不会丢失已经提交的记录。这种能力就是crash-safe。
1.3 bin log(归档日志)
redo log日志是innoDB存储引擎专用的日志,拥有carsh-safe的能力。而实际上在MySQL的Server层上还实现了一种日志—Bin Log(归档日志)。
常用于数据库异常重启后的数据库恢复以及数据库扩容从库的实现。
1.3.1 怎么使用bin log日志进行数据库的恢复和扩容?
①找到最近的一次全量备份,从这个备份恢复临时库。
②从备份的时间点开始,将备份的bin log一次取出,重放到目标时刻。
③把这个临时库恢复到线上库中去。
扩容添加从库也是相同的原理,先恢复全量备份,再重放bin log到目的时间点。
1.3.2 redo log和bin log的区别
①redo log是InnoDB存储引擎独有的。而bin log是Server层实现的,所有的存储引擎都共用bin log
②redo log是物理记录(“在呢个表上那一条记录做了什么操作”),bin log是逻辑记录(类似于像sql语句的那一种)。
③redo log是循环写的,如果写满了就从头再写。bin log不是固定大小的,如果bin log写满了一定的空间,就会进行追加,切换到另一个空间再写,并不会覆盖原来的日志。
④redo log是拥有crash-safe能力的,而binlog是没有这个能力的。
1.3.3 InnoDB存储语句对一条简单的SQL语句的执行过程
①执行器先通过条件找到内存中的这一条记录,如果有索引,就用索引来查找。如果这条记录不在内存中,则从磁盘中把数据记加载到内存中,再返回给执行器,如果在内存中,直接返回。
②执行器找到这条记录后,对其进行更新操作,再使用引擎接口这条记录更新到内存中。
③引擎将将这条数据更新到内存中,并且将这个更新操作记录到到redo log中,此时redo log进入了prepare状态。然后告知执行器完成了,随时准备提交事务。
④执行器这个更新操作记录到bin log日志中,并把bin log写入磁盘。
⑤执行器调用存储引擎的事务提交接口,引擎把刚刚写的redo log改成提交状态,更新完成。
上图深色框表示执行器完成的,浅色框表示执行引擎完成的。
1.4 两阶段提交
值得注意的是,在上述更新语句执行的过程中,我们使用了两阶段提交的方式。那我们为什么要使用两阶段提交呢?
1.4.1 为什么使用两阶段提交
目的:为了让两份日志之间的逻辑一致。
我们可以假设不使用两阶段提交,当MySQL出现异常重启的情况:
1)在redo log提交事务后,没有把更新操作记录到bin log中,MySQL重启
①此时redo log日志提交,但是更新操作还没有记录到bin log中,此时MySQL发生了异常重启。
②因为这个操作在redo log中已经提交了,所以MySQL在恢复的时候这一条操作并不会丢失。
③但是在你的bin log中是没有这一条记录的,所以当你使用bin log进行临时库恢复时,可能会导致最后的数据库少了一次更新。
2)在把更新操作记录到了bin log下,MySQL发生了异常重启的情况
①此时这一条更新操作已经被记录到了bin log中了,但是还没有在redo log中提交。
②所以,在MySQL数据库重启后,这一条语句的更新是不会被恢复的。但是在bin log中多出了这一条更新记录。
③如果你使用bin log日志进行临时库的恢复时,可能会导致数据库中多了一条更新记录。
所以,我们不使用两阶段提交就会导致数据库的状态和它用日志恢复出来的状态不一致。
当你使用bin log扩容增加从库时,会导致主从库中的数据不一致。
\