了解了 SQL 执行的流程,知道每一条语句都经过连接器、查询存储、分析器、优化器、执行器最后到存储引擎的过程。查询语句是如此,更新语句也不例外。
不同的是,更新语句会修改表数据,这里就涉及到两个重要的日志模块 redolog 和 binlog。
本篇还是选用 InnoDB 搜索引擎。
系统的日志模块
之一 redolog
下面引入丁奇的经典比喻。
《孔乙己》这篇文章中,酒店掌柜有一个粉板,专门用来记录客人的赊账记录。
如果赊账的人不多,他可以把顾客名和账目写在板上。
但如果赊账的人多了,粉板总会有记不下的时候,这个时候掌柜一定还有一个专门记录赊账的账本。
如果有人要赊账或者还账的话,掌柜一般有两种做法:
一是直接把账本翻出来,把这次赊的账加上去或者扣除掉;
另一种是先在粉板上记下这次的账,等打烊以后再把账本翻出来核算。
在生意红火柜台很忙时,掌柜一定会选择后者,因为前者操作实在是太麻烦了。
首先,你得找到这个人的赊账总额那条记录。你想想,密密麻麻几十页,掌柜要找到那个名字,可能还得带上老
花镜慢慢找,找到之后再拿出算盘计算,最后再将结果写回到账本上。
你想想,如果掌柜没有粉板的帮助,每次记账都得翻账本,效率是不是低得让人难以忍受?
上面的比喻非常形象地说明了 Mysql 中的 WAL(Write-Ahead-Logging) 技术。
先写日志(redolog),再写磁盘。也就是先写粉板再写账本。
-
redolog 是 InnoDB 引擎特有的日志。
-
当有数据要更新时,InnoDB 会先记录 redolog,同时更新内存,更新操作就结束了。等到合适的时候(空闲或者粉板写满了),再更新磁盘。
-
redolog 有固定大小,就像固定大小的粉板,写满就得回到开头循环写。
-
write pos 是当前记录粉板的位置,一边写一边往后移。
-
check point 是擦除的位置,擦除前会把更新记录到数据文件。
-
redolog 保证了数据库异常重启时数据不丢失,这个能力被称为 crash-safe。
-
(丁奇原图)
- redolog 默认在 Mysql 的 data 目录下,ib_logfile0、ib_logfile1、。。。
mysql> show variables like "innodb_log_group_home_dir";
+---------------------------+-------+
| Variable_name | Value |
+---------------------------+-------+
| innodb_log_group_home_dir | ./ |
+---------------------------+-------+
1 row in set (0.00 sec)
- 设置 redolog 文件大小及个数,默认 2 个文件(innodb_log_files_in_group),每个文件 32M(innodb_log_file_size)
mysql> show variables like "innodb_log_file%";
+---------------------------+----------+
| Variable_name | Value |
+---------------------------+----------+
| innodb_log_file_size | 33554432 |
| innodb_log_files_in_group | 2 |
+---------------------------+----------+
2 rows in set (0.00 sec)
- 设置 redolog 实时直接持久化到磁盘上,保证重启后数据不丢失。
mysql> show variables like "innodb_flush_log_at_trx_commit";
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1 |
+--------------------------------+-------+
1 row in set (0.00 sec)
之二 binlog
-
binlog 是 Server 层的日志,叫归档日志。不同于 redolog 只有 InnoDB 有,binlog 任何引擎都可以使用。
-
binlog 是逻辑日志,记录语句的原始逻辑;redolog 是物理日志,记录数据页的修改内容。
-
binlog 不会循环写,一个写完后切换到写一个;redolog 有固定的大小。
-
binlog 在 data 目录下,mysql-bin.xxxxxx。
-
设置 binlog 每次事务直接持久化到磁盘上,保证重启后数据不丢失。
mysql> show variables like "sync_binlog";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog | 1 |
+---------------+-------+
1 row in set (0.00 sec)
update 的执行过程
update T set c=c+1 where ID=2;
-
执行器调用引擎接口找 ID=2 的数据,如果 ID=2 数据所在的数据页在内存中,则直接返回给执行器。否则先从磁盘读取再返回。
-
执行器拿到数据,把 c 加 1,得到新的一行数据。然后调用引擎接口,写入这行数据。
-
引擎将新数据更新到内存中,顺便记录 redolog,此时 redolog 处于 prepare 状态,然后告诉执行器可以提交事务。
-
执行器把 binlog 写入磁盘,然后调用引擎接口提交事务。redolog 状态变为 commit。更新完成。
数据恢复
既然记录了 log,那么当数据误操作后,就可以根据数据库定期备份,加上记录的 log 方便的恢复到误操作之前的状态。
-
先根据备份恢复到临时表
-
根据 binlog 找到备份后到误操作前的记录。
-
把恢复好的临时库按需要同步到正式库中。
只要有 log,就可以恢复到任何时刻的数据状态。
再也不用担心误操作无法恢复了。