前言
对于Myql来说,其所包含的文件有多种,主要有如下几种:
参数文件:存储了Mysql启动与运行时配置信息相关的属性;如 my.cnf配置文件存储了Mysql 相应的属性。
日志文件:记录了Mysql在运行时产生的日志信息,可用于对操作日志审计或恢复数据等。通常所说的日志文件有:错误日志、二进制日志文件、慢查询日志文件、查询日志文件等。
pid 文件:Mysql 实例进程的 ID 文件。
Mysql 表结构文件:用于存放表结构的定义文件。
存储引擎文件:Mysql的引擎对应的是表,每个引擎都有自己存储数据对应的文件格式。这些引擎文件存储了真正的数据与索引信息。
本文重点介绍Mysql的日志架构,因此下文着重介绍Mysql的日志文件。
笔者整理后,主要有以下几种日志文件:binlog 、redolog、error log、show query log、log。如下图所示:
一、错误日志
存储内容:错误日志文件对MySQL的启动、运行、关闭过程进行了记录。默认为开启。
- 1. mysql 错误日志存储路径(或者直接查看配置文件my.cnf):
mysql> SHOW VARIABLES LIKE 'log_error';
+---------------+------------+
| Variable_name | Value |
+---------------+------------+
| log_error | ./dn02.err |
+---------------+------------+
1 row in set (0.00 sec)
如上可知:错误日志存储路径为 ./dn02.err,其中dn02为笔者主机映射域名,/usr/local/mysql/data为笔者mysql安装路径,故绝对路径为:/usr/local/mysql/data/dn02.err
- 2. 查看错误日志内容
重启Mysql服务器,查看错误日志如下
2019-06-30T06:16:34.170958Z 0 [Warning] [MY-010909] [Server] /usr/local/mysql/bin/mysqld: Forcing close of thread 1715 user: 'root'.
2019-06-30T06:16:35.756124Z 0 [System] [MY-010910] [Server] /usr/local/mysql/bin/mysqld: Shutdown complete (mysqld 8.0.11) MySQL Community Server - GPL.
2019-06-30T06:16:36.363776Z 0 [Warning] [MY-010915] [Server] 'NO_ZERO_DATE', 'NO_ZERO_IN_DATE' and 'ERROR_FOR_DIVISION_BY_ZERO' sql modes should be used with strict mode. They will be merged with strict mode in a future release.
2019-06-30T06:16:36.363923Z 0 [System] [MY-010116] [Server] /usr/local/mysql/bin/mysqld (mysqld 8.0.11) starting as process 117663
2019-06-30T06:16:36.382442Z 1 [Warning] [MY-012197] [InnoDB] InnoDB: Unable to open './gzdc/TBL_PERSON.ibd'
2019-06-30T06:16:36.382542Z 1 [Warning] [MY-012197] [InnoDB] InnoDB: Unable to open './gzdc/TBL_ORG.ibd'
2019-06-30T06:16:36.382566Z 1 [Warning] [MY-012197] [InnoDB] InnoDB: Unable to open './gzdc/EX_GONGSHANG_01V3_KYDJ.ibd'
2019-06-30T06:16:43.610735Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2019-06-30T06:16:43.636304Z 0 [System] [MY-010931] [Server] /usr/local/mysql/bin/mysqld: ready for connections. Version: '8.0.11' socket: '/tmp/mysql.sock' port: 3306 MySQL Community Server - GPL.
由于错误日志记录了Mysql启动相关的信息,因此当mysql出现与启动或配置属性等服务相关的错误时,第一个应该查看的就是这个错误日志。
二、慢查询日志
存储内容:记录了查询过程中执行比较慢的SQL语句。默认为关闭。
前面提到可以通过错误日志得到一些关于数据库优化的信息,而慢查询日志 (slow_log)可帮助DBA定位可能存在问题的SQL语句,从而进行SQL语句层面的优化。
例如,可以在MySQL启动时设一个阈值,将运行时间超过该值的所有SQL语句都记录到慢查询日志文件中。DBA每天或每过一段时间对其进行检查,确认是否有SQL语句需要进行优化。
该阈值可以通过参数long-query_time来设置,默认值为10,代表 10秒。在默认情况下,MySQL数据库并不启动慢查询日志,用户需要手工将这个参数设为ON。因此该功能主要是在对SQL语句进行调优时再打开。
查看慢查询日志默认阈值如下:
mysql> SHOW VARIABLES LIKE 'long_query_time'\G;
*************************** 1. row ***************************
Variable_name: long_query_time
Value: 10.000000
1 row in set (0.00 sec)
慢查询日志启动:
mysql> SET GLOBAL slow_query_log='on';
Query OK, 0 rows affected (0.00 sec)
Mysql 5.1后,如开启了慢查询日志可支持将对应的日志记录于slow_log表中,表结构如下图所示:
mysql> SHOW CREATE TABLE mysql.slow_log\G;
*************************** 1. row ***************************
Table: slow_log
Create Table: CREATE TABLE `slow_log` (
`start_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
`user_host` mediumtext NOT NULL,
`query_time` time(6) NOT NULL,
`lock_time` time(6) NOT NULL,
`rows_sent` int(11) NOT NULL,
`rows_examined` int(11) NOT NULL,
`db` varchar(512) NOT NULL,
`last_insert_id` int(11) NOT NULL,
`insert_id` int(11) NOT NULL,
`server_id` int(10) unsigned NOT NULL,
`sql_text` mediumblob NOT NULL,
`thread_id` bigint(21) unsigned NOT NULL
) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log'
1 row in set (0.25 sec)
但是,慢查询默认的日志还是记录于文件中,如下所示:
mysql> SHOW VARIABLES LIKE 'log_output'\G;
*************************** 1. row ***************************
Variable_name: log_output
Value: FILE
1 row in set (0.00 sec)
修改成存储到表:
mysql> SET GLOBAL log_output='TABLE';
测试:
mysql> select sleep (11)\G;
*************************** 1. row ***************************
sleep (11): 0
1 row in set (11.00 sec)
这条查询语句执行时间超过10秒,按理来说会被记录到slow_log 慢查询日志中。
验证:
mysql> SELECT * FROM mysql.slow_log\G;
*************************** 1. row ***************************
start_time: 2019-06-30 15:19:50.519724
user_host: root[root] @ localhost []
query_time: 00:00:11.000336
lock_time: 00:00:00.000000
rows_sent: 1
rows_examined: 0
db:
last_insert_id: 0
insert_id: 0
server_id: 1
sql_text: select sleep (11)
thread_id: 14
1 row in set (0.00 sec)
三、查询日志
存储内容:记录了所有对数据库的请求,无论执行请求是否得到正确结果。默认为关闭。
也可跟慢查询日志一样将记录信息记录到表中。
- 1. 查询是否开启查询日志功能:
mysql> SHOW VARIABLES LIKE '%general_log%'\G;
*************************** 1. row ***************************
Variable_name: general_log
Value: OFF
*************************** 2. row ***************************
Variable_name: general_log_file
Value: /usr/local/mysql/data/dn02.log
2 rows in set (0.01 sec)
- 2. 开启查询日志
SET GLOBAL general_log='on';
注:因为在前面设置了log存储到表中,因此所有的日志便直接输出到对应的表中了;
开启后没过几分钟便记录下几百条数据,部分样例如下:
*************************** 231. row ***************************
event_time: 2019-06-30 15:42:20.242534
user_host: smartbi[smartbi] @ dn02.bmsoft.com.cn [10.194.186.118]
thread_id: 8
server_id: 1
command_type: Query
argument: SET autocommit=1
*************************** 232. row ***************************
event_time: 2019-06-30 15:42:20.945496
user_host: root[root] @ localhost []
thread_id: 14
server_id: 1
command_type: Query
argument: select * from general_log
232 rows in set (0.01 sec)
测试完,为了减小数据频繁操作,还是乖乖把它关了为好。
mysql> SET GLOBAL general_log='off';
Query OK, 0 rows affected (0.00 sec)
四、二进制binlog日志
存储内容:该日志记录了对Mysql执行更新的所有操作,例如 update/delete等,有一点需要注意的是,该记录原则是执行了更新语句便会进入记录,如里执行update操作,但实际没有发生数据的更新也一样会被记录进来。
此外,二进制日志还包括了执行数据库更改操作的时间等其他额外信息。总的来说,二进制日志主要有以下几种作用。
- 恢复(recovery):某些数据的恢复需要二进制日志,例如,在一个数据库全备文件恢复后,用户可以通过二进制日志进行point-in-time的恢复。
- 复制(replication):其原理与恢复类似,通过复制和执行二进制日志使一台远程的MySQL数据库(一般称为slave或standby) 与一台MySQL数据库 (一般称为master或primary) 进行实时同步。
- 审计 (audit):用户可以通过二进制日志中的信息来进行审计,判断是否有对数据库进行注入的攻击。
在默认情况下,二进制日志并不是在每次写的时候同步到磁盘(用户可以理解成缓冲写)。
因此,当数据库所在操作系统发生宕机时,可能会有最后一部分数据没有写入二进制日志文件中,这会给恢复和复制带来问题。解决这一点可以引入redolog日志。
- 1. 可以通过例子感性地认识该日志
mysql> update test.user_t set name='Hadoop' where id=1;
Query OK, 1 row affected (0.08 sec)
由上述运行结果可知,其实并没有数据发生更新;查看目前该更新操作记录的binlog文件名
mysql> show master status\G;
*************************** 1. row ***************************
File: binlog.000017
Position: 485
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec)
binlog日志记录于 binlog.000017;其中485为文件大小为
-rw-r----- 1 mysql mysql 485 Jun 30 16:51 binlog.000016
注:binlog.index 中记录了这些归档日志对应的索引名(简单理解为文件名);
- 2. 查看具体日志:
发生的修改后的日志文件如下:
mysql> SHOW BINLOG EVENTS IN 'binlog.000017' \G ;
*************************** 1. row ***************************
Log_name: binlog.000017
Pos: 4
Event_type: Format_desc
Server_id: 1
End_log_pos: 124
Info: Server ver: 8.0.11, Binlog ver: 4
*************************** 2. row ***************************
Log_name: binlog.000017
Pos: 124
Event_type: Previous_gtids
Server_id: 1
End_log_pos: 155
Info:
*************************** 3. row ***************************
Log_name: binlog.000017
Pos: 155
Event_type: Anonymous_Gtid
Server_id: 1
End_log_pos: 230
Info: SET @@SESSION.GTID_NEXT= 'ANONYMOUS'
*************************** 4. row ***************************
Log_name: binlog.000017
Pos: 230
Event_type: Query
Server_id: 1
End_log_pos: 310
Info: BEGIN
*************************** 5. row ***************************
Log_name: binlog.000017
Pos: 310
Event_type: Table_map
Server_id: 1
End_log_pos: 375
Info: table_id: 62 (test.user_t)
*************************** 6. row ***************************
Log_name: binlog.000017
Pos: 375
Event_type: Update_rows
Server_id: 1
End_log_pos: 456
Info: table_id: 62 flags: STMT_END_F
*************************** 7. row ***************************
Log_name: binlog.000017
Pos: 456
Event_type: Xid
Server_id: 1
End_log_pos: 487
Info: COMMIT /* xid=8 */
7 rows in set (0.00 sec)
- 3. 因为是二进制文件,没方法通过cat、more/less 等查看文本文件的方式进行查看,因此用如下方式:
[root@dn02 data]# mysqlbinlog --start-position=1 binlog.000017
...
#190630 17:02:44 server id 1 end_log_pos 375 CRC32 0xf88cd5eb Table_map: `test`.`user_t` mapped to number 62
# at 375
#190630 17:02:44 server id 1 end_log_pos 456 CRC32 0x13626f55 Update_rows: table id 62 flags: STMT_END_F
....
[root@dn02 data]#
- 4. 在Mysql 中查看有关binlog相关的配置属性,会发现特别多,如下所示:
mysql> SHOW VARIABLES LIKE '%binlog%';
+--------------------------------------------+----------------------+
| Variable_name | Value |
+--------------------------------------------+----------------------+
| binlog_cache_size | 32768 |
| binlog_checksum | CRC32 |
| binlog_direct_non_transactional_updates | OFF |
| binlog_error_action | ABORT_SERVER |
| binlog_expire_logs_seconds | 2592000 |
| binlog_format | ROW |
| binlog_group_commit_sync_delay | 0 |
| binlog_group_commit_sync_no_delay_count | 0 |
| binlog_gtid_simple_recovery | ON |
| binlog_max_flush_queue_time | 0 |
| binlog_order_commits | ON |
| binlog_row_image | FULL |
| binlog_row_metadata | MINIMAL |
| binlog_row_value_options | |
| binlog_rows_query_log_events | OFF |
| binlog_stmt_cache_size | 32768 |
| binlog_transaction_dependency_history_size | 25000 |
| binlog_transaction_dependency_tracking | COMMIT_ORDER |
| innodb_api_enable_binlog | OFF |
| log_statements_unsafe_for_binlog | ON |
| max_binlog_cache_size | 18446744073709547520 |
| max_binlog_size | 1073741824 |
| max_binlog_stmt_cache_size | 18446744073709547520 |
| sync_binlog | 1 |
+--------------------------------------------+----------------------+
24 rows in set (0.01 sec)
如:binlog_expire_logs_seconds | 2592000 这个设置意思是binlog.xxx日志保存一个月的记录,也就是说可以通过其恢复一个月内任何时间内对数据所做的处理,包含删除修改等更新操作。
五、redo log(InnoDB 引擎日志文件)
存储内容:记录了 Mysql 中实际被修改的数据。其主要有以下特点:
- 1. InnoDB特有的,可用于解决Mysql 的 crash-safe 问题;注:数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
- 2. 在对数据进行操作时,为了减少磁盘IO开销,数据不会立马写入数据存储对应的磁盘中,而是先写入日志中。MySQL里面有一种叫做WAL(Write-Ahead Logging),就是先写日志在写磁盘。就是当有一条记录需要更新的时候,InnoDB 会先写redo log 里面,并更新内存,这个时候更新的操作就算完成了。之后,MySQL会在合适的时候将操作记录 flush 到磁盘上面。当然 flush 的条件可能是系统比较空闲,或者是 redo log 空间不足时。
- 3. redo log 文件的大小是固定的,其通过循环写的方式往redolog中写入数据,当空间不足时则flush。如下图所示:
binlog 与 redolog的区别
- redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。
- redo log是物理日志,记录的是"在某个数据页上做了什么修改" ; binlog是逻辑日志,记录的是这个语句的原始逻辑,比如"给ID-2这一行的c字段加1"。
- redo log是循环写的,空间固定会用完;binlog是可以追加写入的。"追加写"是指binlog写到一定大小后会切换到下一个,并不会覆盖以前的日志。
六、Mysql 执行一条更新操作的整体流程
- 执行的语句如下:
mysql> update T set c=c+1 where ID=2;
在上一篇文章中,我们知道Mysql中执行一条查询语句,其执行流程图如下图:
- 同理,在执行更新操作也基本与查询一样,主要的不同之处是更新的操作会涉及对binlog、redolog日志进行更新。
- 其更新过程如下图所示:图中浅色框表示是在InnoDB内部执行的,深色框表示是在执行器中执行的。
通过上图可看出,对于redolog日志是先写入然后进入prepare阶段,然后再写入binlog,两个都写成功后再进行一起提交,对于redolog从专业术语上来说,这就是redolog的两阶段提交。
两阶段提交主要是为了后期当通过binlog对数据进行恢复时,能确保binlog与redolog对应的数据是一致的。
详细学习redolog与binlog 推荐阅读极客时间《Mysql常见的6个日志问题》一文
注:参考极客时间《日志系统:一条SQL更新语句是如何执行的?》
Mysql技术内幕InnoDB存储引擎