Mysql的深入浅出——日志模块和事务隔离
mysql日志模块
现在有一个场景,就是把你放到一个古老的社会里,没有计算机,你开了一个饭店,所有的客人都是赊账的方式,你怎么来记账? 不难想到,有两种方式:
1⃣️ 直接记下算好的每一桌结账的钱
2⃣️ 先将每一桌点的啥作为一条日志记录下来,之后再去算账单
显然第一种方式对于客流量较大的情况下,不可能客人都被阻塞了,等待你这边算完的结果才能吃饭,客人可能因为太慢而流失;而第二种先记下日志则可以很快作出响应,后后续再算账来保证数据一致性。
类似mysql也有同样的问题,是直接写磁盘还是先写日志再写磁盘,如果每一次的更新都写磁盘,那么整个IO的成本是很高的,所以mysql还是采取了WAL(Write-Ahead Logging),先写日志再写磁盘。
这就是引出了mysql重要的日志模块 redo log。
当有update语句执行的时候,Innodb引擎先会把记录写到redo log里,并且更新内存,这个时候更新就已经算完成了,同时,引擎也会在合适的时候将这个操作记录同步到磁盘上(就好比老板的店打烊的时候)
又一个问题来了,如果记账的本子写满了怎么办,如果是你,你肯定会拿另一个本子来记录新的账单,以前的账本里已经算过的可以擦出掉,以便重新记录新的,与此类似,innodb的redo log是固定大小的,比如可以配置为一组 4 个文件,每个文件 的大小是 1GB,那么这块“粉板”总共就可以记录 4GB 的操作。从头开始写,写到末尾就 又回到开头循环写,如下面这个图所示。
write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
write pos 和 checkpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。
有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe
说完存储引擎层的log,server也有对应的log,引出了 binlog。
一开始没有innodb的时候,只有binlog是保证补了crash-safe的,所以才有了后面的redo-log。
这两种日志有以下三点不同。
-
redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都 可以使用。
-
redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日 志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
-
redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
介绍完了之后对于update的流程可以如下图:
这就引出了 两阶段提交,两阶段提交是跨系统维持数据逻辑一致性时常用的一个方案,即使你不做数据库内核开发,日常开发中也有可能会用到。