MySQL不会丢失数据的秘密,就藏在它的 7种日志里

本文收录在 GitHub 地址 https://github.com/chengxy-nds/Springboot-Notebookmysql

进入正题前先简单看看MySQL的逻辑架构,相信我用的着。git

MySQL逻辑架构

MySQL的逻辑架构大体能够分为三层:程序员

  • 第一层:处理客户端链接、受权认证,安全校验等。github

  • 第二层:服务器server层,负责对SQL解释、分析、优化、执行操做引擎等。面试

  • 第三层:存储引擎,负责MySQL中数据的存储和提取。sql

咱们要知道MySQL的服务器层是无论理事务的,事务是由存储引擎实现的,而MySQL中支持事务的存储引擎又属InnoDB使用的最为普遍,因此后续文中提到的存储引擎都以InnoDB为主。数据库

MySQL数据更新流程

记住! 记住! 记住! 上边这张图,她是MySQL更新数据的基础流程,其中包括redo logbin logundo log三种日志间的大体关系,好了闲话少说直奔主题。缓存

redo log(重作日志)

redo log属于MySQL存储引擎InnoDB的事务日志。安全

MySQL的数据是存放在磁盘中的,每次读写数据都需作磁盘IO操做,若是并发场景下性能就会不好。为此MySQL提供了一个优化手段,引入缓存Buffer Pool。这个缓存中包含了磁盘中部分数据页(page)的映射,以此来缓解数据库的磁盘压力。服务器

当从数据库读数据时,首先从缓存中读取,若是缓存中没有,则从磁盘读取后放入缓存;当向数据库写入数据时,先向缓存写入,此时缓存中的数据页数据变动,这个数据页称为脏页Buffer Pool中修改完数据后会按照设定的更新策略,按期刷到磁盘中,这个过程称为刷脏页

MySQL宕机

若是刷脏页还未完成,可MySQL因为某些缘由宕机重启,此时Buffer Pool中修改的数据尚未及时的刷到磁盘中,就会致使数据丢失,没法保证事务的持久性。

为了解决这个问题引入了redo log,redo Log如其名侧重于重作!它记录的是数据库中每一个页的修改,而不是某一行或某几行修改为怎样,能够用来恢复提交后的物理数据页,且只能恢复到最后一次提交的位置。

redo log用到了WAL(Write-Ahead Logging)技术,这个技术的核心就在于修改记录前,必定要先写日志,并保证日志先落盘,才能算事务提交完成。

有了redo log再修改数据时,InnoDB引擎会把更新记录先写在redo log中,在修改Buffer Pool中的数据,当提交事务时,调用fsync把redo log刷入磁盘。至于缓存中更新的数据文件什么时候刷入磁盘,则由后台线程异步处理。

注意:此时redo log的事务状态是prepare,还未真正提交成功,要等bin log日志写入磁盘完成才会变动为commit,事务才算真正提交完成。

这样一来即便刷脏页以前MySQL意外宕机也不要紧,只要在重启时解析redo log中的更改记录进行重放,从新刷盘便可。

大小固定

redo log采用固定大小,循环写入的格式,当redo log写满以后,从新从头开始如此循环写,造成一个环状。

那为何要如此设计呢?

由于redo log记录的是数据页上的修改,若是Buffer Pool中数据页已经刷磁盘后,那这些记录就失效了,新日志会将这些失效的记录进行覆盖擦除。

上图中的write pos表示redo log当前记录的日志序列号LSN(log sequence number),写入还未刷盘,循环日后递增;check point表示redo log中的修改记录已刷入磁盘后的LSN,循环日后递增,这个LSN以前的数据已经全落盘。

write poscheck point之间的部分是redo log空余的部分(绿色),用来记录新的日志;check pointwrite pos之间是redo log已经记录的数据页修改数据,此时数据页还未刷回磁盘的部分。当write pos追上check point时,会先推进check point向前移动,空出位置(刷盘)再记录新的日志。

