MySQL不会丢失数据的秘密,就藏在它的 7种日志里
共 11403字,需浏览 23分钟
·
2021-03-27 04:05
进入正题前先简单看看MySQL的逻辑架构,相信我用的着。
MySQL的逻辑架构大致可以分为三层:
第一层:处理客户端连接、授权认证,安全校验等。 第二层:服务器 server
层,负责对SQL解释、分析、优化、执行操作引擎等。第三层:存储引擎,负责MySQL中数据的存储和提取。
我们要知道MySQL的服务器层是不管理事务的,事务是由存储引擎实现的,而MySQL中支持事务的存储引擎又属 InnoDB
使用的最为广泛,所以后续文中提到的存储引擎都以InnoDB
为主。
redo log
、bin log
、undo log
三种日志间的大致关系,好了闲话少说直奔主题。redo log(重做日志)
redo log
属于MySQL存储引擎InnoDB
的事务日志。Buffer Pool
。这个缓存中包含了磁盘中部分数据页(page
)的映射,以此来缓解数据库的磁盘压力。Buffer Pool
中修改完数据后会按照设定的更新策略,定期刷到磁盘中,这个过程称为刷脏页。MySQL宕机
Buffer Pool
中修改的数据还没有及时的刷到磁盘中,就会导致数据丢失,无法保证事务的持久性。redo log
,redo Log如其名侧重于重做!它记录的是数据库中每个页的修改,而不是某一行或某几行修改成怎样,可以用来恢复提交后的物理数据页,且只能恢复到最后一次提交的位置。redo log
用到了WAL
(Write-Ahead Logging)技术,这个技术的核心就在于修改记录前,一定要先写日志,并保证日志先落盘,才能算事务提交完成。Buffer Pool
中的数据,当提交事务时,调用fsync
把redo log刷入磁盘。至于缓存中更新的数据文件何时刷入磁盘,则由后台线程异步处理。注意:此时redo log的事务状态是 prepare
,还未真正提交成功,要等bin log
日志写入磁盘完成才会变更为commit
,事务才算真正提交完成。
大小固定
Buffer Pool
中数据页已经刷磁盘后,那这些记录就失效了,新日志会将这些失效的记录进行覆盖擦除。write pos
表示redo log当前记录的日志序列号LSN
(log sequence number),写入还未刷盘,循环往后递增;check point
表示redo log中的修改记录已刷入磁盘后的LSN,循环往后递增,这个LSN之前的数据已经全落盘。write pos
到check point
之间的部分是redo log空余的部分(绿色),用来记录新的日志;check point
到write pos
之间是redo log已经记录的数据页修改数据,此时数据页还未刷回磁盘的部分。当write pos
追上check point
时,会先推动check point
向前移动,空出位置(刷盘)再记录新的日志。注意:redo log日志满了,在擦除之前,需要确保这些要被擦除记录对应在内存中的数据页都已经刷到磁盘中了。擦除旧记录腾出新空间这段期间,是不能再接收新的更新请求的,此刻MySQL的性能会下降。所以在并发量大的情况下,合理调整redo log的文件大小非常重要。
crash-safe
Innodb
引擎具有了crash-safe
的能力,即MySQL宕机重启,系统会自动去检查redo log,将修改还未写入磁盘的数据从redo log恢复到MySQL中。LSN
,如果这个 LSN 小于 redo log 中的LSN,即write pos
位置,说明在redo log
上记录着数据页上尚未完成的操作,接着就会从最近的一个check point
出发,开始同步数据。LSN
是500,数据页的LSN
是300,表明重启前有部分数据未完全刷入到磁盘中,那么系统则将redo log中LSN
序号300到500的记录进行重放刷盘。undo log(回滚日志)
undo log
也是属于MySQL存储引擎InnoDB的事务日志。undo log
属于逻辑日志,如其名主要起到回滚的作用,它是保证事务原子性的关键。记录的是数据修改前的状态,在数据修改的流程中,同时会记录一条与当前操作相反的逻辑日志到undo log
中。update X set name = 程序员内点事 where id =1
语句时,先会在undo log
中记录一条相反逻辑的update X set name = 小富 where id =1
记录,这样当某些原因导致服务异常事务失败,就可以借助undo log
将数据回滚到事务执行前的状态,保证事务的完整性。undo log
呢?undo log
只负责记录事务开始前要修改数据的原始版本,当我们再次对这行数据进行修改,所产生的修改记录会写入到redo log
,undo log
负责完成回滚,redo log
负责完成前滚。回滚
commit
。但该事务内修改的脏页中,可能有一部分脏块已经刷盘。如果此时数据库实例宕机重启,就需要用回滚来将先前那部分已经刷盘的脏块从磁盘上撤销。前滚
commit
,但该事务内修改的脏页中只有一部分数据被刷盘,另外一部分还在buffer pool
缓存上,如果此时数据库实例宕机重启,就需要用前滚来完成未完全提交的事务。将先前那部分由于宕机在内存上的未来得及刷盘数据,从redo log
中恢复出来并刷入磁盘。数据库实例恢复时,先做前滚,后做回滚。
MySQL数据更新流程图
就会发现,undo log
、redo log
、bin log
三种日志都是在刷脏页之前就已经刷到磁盘了的,相互协作最大限度保证了用户提交的数据不丢失。bin log(归档日志)
bin log
是一种数据库Server层(和什么引擎无关),以二进制形式存储在磁盘中的逻辑日志。bin log
记录了数据库所有DDL
和DML
操作(不包含 SELECT
和 SHOW
等命令,因为这类操作对数据本身并没有修改)。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
)中,主从节点间的数据同步;以及基于时间点的数据还原。主从同步
bin log
在主从模式下的应用。用户在主库 master
执行DDL
和DML
操作,修改记录顺序写入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 在事务开始时即开始写入。
relay log(中继日志)
relay log
日志文件具有与bin log
日志文件相同的格式,从上边MySQL主从复制的流程可以看出,relay log
起到一个中转的作用,slave
先从主库master
读取二进制日志数据,写入从库本地,后续再异步由SQL线程
读取解析relay log
为对应的SQL命令执行。slow query log
slow query log
): 用来记录在 MySQL 中执行时间超过指定时间的查询语句,在 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
general query log
general query log
):用来记录用户的所有操作,包括客户端何时连接了服务器、客户端发送的所有SQL
以及其他事件,比如 MySQL
服务启动和关闭等等。MySQL
服务器会按照它接收到语句的先后顺序写入日志文件。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 |
+------------------+---------------------------------------------------+
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 |
+---------------------+------------+---------------------+---------------------+
error log
error log
): 应该是 MySQL 中最好理解的一种日志,主要记录 MySQL 服务器每次启动和停止的时间以及诊断和出错信息。mysql> SHOW VARIABLES LIKE 'log_error';
+---------------+----------------------------------------------------------------+
| Variable_name | Value |
+---------------+----------------------------------------------------------------+
| log_error | /usr/local/mysql/data/LAPTOP-UHQ6V8KP.err |
+---------------+----------------------------------------------------------------+
InnoDB
的表空间文件、如何初始化自己的存储引擎,初始化 buffer pool
等等,这些也记录在错误日志文件中。总结
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️