MySQL-日志
MySQL中的日志文件,有这么两种与事务有关:undo日志与redo日志。
undo和redo: : 解决原子性、持久性
日志在内存即有缓存Log buffer,又有磁盘文件log file。
原子性(Atomicity):不全部成功,就全部恢复。
持久性**(Durability),一旦提交不可逆。
undo log 记录原始数据,用于保证事务原子性
redo log 记录修改数据,用于保证事务的持久性
redo log有自己的内存buffer,先写入到buffer,事务提交时(缓存满的时候)写入磁盘
redo log持久化之后,意味着事务是可提交**的
Undo Log
原理:
- 为了满足事务原子性,操作任何数据时,先将数据备份到undo_log,再进行数据的修改。如果出现了未知错误或者用户进行了ROLLBACK,db就可以利用undo_log 备份将数据恢复到事务开始之前的状态。
- DB: 写入数据到磁盘之前,先将数据缓存到内存中,事务提交之前才会写入磁盘中
简化过程:
假设有A、B两个数据,值分别为1,2
- 事务开始.
- 记录A=1到undo log.
- 修改A=3.
- 记录B=2到undo log.
- 修改B=4.
- 将undo log写到磁盘。
- 将数据写到磁盘。
- 事务提交
保证持久性:
- 事务提交前会将修改数据写到磁盘
- 也就是说一旦事务提交,肯定持久性
保证原子性:
- 每次对DB修改,都会把原始数据存到undo_log中,需要回滚时,直接读取undo_log,恢复数据
- 系统在7和8之间挂了,事务未提交,直接回滚,通过undo_log(6已持久化)
- 系统在7之前挂了,此时数据未持久化到硬盘(内存当中),挂了之后,内存清理,依然保存之前的状态。
缺陷:
cpu
磁盘(光盘) 激光探头读取数据,寻址—》读取数据
转速 4800 5200
顺序读写
随机读写 RAM
mysql索引 随机写
- 每个事务提交前数据和undo_log写入磁盘,造成大量磁盘IO,性能低
如果可以将数据缓存一段时间,就能减少IO提高性能。但是这样就会丧失事务的持久性。因此引入了另外一种机制来实现持久化,即Redo Log.
Redo Log
undo 记录旧数据,redo_log 记录修改之后的数据
在内存中
== 将数据写到磁盘(异步) == 随机写 寻址浪费大量时间
redo 与数据不太一样,redo_log是一种高性能写入方式,顺序写(连续空间),减少寻址的性能问题
真实场景:undo_log(随机写)写入redo_log.降低性能瓶颈
基本原理:
Undo + Redo事务的简化过程
- 事务开始.
- 记录A=1到undo log buffer.
- 修改A=3.
- 记录A=3到redo log buffer.
- 记录B=2到undo log buffer.
- 修改B=4.
- 记录B=4到redo log buffer.
- 将undo log写入磁盘(真实场景redo_log)
- 将redo log写入磁盘
- 事务提交
保证原子性:
- 事务提交前一步故障,通过undo_log 日志恢复
- 如果undo_log未写入,数据尚未持久化,无需回滚
保证持久性:
- 数据已经写入redo log,而redo log持久化到了硬盘,
- 只要到了步骤`9以后,事务可以提交的。
内存中的DB数据持久化到磁盘:
- redo log已经持久化,因此数据库数据写入磁盘与否影响不大,
- 为了避免出现脏数据(内存中与磁盘不一致),事务提交后也会将内存数据刷入磁盘(也可以按照固设定的频率刷新内存数据到磁盘中)。异步
redo log会在事务提交之前,或者redo log buffer满了的时候写入磁盘
极端问题
1、undo是写undo和数据库数据到硬盘,redo_是写undo和redo到磁盘,IO次数都是两次,没有减少
- DB数据写入是随机IO,性能很差
- redo log在初始化时会开辟一段连续的空间,写入是顺序IO,性能很好
- 真实场景,undo log并不是直接写入磁盘,而是先写入到redo log buffer中,当redo log持久化时,undo log就同时持久化到硬盘了。
- 故事务提交前,只需要对redo log持久化即可。
- redo log并不是写入一次就持久化一次,redo log在内存中也有自己的缓冲池:
redo log buffer
。每次写redo log都是写入到buffer,在提交时一次性持久化到磁盘,减少IO次数。
2、redo log 数据是写入内存buffer中,当buffer满或者事务提交时,将buffer数据写入磁盘。redo log中记录的数据,有可能包含尚未提交事务,如果此时数据库崩溃,那么如何完成数据恢复?
- 数据恢复有两种策略:
- 恢复时,只重做已经提交了的事务(redo_log 中挑undo)
- 恢复时,重做所有事务包括未提交的事务和回滚了的事务。然后通过Undo Log回滚那些未提交的事务(先redo_log顺序执行,后面在undo_log中找,再还原)
Inodb引擎采用的是第二种方案,因此undo log要在 redo log前持久化