注意:redo log日志满了,在擦除以前,须要确保这些要被擦除记录对应在内存中的数据页都已经刷到磁盘中了。擦除旧记录腾出新空间这段期间,是不能再接收新的更新请求的,此刻MySQL的性能会降低。因此在并发量大的状况下,合理调整redo log的文件大小很是重要。

crash-safe

由于redo log的存在使得Innodb引擎具备了crash-safe的能力,即MySQL宕机重启,系统会自动去检查redo log,将修改还未写入磁盘的数据从redo log恢复到MySQL中。

MySQL启动时,无论上次是正常关闭仍是异常关闭,老是会进行恢复操做。会先检查数据页中的LSN,若是这个 LSN 小于 redo log 中的LSN,即write pos位置,说明在redo log上记录着数据页上还没有完成的操做,接着就会从最近的一个check point出发,开始同步数据。

简单理解,好比:redo log的LSN是500,数据页的LSN是300,代表重启前有部分数据未彻底刷入到磁盘中,那么系统则将redo log中LSN序号300到500的记录进行重放刷盘。

undo log(回滚日志)

undo log也是属于MySQL存储引擎InnoDB的事务日志。

undo log属于逻辑日志,如其名主要起到回滚的做用,它是保证事务原子性的关键。记录的是数据修改前的状态,在数据修改的流程中,同时会记录一条与当前操做相反的逻辑日志到undo log中。

咱们举个栗子:假如更新ID=1记录的name字段,name原始数据为小富,现改name为程序员内点事

事务执行update X set name = 程序员内点事 where id =1语句时,先会在undo log中记录一条相反逻辑的update X set name = 小富 where id =1记录,这样当某些缘由致使服务异常事务失败,就能够借助undo log将数据回滚到事务执行前的状态,保证事务的完整性。

那可能有人会问:同一个事物内的一条记录被屡次修改,那是否是每次都要把数据修改前的状态都写入undo log呢?

答案是不会的!

undo log只负责记录事务开始前要修改数据的原始版本,当咱们再次对这行数据进行修改,所产生的修改记录会写入到redo logundo log负责完成回滚,redo log负责完成前滚。

回滚

未提交的事务,即事务未执行commit。但该事务内修改的脏页中,可能有一部分脏块已经刷盘。若是此时数据库实例宕机重启,就须要用回滚来将先前那部分已经刷盘的脏块从磁盘上撤销。

前滚

未彻底提交的事务,即事务已经执行commit,但该事务内修改的脏页中只有一部分数据被刷盘,另一部分还在buffer pool缓存上,若是此时数据库实例宕机重启,就须要用前滚来完成未彻底提交的事务。将先前那部分因为宕机在内存上的将来得及刷盘数据,从redo log中恢复出来并刷入磁盘。

数据库实例恢复时,先作前滚,后作回滚。

若是你仔细看过了上边的 MySQL数据更新流程图 就会发现,undo logredo logbin log三种日志都是在刷脏页以前就已经刷到磁盘了的,相互协做最大限度保证了用户提交的数据不丢失。

bin log(归档日志)

bin log是一种数据库Server层(和什么引擎无关),以二进制形式存储在磁盘中的逻辑日志。bin log记录了数据库全部DDLDML操做(不包含 SELECTSHOW等命令,由于这类操做对数据自己并无修改)。

默认状况下,二进制日志功能是关闭的。能够经过如下命令查看二进制日志是否开启:

mysql> SHOW VARIABLES LIKE 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | OFF   |
+---------------+-------+

bin log也被叫作归档日志,由于它不会像redo log那样循环写擦除以前的记录,而是会一直记录日志。一个bin log日志文件默认最大容量1G(也能够经过max_binlog_size参数修改),单个日志超过最大值,则会新建立一个文件继续写。

mysql> show binary logs;
+-----------------+-----------+
| Log_name        | File_size |
+-----------------+-----------+
| mysq-bin.000001 |      8687 |
| mysq-bin.000002 |      1445 |
| mysq-bin.000003 |      3966 |
| mysq-bin.000004 |       177 |
| mysq-bin.000005 |      6405 |
| mysq-bin.000006 |       177 |
| mysq-bin.000007 |       154 |
| mysq-bin.000008 |       154 |

