参考文章:https://zhuanlan.zhihu.com/p/142491549
Mysql 保证数据不会丢从两个方面体现:
1)能够恢复到任何时间的状态
实现:binlog
2)如果Mysql突然宕机,那么之前的数据不会丢失(crash-safe)
实现:redo log和undo log
1.WAL机制
问题:为什么不直接更改磁盘中的数据,而要在内存中更改,然后还需要写日志,最后再落盘这么复杂?
这个问题相信很多同学都能猜出来,MySQL更改数据的时候,之所以不直接写磁盘文件中的数据,最主要就是性能问题。因为直接写磁盘文件是随机写,开销大性能低,没办法满足MySQL的性能要求。所以才会设计成先在内存中对数据进行更改,再异步落盘。但是内存总是不可靠,万一断电重启,还没来得及落盘的内存数据就会丢失,所以还需要加上写日志这个步骤,万一断电重启,还能通过日志中的记录进行恢复。
写日志虽然也是写磁盘,但是它是顺序写,相比随机写开销更小,能提升语句执行的性能(针对顺序写为什么比随机写更快,可以比喻为你有一个本子,按照顺序一页一页写肯定比写一个字都要找到对应页写快得多)。
这个技术就是大多数存储系统基本都会用的WAL(Write Ahead Log)技术,也称为日志先行的技术,指的是对数据文件进行修改前,必须将修改先记录日志。保证了数据一致性和持久性,并且提升语句执行性能。
2.核心日志模块
1.redo log 重做日志
redo log 是WAL机制的典型应用。当有事务提交时,对数据进行修改时,只会在内存中修改对应的数据页,并且记录redo log日志,完成后即表示事务提交成功,磁盘数据文件的更新由后台线程异步处理。
redo log是固定大小的,所以只能循环写,从头开始写,写到末尾就又回到开头,相当于一个环形。当日志写满了,就需要对旧的记录进行擦除,但在擦除之前,需要确保这些要被擦除记录对应在内存中的数据页都已经刷到磁盘中了。在redo log满了到擦除旧记录腾出新空间这段期间,是不能再接收新的更新请求,所以有可能会导致MySQL卡顿。(所以针对并发量大的系统,适当设置redo log的文件大小非常重要!!!)
2.undo log 回滚日志
保证了事务的原子性,在修改数据时,会记录一条与当前操作相反的逻辑日志到undo log中,(进行一个delete操作,undo log中增加一条insert操作),如果某个事务提交失败了,可以利用undo log 进行回滚。
3.binlog 归档日志
binlog产生于server层,与存储引擎无关。主要记录用户对数据库操作的SQL语句,binlog不会像redo log擦掉之前的记录循坏写,而是一直记录,直到达到有限期或者达到单个日志文件的最大大小。达到最大后从一个新的文件继续写。
所以基于此,binlog可以基于时间点恢复数据库。
那么回到刚才的问题,binlog可以简化掉吗?这里需要分场景来看:
- 如果是主从模式下,binlog是必须的,因为从库的数据同步依赖的就是binlog;
- 如果是单机模式,并且不考虑数据库基于时间点的还原,binlog就不是必须,因为有redo log就可以保证crash-safe能力了;但如果万一需要回滚到某个时间点的状态,这时候就无能为力,所以建议binlog还是一直开启;
根据上面对三个日志的详解,我们可以对这个问题进行解答:在主从模式下,三个日志都是必须的;在单机模式下,binlog可以视情况而定,保险起见最好开启。



