Mysql 日志系统

bin-log & redo-log & undo log

1. 避免从删库到跑路 - bin log

怎么避免从删库到跑路 – 详解 mysql binlog 的配置与使用

1.1 什么是bin log

  • binlog 即二进制日志,他记录了引起或可能引起数据库改变事件,包括事件发生的时间、开始位置、结束位置等信息,select、show 等查询语句不会引起数据库改变,因此不会被记录在 binlog 中
  • 对于事务的执行,只有事务提交时才会一次性写入 binlog,对于非事务操作,则每次语句执行成功后都会直接写入 binlog
  • 因此,基于 binlog,我们可以看到每一次对数据库的修改是在何时以何种方式执行的,从而可以实现对任意条操作的回滚,当然
  • mysql 的主从同步机制也是依赖 binlog 来实现的,binlog 让从数据库可以精准还原主库的每一个操作

1.2 bin log结构

binlog是可以追加写入的,追加写入指的是binlog文件写到一定大小后会切换到下一个文件,并不会覆盖以前的文件

1.3 如何靠bin log恢复数据

bin log会记录所有的逻辑操作,并且采用“追加写”的形式,如果你的DBA承诺说半个月内可以恢复,那么备份系统中一定会保存最近半个月的所有bin-log,同时系统会定期做整库备份
当需要恢复到指定的某一秒时,比如某天下午两点发现中午12点有一次误删表,需要找回数据,那么可以这么做

  • 首先,找到最近的一次去全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备份恢复到临时库
  • 然后,从备份的时间点开始,将备份的binlog一次取出来,重放到中午误删表之前的那个时刻

这样你的临时库就和误删之前的线上库一样了,然后你可以把表数据从临时库中求出来,按需求恢复到线上库中

2. 异常情况下的事务安全 - 重做日志redo log

mysql日志系统之redo log和bin log
mysql 异常情况下的事务安全 – 详解 mysql redolog

2.1 更新操作是否应该直接操作磁盘数据?

对于每次更新来说,最简单的方法就是每次都把操作记录到磁盘,去磁盘找相应的数据,再进行更新,但这样频繁的IO操作会导致性能的下降

2.2 WAL技术

数据库如何用 WAL 保证事务一致性?
再同一事务中,当有记录需要更新时,InnoDB引擎将修改结果更新到内存后,会在redo log添加一行记录来记录“需要在哪个数据页上做什么修改”,并将该记录的状态置为prepare,等到commit提交事务后,会将此次事务中在redo log添加的记录的状态都置为commit状态,同时,InnoDB引擎会在适当的时候,将redo log中状态为commit的记录的修改更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做

这样的操作叫做Write Ahead Logging,他的关键在于先写日志,再写磁盘

写日志也是在磁盘上的写操作,为什么比直接在磁盘持久化数据高效?
WAL是顺序写入的,也就是一直在文件末尾append,而持久化数据库的数据是一个随机写入的操作,顺序写会节省大量磁盘悬臂来回寻址的过程,效率更高

现在是否还需要WAL?
现在都用SSD而不在使用HARD,SSD没有机械结构,无需寻道,那么上面所说的优点是否就不存在了?

2.3 redo-log的结构

InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么这块“粉板”总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写
Mysql 日志系统

  • write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头
  • checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件
  • write poscheckpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下

2.4 crash-safe

有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,当数据库发生宕机重启后,可以通过redo log将未落盘的数据恢复,这个能力称为 crash-safe

2.5 redo log是如何保证crash safe

每条 redolog 都有两个状态 – prepare 与 commit 状态

例如对于一张 mysql 表

(CREATE TABLE `A` (`ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `C` int(10) NOT NULL DEFAULT 0, PRIMARY KEY (`ID`)) ENGINE=InnoDB)

我们执行一条 SQL 语句:

mysql> update T set c=c+1 where ID=2
  1. 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  2. 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成

Mysql 日志系统
(图中浅色框表示是在InnoDB内部执行的,深色框表示在执行器中执行的)

可以看到,在写入 binlog 及事务提交前,innodb 先记录了 redolog,并标记为 prepare 状态,在事务提交后,innodb 会将 redolog 更新为 commit 状态,这样在异常发生时,就可以按照下面两条策略来处理:

  1. 当异常情况发生时,如果第一次写入 redolog 成功,写入 binlog 失败,MySQL 会当做事务失败直接回滚,保证了后续 redolog 和 binlog 的准确性
  2. 如果第一次写入 redolog 成功,binlog 也写入成功,当第二次写入 redolog 时候失败了,那数据恢复的过程中,MySQL 判断 redolog 状态为 prepare,且存在对应的 binlog 记录,则会重放事务提交,数据库中会进行相应的修改操作
上一篇:全网最牛X的!!! MySQL两阶段提交串讲


下一篇:mysql锁