bin log日志的内容格式其实就是执行SQL命令的反向逻辑,这点和undo log有点相似。通常来讲开启bin log都会给日志文件设置过时时间(expire_logs_days参数,默认永久保存),要否则日志的体量会很是庞大。

mysql> show variables like 'expire_logs_days';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| expire_logs_days | 0     |
+------------------+-------+
1 row in set

mysql> SET GLOBAL expire_logs_days=30;
Query OK, 0 rows affected

bin log主要应用于MySQL主从模式(master-slave)中,主从节点间的数据同步;以及基于时间点的数据还原。

主从同步

经过下图MySQL的主从复制过程,来了解下bin log在主从模式下的应用。

  • 用户在主库master执行DDLDML操做,修改记录顺序写入bin log;

  • 从库slave的I/O线程链接上Master,并请求读取指定位置position的日志内容;

  • Master收到从库slave请求后,将指定位置position以后的日志内容,和主库bin log文件的名称以及在日志中的位置推送给从库;

  • slave的I/O线程接收到数据后,将接收到的日志内容依次写入到relay log文件最末端,并将读取到的主库bin log文件名和位置position记录到master-info文件中,以便在下一次读取用;

  • slave的SQL线程检测到relay log中内容更新后,读取日志并解析成可执行的SQL语句,这样就实现了主从库的数据一致;

基于时间点还原

咱们看到bin log也能够作数据的恢复,而redo log也能够,那它们有什么区别?

  • 层次不一样:redo log 是InnoDB存储引擎实现的,bin log 是MySQL的服务器层实现的,但MySQL数据库中的任何存储引擎对于数据库的更改都会产生bin log。

  • 做用不一样:redo log 用于碰撞恢复(crash recovery),保证MySQL宕机也不会影响持久性;bin log 用于时间点恢复(point-in-time recovery),保证服务器能够基于时间点恢复数据和主从复制。

  • 内容不一样:redo log 是物理日志,内容基于磁盘的页Page;bin log的内容是二进制,能够根据binlog_format参数自行设置。

  • 写入方式不一样:redo log 采用循环写的方式记录;binlog 经过追加的方式记录,当文件大小大于给定值后,后续的日志会记录到新的文件上。

  • 刷盘时机不一样:bin log在事务提交时写入;redo log 在事务开始时即开始写入。

bin log 与 redo log 功能并不冲突而是起到相辅相成的做用,须要两者同时记录,才能保证当数据库发生宕机重启时,数据不会丢失。

relay log(中继日志)

relay log日志文件具备与bin log日志文件相同的格式,从上边MySQL主从复制的流程能够看出,relay log起到一个中转的做用,slave先从主库master读取二进制日志数据,写入从库本地,后续再异步由SQL线程读取解析relay log为对应的SQL命令执行。

slow query log

慢查询日志(slow query log): 用来记录在 MySQL 中执行时间超过指定时间的查询语句,在 SQL 优化过程当中会常用到。经过慢查询日志,咱们能够查找出哪些查询语句的执行效率低,耗时严重。

出于性能方面的考虑,通常只有在排查慢SQL、调试参数时才会开启,默认状况下,慢查询日志功能是关闭的。能够经过如下命令查看是否开启慢查询日志:

mysql> SHOW VARIABLES LIKE 'slow_query%';
+---------------------+--------------------------------------------------------+
| Variable_name       | Value                                                  |
+---------------------+--------------------------------------------------------+
| slow_query_log      | OFF                                                    |
| slow_query_log_file | /usr/local/mysql/data/iZ2zebfzaequ90bdlz820sZ-slow.log |
+---------------------+--------------------------------------------------------+

经过以下命令开启慢查询日志后,我发现 iZ2zebfzaequ90bdlz820sZ-slow.log 日志文件里并无内容啊,可能由于我执行的 SQL 都比较简单没有超过指定时间。

