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 的操作。从头开始写,写到末尾就又回到开头循环写
write pos
是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头checkpoint
是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件write pos
和checkpoint
之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果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
- 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
- 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N 1,得到新的一行数据,再调用引擎接口写入这行新数据。
- 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
- 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
- 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成
(图中浅色框表示是在InnoDB内部执行的,深色框表示在执行器中执行的)
可以看到,在写入 binlog 及事务提交前,innodb 先记录了 redolog,并标记为 prepare 状态,在事务提交后,innodb 会将 redolog 更新为 commit 状态,这样在异常发生时,就可以按照下面两条策略来处理:
- 当异常情况发生时,如果第一次写入 redolog 成功,写入 binlog 失败,MySQL 会当做事务失败直接回滚,保证了后续 redolog 和 binlog 的准确性
- 如果第一次写入 redolog 成功,binlog 也写入成功,当第二次写入 redolog 时候失败了,那数据恢复的过程中,MySQL 判断 redolog 状态为 prepare,且存在对应的 binlog 记录,则会重放事务提交,数据库中会进行相应的修改操作