binlog记录了数据库表结构和表数据变更,比如update/delete/insert/truncate/create
。它不会记录select(因为这没有对表没有进行变更)
binlog我们可以简单理解为:存储着每条变更的SQL语句(当然不止SQL,还有XID「事务Id」等等)
主要有两个作用:复制和恢复数据
binlog
来实现的binlog
来对数据进行恢复。因为binlog
记录了数据库表的变更,所以我们可以用binlog
进行复制(主从复制)和恢复数据。
假设我们执行下面一条SQL语句:
update user set name='zhangsan' where id=5;
MySQL执行这条SQL语句,需要先把这条记录所在的页找到,然后把页加载到内存中,最后再对对应的记录进行修改。(因为MySQL的基本存储结构是页,记录都存在页里)
存在的问题:如果在内存中把数据改了,还没来得及落磁盘,而此时的数据库挂了怎么办?显然这次更改就丢了。
但是如果每个请求都需要将数据立马落磁盘之后,那速度会很慢,MySQL可能也顶不住。所以MySQL是怎么做的呢?
MySQL引入了redo log
,内存写完了,然后会写一份redo log
,这份redo log
记载着这次在某个页上做了什么修改。
其实写redo log
的时候,也会有buffer
,是先写buffer
,再真正落到磁盘中的。至于从buffer什么时候落磁盘,会有配置供我们配置。
写redo log
也是需要写磁盘的,但它的好处就是顺序IO
(我们都知道顺序IO比随机IO快非常多)。
所以,redo log
的存在为了:当我们修改的时候,写完内存了,但数据还没真正写到磁盘的时候。此时我们的数据库挂了,我们可以根据redo log
来对数据进行恢复。因为redo log
是顺序IO,所以写入的速度很快,并且redo log
记载的是物理变化(xxxx页做了xxx修改),文件的体积很小,恢复速度很快。
redo log写磁盘比一般的写磁盘快的原因:
binlog
记载的是update/delete/insert
这样的SQL语句redo log
记载的是物理修改的内容(xxxx页修改了xxx)。redo log
的作用是为持久化而生的。
写完内存,如果数据库挂了,那我们可以通过redo log
来恢复内存还没来得及刷到磁盘的数据,将redo log
加载到内存里边,那内存就能恢复到挂掉之前的数据了。
binlog
的作用是复制和恢复而生的。
主从服务器需要保持数据的一致性,通过binlog
来同步数据。
如果整个数据库的数据都被删除了,binlog
存储着所有的数据变更情况,那么可以通过binlog
来对数据进行恢复。
又看到这里,你会想:”如果整个数据库的数据都被删除了,那我可以用redo log
的记录来恢复吗?不能
因为功能的不同,redo log
存储的是物理数据的变更,如果我们内存的数据已经刷到了磁盘了,那redo log
的数据就无效了。所以redo log
不会存储着历史所有数据的变更,文件的内容会被覆盖的。
redo log
是MySQL的InnoDB引擎所产生的。
binlog
无论MySQL用什么引擎,都会有的。
InnoDB是有事务的,事务的四大特性之一:持久性就是靠redo log来实现的(如果写入内存成功,但数据还没真正刷到磁盘,如果此时的数据库挂了,我们可以靠redo log来恢复内存的数据,这就实现了持久性)。
上面也提到,在修改的数据的时候,binlog
会记载着变更的类容,redo log
也会记载着变更的内容。(只不过一个存储的是物理变化,一个存储的是逻辑变化)。那他们的写入顺序是什么样的呢?
redo log
事务开始的时候,就开始记录每次的变更信息于是新有的问题又出现了:我写其中的某一个log,失败了,那会怎么办?现在我们的前提是先写redo log
,再写binlog
,我们来看看:
redo log
失败了,那我们就认为这次事务有问题,回滚,不再写binlog。redo log
成功了,写binlog
,写binlog写一半了,但失败了怎么办?我们还是会对这次的事务回滚,将无效的binlog
给删除(因为binlog会影响从库的数据,所以需要做删除操作)redo log
和binlog
都成功了,那这次算是事务才会真正成功。简单来说:MySQL需要保证redo log
和binlog
的数据是一致的,如果不一致,那就乱套了。
redo log
写失败了,而binlog
写成功了。那假设内存的数据还没来得及落磁盘,机器就挂掉了。那主从服务器的数据就不一致了。(从服务器通过binlog
得到最新的数据,而主服务器由于redo log
没有记载,没法恢复数据)redo log
写成功了,而binlog
写失败了。那从服务器就拿不到最新的数据了。最后,那么当我执行一条 update
语句时,redo log
和 binlog
是在什么时候被写入的呢?这就有了我们常说的「两阶段提交」:
redo log
(prepare)binlog
redo log
(commit)为什么 redo log 要分两个阶段: prepare 和 commit ?redo log 就不能一次写入吗?
我们分两种情况讨论:
先写 redo log,再写 binlog
这样会出现 redo log 写入到磁盘了,但是 binlog 还没写入磁盘,于是当发生 crash recovery 时,恢复后,主库会应用 redo log,恢复数据,但是由于没有 binlog,从库就不会同步这些数据,主库比从库“新”,造成主从不一致
先写 binlog,再写 redo log
这样造成从库比主库“新”,也会造成主从不一致
而两阶段提交,就解决这个问题,crash recovery 时:
两阶段提交,其实是为了保证 redo log 和 binlog 的逻辑一致性。
undo log
主要有两个作用:回滚和多版本控制(MVCC)
在数据修改的时候,不仅记录了redo log
,还记录undo log
,如果因为某些原因导致事务失败或回滚了,可以用undo log
进行回滚
undo log主要存储的也是逻辑日志,比如我们要insert
一条数据了,那undo log会记录的一条对应的delete
日志。我们要update
一条记录时,它会记录一条对应相反的update记录。
这也应该容易理解,毕竟回滚嘛,跟需要修改的操作相反就好,这样就能达到回滚的目的。因为支持回滚操作,所以我们就能保证:“一个事务包含多个操作,这些操作要么全部执行,要么全都不执行”。【原子性】
因为undo log存储着修改之前的数据,相当于一个前版本,MVCC实现的是读写不阻塞,读的时候只要返回前一个版本的数据就行了。