mysql>  SET GLOBAL slow_query_log=ON;
Query OK, 0 rows affected

上边提到超过 指定时间 的查询语句才算是慢查询,那么这个时间阈值又是多少嘞?咱们经过 long_query_time 参数来查看一下,发现默认是 10 秒。

mysql> SHOW VARIABLES LIKE 'long_query_time';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+

这里咱们将 long_query_time 参数改小为 0.001秒再次执行查询SQL,看看慢查询日志里是否有变化。

mysql> SET GLOBAL long_query_time=0.001;
Query OK, 0 rows affected

果真再执行 SQL 的时,执行时间大于 0.001秒,发现慢查询日志开始记录了。

慢查询日志

general query log

通常查询日志(general query log):用来记录用户的全部操做,包括客户端什么时候链接了服务器、客户端发送的全部SQL以及其余事件,好比 MySQL 服务启动和关闭等等。MySQL服务器会按照它接收到语句的前后顺序写入日志文件。

因为通常查询日志记录的内容过于详细,开启后 Log 文件的体量会很是庞大,因此出于对性能的考虑,默认状况下,该日志功能是关闭的,一般会在排查故障需得到详细日志的时候才会临时开启。

咱们能够经过如下命令查看通常查询日志是否开启,命令以下:

mysql> show variables like 'general_log';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| general_log   | OFF   |
+---------------+-------+

下边开启通常查询日志并查看日志存放的位置。

mysql> SET GLOBAL general_log=on;
Query OK, 0 rows affected
mysql> show variables like 'general_log_file';
+------------------+---------------------------------------------------+
| Variable_name    | Value                                             |
+------------------+---------------------------------------------------+
| general_log_file | /usr/local/mysql/data/iZ2zebfzaequ90bdlz820sZ.log |
+------------------+---------------------------------------------------+

执行一条查询 SQL 看看日志内容的变化。

mysql> select * from t_config;
+---------------------+------------+---------------------+---------------------+
| id                  | remark     | create_time         | last_modify_time    |
+---------------------+------------+---------------------+---------------------+
| 1325741604307734530 | 我是广播表 | 2020-11-09 18:06:44 | 2020-11-09 18:06:44 |
+---------------------+------------+---------------------+---------------------+

咱们看到日志内容详细的记录了全部执行的命令、SQL、SQL的解析过程、数据库设置等等。

通常查询日志

error log

错误日志(error log): 应该是 MySQL 中最好理解的一种日志,主要记录 MySQL 服务器每次启动和中止的时间以及诊断和出错信息。

默认状况下,该日志功能是开启的,经过以下命令查找错误日志文件的存放路径。

mysql> SHOW VARIABLES LIKE 'log_error';
+---------------+----------------------------------------------------------------+
| Variable_name | Value                                                          |
+---------------+----------------------------------------------------------------+
| log_error     | /usr/local/mysql/data/LAPTOP-UHQ6V8KP.err |
+---------------+----------------------------------------------------------------+

注意:错误日志中记录的可并不是全是错误信息,像 MySQL 如何启动 InnoDB 的表空间文件、如何初始化本身的存储引擎,初始化 buffer pool 等等,这些也记录在错误日志文件中。

总结

MySQL做为咱们工做中最常接触的中间件,熟练使用只算是入门,若是要在简历写上一笔精通,还须要深刻了解其内部工做原理,而这7种日志也只是深刻学习过程当中的一个起点,学无止境,兄嘚干就完了!

整理了几百本各种技术电子书,有须要的同窗能够,在我同名公众号回复[ 666 ]自取。技术群快满了,想进的同窗能够加我好友,和大佬们一块儿吹吹技术,期待你的加入。

在这里插入图片描述

不管你是刚入行、仍是已经有几年经验的程序员,相信这份面试提纲都会给你很多助力,长按二维码关注 『 程序员内点事 』 ,回复 『 offer 』 自行领取,祝你们 offer 拿到手软

在这里插入图片描述

相关文章
相关标签/搜索