1.MYSQL(一)---基础架构(查询)
2.MYSQL(二)---日志系统(更新)
3.MYSQL(三)---事务隔离
在上文中讲到了数据库的架构和查询,在我们使用数据库的时候的最常见的查询的流程。
数据在进行查询的时候会有缓存,但是当更新了这个表数据的时候,这个缓存就会进行清空,这个数据更新的过程是怎么执行的呢?
同时作为开发我们肯定是了解数据库使用日志的重要性,数据的丢失恢复则是需要日志系统。那么我们是如何进行准确的恢复数据的呢?
update T set c=2 where id=2;
这个操作的流程我们还是可以从这个流程图上来理解:
我们执行这条更新语句,和查询相同的就是我们先需要找个这条数据,这个过程和上边几户一样,找到数据以后进行更新操作,但是就是在这个更新的过程涉及到的新知识,就是redo log和binlog。这个实在MySQL5.6开始执行的新特性有的。
redo log
什么是redo log?
举个简单的例子来说:孔乙己中酒馆老板记赊账依靠的是一个小黑板和账本,如果赊账的人不多会写在黑板上进行记录,如果人多了,黑板写不下,就会找账本进行记录。
如果有人要进行还账或者赊账的话,会有以下操作:
- 直接查账本,对账本上进行操作
- 在黑板上记录,满了或者打烊了再在账本进行操作
在生意火爆的时候,操作肯定是先记在黑板上,然后在进行账本上统一操作。(这就对比了数据库插入的高并发)。这就是WAL技术,WAL的全称是Write-Ahead Logging,就是先写日志,再写磁盘。
具体来说,当有更新需求来的时候,InnoDB就会把这个更新写到redo log里面,并更新内存,同时,InnoDB会在空闲的时候将数据更新进磁盘。
这个时候会发现另一个问题,黑板(redo log)不够用了怎么办?这个时候就不要停下来先计入账本(更新进磁盘),同时会将写进磁盘的数据在redo log进行抹除。
有了redo log,InnoDB就会保证在数据库发生了异常之后,之前提交的记录不会丢失,这个能力称为crash-safe。
什么是crash-safe?
CrashSafe指MySQL服务器宕机重启后,能够保证:
1.所有已经提交的事务的数据仍然存在。
2.所有没有提交的事务的数据自动回滚。
binlog
MySQL是分为两层的,server和引擎,其中上边的redo log是InnoDB引擎中的日志,server中得日志就是binlog。
既然是日志,为嘛要分为两个?
MySQL开始自带的引擎是MyISAM,主要实现的功能是server层的功能,但是它没有crash-safe得功能,而InnoDB是另一个公司以插件形式引入MySQL的,既然只依靠binlog是没有crash-safe能力的,所以InnoDB使用另外一套日志系统——也就是redo log来实现crash-safe能力。
这两种日志有以下三点不同:
1.redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。
2.redo log是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”。
3.redo log是循环写的,空间固定会用完;binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
有了对这两个日志的概念性理解,我们再来看执行器和InnoDB引擎在执行这个简单的update语句时的内部流程。
1.执行器先找引擎取ID=2这一行。ID是主键,引擎直接用树搜索找到这一行。如果ID=2这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
2.执行器拿到引擎给的行数据,把这个值加上1,比如原来是N,现在就是N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
3.引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态。然后告知执行器执行完成了,随时可以提交事务。
4.执行器生成这个操作的binlog,并把binlog写入磁盘。
5.执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成。
两阶段提交
为什么必须有“两阶段提交”呢?这是为了让两份日志之间的逻辑一致。要说明这个问题,我们得从文章开头的那个问题说起:怎样让数据库恢复到半个月内任意一秒的状态?
前面我们说过了,binlog会记录所有的逻辑操作,并且是采用“追加写”的形式。如果你的DBA承诺说半个月内可以恢复,那么备份系统中一定会保存最近半个月的所有binlog,同时系统会定期做整库备份。这里的“定期”取决于系统的重要性,可以是一天一备,也可以是一周一备。
当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:
首先,找到最近的一次全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备份恢复到临时库;
然后,从备份的时间点开始,将备份的binlog依次取出来,重放到中午误删表之前的那个时刻。
这样你的临时库就跟误删之前的线上库一样了,然后你可以把表数据从临时库取出来,按需要恢复到线上库去。好了,说完了数据恢复过程,我们回来说说,为什么日志需要“两阶段提交”。这里不妨用反证法来进行解释。由于redo log和binlog是两个独立的逻辑,如果不用两阶段提交,要么就是先写完redo log再写binlog,或者采用反过来的顺序。我们看看这两种方式会有什么问题。
仍然用前面的update语句来做例子。假设当前ID=2的行,字段c的值是0,再假设执行update语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了crash,会出现什么情况呢?
1.先写redo log后写binlog。假设在redo log写完,binlog还没有写完的时候,MySQL进程异常重启。由于我们前面说过的,redo log写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行c的值是1。但是由于binlog没写完就crash了,这时候binlog里面就没有记录这个语句。因此,之后备份日志的时候,存起来的binlog里面就没有这条语句。然后你会发现,如果需要用这个binlog来恢复临时库的话,由于这个语句的binlog丢失,这个临时库就会少了这一次更新,恢复出来的这一行c的值就是0,与原库的值不同。
2.先写binlog后写redo log。如果在binlog写完之后crash,由于redo log还没写,崩溃恢复以后这个事务无效,所以这一行c的值是0。但是binlog里面已经记录了“把c从0改成1”这个日志。所以,在之后用binlog来恢复的时候就多了一个事务出来,恢复出来的这一行c的值就是1,与原库的值不同。
可以看到,如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。
你可能会说,这个概率是不是很低,平时也没有什么动不动就需要恢复临时库的场景呀?
其实不是的,不只是误操作后需要用这个过程来恢复数据。当你需要扩容的时候,也就是需要再多搭建一些备库来增加系统的读能力的时候,现在常见的做法也是用全量备份加上应用binlog来实现的,这个“不一致”就会导致你的线上出现主从数据库不一致的情况。
简单说,redo log和binlog都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。
问题:
什么是redog和binlog?
redolog是对记录修改之后的物理日志,物理日志就是说redolog保存的是某一行数据修改之后的值,比如把id=1这行的某个属性由1改成2,redolog记录的就是这个2.redolog是InnoDB引擎层的。
相比于redolog,binlog是逻辑日志。其中一种形式是记录的原始sql语句,比如update t set c = c +1 where id = 1; binlog是数据库server层的。
为什么需要redolog?
由于更新数据的时候引擎并不是按条更新的,而是以页为最小单位更新。如果没有redolog,每次更新一条数据都要把整页的数据刷新到磁盘。如果更新多条数据,很可能一次就要更新多页,而且这些io是随机io,磁盘i/o就会多且慢。有了redolog,就不需要每次直接按页更新磁盘,而是把更新写到redolog中,然后等空闲时间再把redolog中的更新写入到磁盘。这样做的好处是redolog是顺序写的,而且是按条不是按页写。所以虽然多了一步,实际上是比直接更新快的。
为什么binlog不能做到crash-safe
假如只有binlog,有可能先提交事务再写binlog,有可能事务提交数据更新之后数据库崩了,还没来得及写binlog。我们都知道binlog一般用来做数据库的主从复制或恢复数据库,这样就导致主从数据库不一致或者无法恢复数据库了。同样即使先写binlog再提交事务更新数据库,还是有可能写binlog成功之后数据库崩掉而导致数据库更新失败,这样也会导致主从数据库不一致或者无法恢复数据库。所以只有binlog做不到crash-safe。为了支持crash-safe,需要redolog,而且为了保证逻辑一致,事务提交需要两个阶段:prepare阶段和commit阶段。写redolog并落入磁盘(prepare状态)-->写binlog-->commit。commit的时候是不会落盘的。
那么为什么要在prepare阶段就落盘呢?
如果binlog写成功之后,将redolog置成commit的时候数据库崩了,如果在commit的时候redolog才落盘,由于事务是否成功以binlog为依据,上面的情况下事务是成功的,但是redolog没有写到磁盘,丢了。恢复之后数据库与binlog就不一致了。如果在prepare阶段落盘,上面的情况下redolog已经写入到文件了(在prepare阶段已经写盘了),恢复的时候不会丢数据。
同样的,如果不分两个阶段。假如redolog和binlog独立,那么还是会出现“为什么binlog不能做到crash-safe”里面描述的问题:数据库与binlog不一致。
参考连接:
1.https://www.jianshu.com/p/2f1585c7f2f3
2.https://blog.csdn.net/shaochenshuo/article/details/73239949