理解Mysql日志
2013-03-15 15:16:09 阿炯

MySQL默认情况下,没有启动任何log,可以通过如下log 选项来启动相关的log;默认情况下,所有的log都会放在data directory 目录下, 可以通过flush log命令,强制关闭server,然后reopen log file。当执行flushlog命令时会触发log flush,或者通过mysqladmin命令也可以完成。

对于binary log,当log 达到max_binlog_size时也会触发flush。可以在db 运行时操作general query 和slow query log,比如启用和禁用log,改变log file 名称,可以选择general query 和slow是存放在log tables 还是log files。

MySQL对general query log 和 slow query log 的控制比较灵活,我们可以指定这2个log输出到log file 或者输入到mysql 数据库的general_log 和 slow_log 2张日志表里。也可以同时使用,即输出到log file 和 log table。如果输出到log table,可能会带来一定的性能问题,log tables 默认使用CSV 引擎。所以最好写入logfile。

目前相关的日志可分类如下:

1、错误日志 Error Log

Error Log 错误日志, 记录MySQL启动、运行、停止过程中出现的错误信息,错误日志记录了mysql server 运行过程中所有较为严重的警告和错误信息,以及mysql每次启动和关闭的详细信息。

开启方法在启动mysql 时候加上--log-error选项。错误日志默认放在数据目录下,以hostname.err命名。但是可以使用命令--log-error[=file_name]修改其存放目录和文件名。有时可将错误日志做备份并重新开始记录,使用flush logs命令备份文件。

2、二进制日志:Binary Log&Binary Log Index
就是常说的binlog,是mysql中最为重要的日志之一。在通过--log-bin[=file_name]打开记录的功能之后,mysql会将所有修改数据库数据的query以二进制的时候记录到日志文件中,其中包括每一条query所执行的时间,所消耗的资源,以及相关事务信息,如果没有指定file_name,会在数据目录下记录为mysql-bin.****。

binlog还有其他一些附加选项参数:
--max_binlog_size设置binlog的最大存储上限,当日志到达这个上限的时候,会重新创建一个文件记录。
--binlog-do-db=db_name参数告诉mysql只对某个数据库记录binlog
--binlog-ignore-db=db_name参数告诉mysql忽略对某个数据库记录binlog

3、通用及查询日志
General Query Log 记录MySQL客户端连接、查询的细节, 调试的时候很有用, 正式运行的时候最好关闭否则会带来大量磁盘IO ,查询日志记录mysql中所有的query,可通过--log[=file_name]来打开该日志,由于记录了所有的query,体积庞大,开启后对性能也有较大的影响;只在跟踪某些特殊的query性能问题时候才会短暂开启该功能,默认的文件名hostname.log。

4、慢查询日志:Slow query log
记录不用索引的Query和执行时间超过long_query_time指定的秒数的Query,通过--log-slow-queries[=file_name]来打开该功能并设置记录位置和文件名,默认文件名:hostname-slow.log,默认目录也是数据目录。

5、InnoDB的在线的REDO日志:InnoDB REDO Log
REDO日志中记录了InnoDB所做的所有物理变更和事务信息,通过REDO日志和UNDO信息,InnoDB保证了在任何情况下的事务安全性。InnoDB的REDO日志同样默认存放在数据目录下,可以通过innodb_log_group_home_dir来更改设置日志的存放位置。通过innodb_log_files_in_group设置日志的数量。

6、更新日志:Update log
mysql5.0以后不支持,和binlog类似,但是不是以二进制形式记录,是简单的文本格式记录。

注意:可以通过修改'/etc/init.d/mysqld'脚本里的相关路径来指定日志文件的个体位置。

查看当前系统(mysql 5.1)中的的以'log'为关键字的选项
mysql> show variables like "%log%";
back_log    50
binlog_cache_size    32768
binlog_direct_non_transactional_updates    OFF
binlog_format    STATEMENT
expire_logs_days    10
general_log    ON
general_log_file    /var/run/mysqld/mysqld.log
innodb_flush_log_at_trx_commit    1
innodb_flush_log_at_trx_commit_session    3
innodb_locks_unsafe_for_binlog    OFF
innodb_log_block_size    512
innodb_log_buffer_size    8388608
innodb_log_file_size    5242880
innodb_log_files_in_group    2
innodb_log_group_home_dir    ./
innodb_mirrored_log_groups    1
innodb_overwrite_relay_log_info    OFF
log    OFF
log_bin    OFF
log_bin_trust_function_creators    OFF
log_bin_trust_routine_creators    OFF
log_error    
log_output    FILE
log_queries_not_using_indexes    OFF
log_slave_updates    OFF
log_slow_admin_statements    OFF
log_slow_filter    
log_slow_queries    OFF
log_slow_rate_limit    1
log_slow_slave_statements    OFF
log_slow_sp_statements    ON
log_slow_timestamp_every    OFF
log_slow_verbosity    microtime
log_warnings    1
max_binlog_cache_size    4294963200
max_binlog_size    104857600
max_relay_log_size    0
relay_log    
relay_log_index    
relay_log_info_file    relay-log.info
relay_log_purge    ON
relay_log_space_limit    0
slow_query_log    OFF
slow_query_log_file    /var/run/mysqld/mysqld-slow.log
slow_query_log_microseconds_timestamp    OFF
sql_log_bin    ON
sql_log_off    OFF
sql_log_update    ON
suppress_log_warning_1592    OFF
sync_binlog    0
use_global_log_slow_control    none
51 rows in set (0.00 sec)

----------------------------
通用、查询、错误日志

log
mysql> set global log=ON;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show warnings;
| Level   | Code | Message                                                                                           |
| Warning | 1287 | '@@log' is deprecated and will be removed in a future release. Please use '@@general_log' instead |
1 row in set (0.00 sec)

用于开启通用日志的选项,将来将废弃,使用'general_log'代替。目前还能发挥作用。

General Query Log

general query log 记录了clients的connect和disconnect,以及从client接收的每个SQL statement 。query log写statements的顺序和执行顺序可能不一致。general query log 和 binary log的不同在于,query log 包含所有的statements,而binary log 不包含statement,只包含select data。

默认情况下,general query log是禁用的,使用—general_log 来启用该功能。general query log 存放在data directory 目录下,默认文件名是host_name.log,如果在DB 运行时修改log 设置,可以通过set general_log 和 general_log_file 2个参数。和error log 一样,可以进行备份操作,然后用flush log 命令重新开始记录。在运行时,也可以通过先disable 然后enabled的方式来rename log。

general_log
用于开启通用日志的选项。

general_log_file
通用日志文件,带有绝对路径。默认为'/var/run/mysqld/mysqld.log'。

back_log
指出在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。如果期望在一个短时间内有很多连接,你需要增加它,换句话说,这值对到来的TCP/IP连接的侦听队列的大小。你的操作系统在这个队列大小上有它自己的限制。当试图设定back_log高于你的操作系统的限制将是无效的。

log_error
Log errors and startup messages to this file. If you omit the file name, MySQL uses host_name.err. If the file name has no extension, the server adds an extension of .err. error log包含mysqld启动和关闭,已经运行的任何criticalerror信息,如果mysqld检测到某个table需要自动checked或者repaired,也会将信息写入log。

在某些系统下,如果mysqld dies error log也会包含相关的stack trace。可以通过—log-error 参数来指定error log 的位置,如果在没有指定error log,那么默认使用host_name.err,默认存放在data directory目录下。如果没有指定error log,则errors 会写入stderr。在MySQL 5.5.5  之后,可以使用如上命令进行error log的一个备份操作。 mysqladmin的flush-logs会重新使用原来的文件名。所以我们只需要对原文件进行移动就可以了。

mysql> set global log_error=ON;
ERROR 1238 (HY000): Variable 'log_error' is a read only variable
需要在'my.cnf文件中设定并重启服务后生效,默认会生成'/var/run/mysqld/mysqld.err'文件。

log_output
用于指定日志文件的存放方式,默认是'FILE'方式,另外还可以使用'TABLE'方式。该参数可以指定的参数有:TABLE,FILE,NONE,如果启用了该参数,缺没有提供参数值,那么默认使用FILE。该参数指定了log的输出位置,下面启用general log 和 slow query log。设置general_log 为1 或 0 来控制启用和禁用general query log。对应slow query log 的参数是:slow_query_log。
日志表结构如下,可能要手动建立。

CREATE TABLE `slow_log` (
   `start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   `user_host` mediumtext NOT NULL,
   `query_time` time NOT NULL,
   `lock_time` time 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` mediumtext NOT NULL,
   `thread_id` bigint(21) unsigned NOT NULL
  ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log'

CREATE TABLE `general_log` (
   `event_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   `user_host` mediumtext NOT NULL,
   `thread_id` bigint(21) unsigned NOT NULL,
   `server_id` int(10) unsigned NOT NULL,
   `command_type` varchar(64) NOT NULL,
   `argument` mediumtext NOT NULL
  ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='General log'

Enable Query logging on the database
SET global general_log = 1;
SET global log_output = 'table';

View the log
select * from mysql.general_log

Disable Query logging on the database
SET global general_log = 0;

In MySQL 5.1 you have the luxury of different output sources, FILE, TABLE or BOTH for the general log and the slow log with –log-output. No option exists for the error log.

Other my.cnf options to be aware of include:
log_warnings | skip_log_warnings
syslog | skip_syslog

注意:开启日志增加系统负载(i/o),当使用数据库表来存放时可能更大。可以修改log table 使用MyISAM 引擎,但不能在线进行修改,需要先disable。
(1) To disable logging so that you can alter (or drop) a log table, you can use the following strategy. The example uses the general query log; the procedure for the slow query log is similar but uses the slow_log table and slow_query_log system variable.
SET @old_log_state = @@global.general_log;
SET GLOBAL general_log = 'OFF';
ALTER TABLE mysql.general_log ENGINE = MyISAM;
SET GLOBAL general_log = @old_log_state;

(2) TRUNCATE TABLE is a validoperation on a log table. It can be used to expire log entries.

(3) RENAME TABLE is a valid operationon a log table. You can atomically rename a log table (to perform log rotation,for example) using the following strategy:
USE mysql;
CREATE TABLE IFNOT EXISTS general_log2 LIKE general_log;
RENAME TABLEgeneral_log TO general_log_backup, general_log2 TO general_log;

(4) As of MySQL 5.5.7, CHECK TABLE is a valid operation on a log table.

(5) LOCK TABLES cannot be used on a log table.

(6) INSERT, DELETE, and UPDATE cannot be used on a log table. These operations are permitted only internally to theserver itself.

(7) FLUSH TABLES WITH READ LOCK and the state of the global read_only system variable have no effect on log tables.The server can always write to the log tables.

(8) Entries written to the log tablesare not written to the binary log and thus are not replicated to slave servers.

(9) To flush the log tables or logfiles, use FLUSH TABLES or FLUSH LOGS, respectively.

(10) Partitioning of log tables is notpermitted.


log_warnings
将警告打印输出到错误log文件,如果你对于MySQL有任何问题时你应该打开警告log并且仔细审查错误日志,查出可能的原因。默认为开启。

sql_log_update
除影响更新日志的记录和不影响常规日志外,此选项类似于SQL_LOG_OFF(包括要求PROCESS 权限)。过时选项,默认为开启。

mysql> set global sql_log_update=0;
ERROR 1228 (HY000): Variable 'sql_log_update' is a SESSION variable and can't be used with SET GLOBAL

This variable is deprecated, and is mapped to sql_log_bin. It was removed in MySQL 5.5.3.

sql_log_off
If set to 1, no logging is done to the general query log for this client. The client must have the SUPER privilege to set this option. The default value is 0.

mysql> set global sql_log_off=1;
ERROR 1228 (HY000): Variable 'sql_log_off' is a SESSION variable and can't be used with SET GLOBAL

expire_logs_days
二进制日志保留的天数,默认值为10,启动时和二进制日志循环时可能删除。

----------------------------
慢查询日志

slow_query_log
用于是否开启慢查询日志,默认为'OFF'。

slow_query_log_file
指定记录慢查询日志文件位置,默认为'/var/run/mysqld/mysqld-slow.log'。

以下是控制关于慢查询一些具体细节性参数,通过'show variables like "%slow%";'还可以得到一个关于慢查询时间的参数。
slow_query_log_microseconds_timestamp    OFF
log_slow_admin_statements    OFF
log_slow_filter    
log_slow_queries    OFF
log_slow_rate_limit    1
log_slow_slave_statements    OFF
log_slow_sp_statements    ON
log_slow_timestamp_every    OFF
log_slow_verbosity    microtime

slow_launch_time
指定了慢查询时间的参数,默认为'2'秒,可以在当前会话中调整。

mysql> set global slow_launch_time=3;
Query OK, 0 rows affected (0.00 sec)

Slow Query Log

slow query log 包含所有执行时间超过long_query_time 并且需要至少min_examined_row_limitrows的SQL statements。 这个时间由initial table lock来精确的统计,而不是execution time。

mysqld 等statement 执行完毕,并且所有的lock 都释放之后,才将记录写入slow query log。默认long_query_time 是10秒,最小值是0,当写入logfile时,支持到微妙,当写入log table时,则忽略微妙,只保留整数值。默认情况下,slow query log 是禁用的,使用slow_query_log 来控制slowquery log。

在没有指定slow query log file 名称的情况下, 默认文件名是:host_name-slow.log.默认的存放在data directory目录下。在runtime 状态也可以通过slow_query_log和 slow_query_log_file 来修改。 通过slow query log 可以来对相关的SQL 进行优化,但是直接查看log 比较困难,可以使用mysqldumpslow 命令对log 进行dump 之后在查看。slow query log 应该受保护,因为里面可能包含密码。

---------------------------
二进制日志

log_bin    
If set to 0, no logging is done to the binary log for the client. The client must have the SUPER privilege to set this option. The default value is 1.
指定是否开启'binlog'日志,默认为'OFF'。
mysql> set global log_bin=ON;
ERROR 1238 (HY000): Variable 'log_bin' is a read only variable
应该通过修改'my.cnf'文件来开启,在其日志目录里会生成'mysql-bin.000001、mysql-bin.index'等文件。

log_bin_trust_function_creators    OFF
log_bin_trust_routine_creators    OFF

binlog_cache_size    32768
binlog_direct_non_transactional_updates    OFF
binlog_format
用于设定biglog日志文件的格式,目前有三种可以供选择,默认为'STATEMENT',3种不同的格式为:Mixed,Statement,Row
1).Row
日志中会记录成每一行数据被修改的形式,然后在 slave 端再对相同的数据进行修改。

2).Statement
每一条会修改数据的 SQL 都会记录到 master 的 bin-log 中。slave 在复制的时候 SQL 进程会解析成和原来 master 端执行过的相同的 SQL 再次执行。

3).Mixed
从官方文档中看到,之前的 MySQL 一直都只有基于 statement 的复制模式,直到 5.1.5 版本的 MySQL 才开始支持 row 复制,实际上就是前两种模式的结合。
max_binlog_cache_size    4294963200
max_binlog_size    104857600

sync_binlog
This makes MySQL synchronize the binary log’s contents to disk each time it commits a transaction

请注意如果在autocommit模式,每执行一个语句向二进制日志写入一次,否则每个事务写入一次,默认值是0,不与硬盘同步。值为1是最安全的选择,因为崩溃时,你最多丢掉二进制日志中的一个语句/事务;但是,这是最慢的选择(除非硬盘有电池备份缓存,从而使同步工作较快)。

默认情况下,并不是每次写入时都将binlog与硬盘同步。因此如果操作系统或机器(不仅仅是MySQL服务器)崩溃,有可能binlog中最后的语句丢失了。要想防止这种情况,你可以使用sync_binlog全局变量(1是最安全的值,但也是最慢的),使binlog在每N次binlog写入后与硬盘同步。即使sync_binlog设置为1,出现崩溃时,也有可能表内容和binlog内容之间存在不一致性。如果使用InnoDB表,MySQL服务器处理COMMIT语句,它将整个事务写入binlog并将事务提交到InnoDB中。如果在两次操作之间出现崩溃,重启时,事务被InnoDB回滚,但仍然存在binlog中。可以用--innodb-safe-binlog选项来增加InnoDB表内容和binlog之间的一致性。(注释:在MySQL 5.1中不需要--innodb-safe-binlog;由于引入了XA事务支持,该选项作废了),该选项可以提供更大程度的安全,使每个事务的 binlog(sync_binlog =1)和(默认情况为真)InnoDB日志与硬盘同步,该选项的效果是崩溃后重启时,在滚回事务后,MySQL服务器从binlog剪切回滚的 InnoDB事务。这样可以确保binlog反馈InnoDB表的确切数据等,并使从服务器保持与主服务器保持同步(不接收回滚的语句)。

relay_log
指定从服的中转日志,一般其名称为'mysql-relay-bin'。主服务器的binlog(二进制日志)传到备机时被写到relaylog里,然后从服务器的slave sql线程从relaylog里读取然后应用到本地(relay-log日志记录的是从服务器I/O线程将主服务器的二进制日志读取过来记录到从服务器本地文件,然后SQL线程会读取relay-log日志的内容并应用到从服务器)。每从Master fetch一个events后,判断当前文件是否超过 max_relay_log_size 如果超过则自动生成一个新的relay-log-file。

relay_log_index

relay_log_info_file
记录了中转的相关信息,名称为'relay-log.info',与'master.info'相似。

relay_log_purge    ON
在SQL Thread每执行完一个events时判断,如果该relay-log 已经不再需要则自动删除它。

relay_log_space_limit    0

max_relay_log_size    0

log_slave_updates    OFF
是否记录从从服上所做的修改操作记录。

注意:expire-logs-days和relaylog的purge没有关系,这个参数只管理主服的binlog文件数据存放时间。

binary log 包含数据库的改变信息,如创建表,修改数据等。启用binary log会对性能带来一定影响。binary log 不记录没有修改data 的操作,如select 或show。 这些信息可以通过general query log来记录。当生成新的log时,mysqld 通过数字来增加binary log的文件名,每次切换都生成一个新的名称,可以手工用flush 来切换或者自动切换。自动切换受max_binlog_size 参数控制,但是binary log file 可能会大于max_binllog_size的设置,因为如果遇到一个大的事务,这个事务会全部写入binary log,不会切分到几个log 文件里。

为了记录哪些binary log已经使用,mysqld 可以创建一个binarylog index file,其包含所有已经使用binary log 的文件名。 默认情况下,其文件名和binary log file一致,只是扩展名是.index. 可以使用 –log-bin-index 参数来修改,但是在mysqld 运行不能修改这个参数。可以通过mysqlbinlog 工具来显示binary log的内容。

当statement 执行完毕,并且所有的锁都释放之后,或者有commit操作,那么对应的记录都会写入binary log。对于uncommit transaction,所有的change transactional 都会写入binarylog的cache。 从这一点上来看,即使事务没有commit,信息也会提前写入binary log。 对于non-transactioanl 的table上的修改不能进行rollback。

当处理transaction 的thread 启动之后,就会分配binlog_cache_size 的buffer。 如果statement 的值超过这个cache,那么thread会创建一个temporary file来保存这个事务,当thread 完成之后,会delete这个temporary file。

可以通过binlog_cache_use 变量来查看transaction 使用buffer的情况,包括temporary file。binlog_cache_disk_use变量显示实际使用temporary file的大小。 max_binlog_cache_size 默认大小是4G,也是最大值,如果transaction 超过这个大小,那么将失败,并且不能roll back。 这个cache的最小值是4096。如果使用binary log 和row based logging,对于concurrentinsert 会转换成 normal insert。从而确保我们在re-create 数据数据时的精确。 如果使用statement-basedlogging,那么original statement 会被写入log。

默认情况下,binary log 不是实时的将每个write写入disk。 如果OS或服务器crash, 那么可能最后一次写入binary log的statements 就可能丢失。为了避免这种情况,可以设置binary log 在N次writes binary log之后就同步到disk上去。这个参数是sync_binlog,该值设为1是最安全的,但是也是最慢的。即使设置为1,也是有可能导致table content 和 binary log 内容的不一致。

如果在2个操作进行时,server crash了,这时候transaction 会在InnoDB重启时回滚,但是记录还是会存在binary log里。 这在同步时就会有问题。为了解决这个问题,可以设置—innodb_support_xa 为1. 这样就可以保证binarylog和InnoDB data file 的同步。

为了更大程度的实现同步功能。MySQL server 配置在每个事务后将binary log 和 InnoDBlog 写入disk。InnoDB log 默认是同步的,对于binary log,设置sync_binlog为1即可。设置这个参数之后, 在每次crash之后,事务会回滚,同时MySQL 也会从binary log里清除已经rollback的InnoDB 事务。 这样就就保证了tablelog 和 Binary log 的一致,在Replication时就没有了问题。

如果MySQL 检测到crash recover 的binarylog 信息不足,缺少最后一次成功的commited InnoDB 事务,那么会输出错误信息,对于这种情况,replication需要重新采集快照。这种情况在设置sync_binlog 为1时不会发生。在MySQL 5.1.20 之后,如下session 值会被写入binarylog:
(1)、sql_mode
(2)、foreign_key_checks
(3)、unique_checks
(4)、character_set_client
(5)、collation_connection
(6)、collation_database
(7)、collation_server
(8)、sql_auto_is_null

binary log 有几种不同的log format, 具体的格式和MySQL 版本有关。在混合模式下,默认使用statement-based logging,可根据实际情况也会自动转成row-based 模式。在MySQL 5.5中,默认的binary logging format是statement based。在NDBCLUSTER 引擎下,默认使用的binary log 格式是MIXED。可以通过命令修改globally 的信息或者修改某个session 的值。
mysql> SET GLOBAL binlog_format = 'ROW';
mysql> SET SESSION binlog_format ='MIXED';

修改binlog_format 需要SUPER 权限,在Replication时,修改Master的logging format,slave 也会自动switch。

---------------------------
InnoDB的在线的 REDO日志

innodb_flush_log_at_trx_commit    1
innodb_flush_log_at_trx_commit_session    3
innodb_locks_unsafe_for_binlog    OFF
innodb_log_block_size    512
innodb_log_buffer_size    8388608
innodb_log_file_size    5242880
innodb_log_files_in_group    2
innodb_log_group_home_dir    ./
innodb_mirrored_log_groups    1
innodb_overwrite_relay_log_info    OFF


关于重做日志 redo log

在事务的ACID特性中,原子性(A)、一致性(C)、持久性(D)由undo log和redo log实现,隔离性(I)由锁+MVCC实现。

undo log:事务还没有commit,中途执行异常,可以使用undo log把数据恢复到事务执行前的状态,确保事务的原子性。
redo log:事务commit成功,由于更新磁盘数据需要一段时间,此时若发生异常,就要使用redo log重新执行这一事务的SQL,确保事务的持久性(只要事务commit成功,不管发生什么异常事件,只要下一次MySQL服务正常进行,那上一次commit的数据一定要恢复回来)。

一、redo log概念

redo log:被称为物理日志,用于记录事务操作的变化,确保事务的持久性。事务begin就开始记录,无论事务是否commit都会记录,若异常发生,下一次mysqld再启动时,会使用redo log将数据重新写入磁盘,确保事务的持久性。记录的就是最终修改后的按页面存储的数据页,直接存数据最终的状态

undo log:被称为逻辑日志,存储的是具体的相应的SQL语句。如果现在执行的是insert,回滚的时候就执行delete;如果现在执行的update,就把原来的旧值再update回来。

redo log默认放在mysql的数据目录下(通常有两个文件:ib_logfile0、ib_logfile1),是在事务begin时就开始记录(并不是事务commit时才记录,因为整个事务做的操作可能很多,如果在commit的时候才写redo log,此时一旦发生异常,redo log还没写,这就太晚了,无法确保事务的持久性),不管事务是否提交都会记录下来,在异常发生时(如数据持久化过程中掉电),InnoDB会使用redo log恢复到掉电前的时刻,保证数据的完整性。

innodb_log_buffer_size默认是16M,就是redo log缓冲区的大小,它随着事务开始,就开始写redo log,如果事务比较大,为了避免事务执行过程中花费过多磁盘IO,可以设置比较大的redo log缓存,节省磁盘IO。往磁盘上刷是有刷新的时机,达到时机就花费磁盘IO,如果buffer比较大,会更慢的达到刷新的时机,效率更高。

InnoDB修改操作数据,不是直接修改磁盘上的数据,实际只是修改Buffer Pool中的数据。InnoDB总是先把Buffer Pool中的数据改变记录到redo log中,用来进行崩溃后的数据恢复。优先记录redo log,然后会有一个独立的线程找时机慢慢的将Buffer Pool中的脏数据刷新到磁盘上(异步)。

innodb_log_group_home_dir指定的目录下的两个文件:ib_logfile0,ib_logfile1,该文件被称作重做日志。

buffer pool缓存池: 可存放索引缓存、数据缓存等,可加速读写,直接操作数据页,写redo log修改就算完成,有专门的线程去做把buffer pool中的dirty page写入磁盘。

buffer pool大致结构如图所示:


事务读取,修改都是优先操作缓存池中的数据。在实际项目中,mysqld会单独的跑在一个机器上,可以分配大量的内存专门作为InnoDB的buffer pool,加快CRUD。其在5.7版本中的默认大小为128M。

二、缓存、磁盘结构


当事务commit的时候,在关系图上的操作就是把InnoDB Log Buffer的内容写入磁盘上的redo log,写成功的话会在磁盘上的redo log会记录状态为commit,如果没有写成功或者写完,则记录状态为prepare。

log在写入磁盘的过程中也有可能发生异常,断电等问题,导致在写redo log的时候没有写完(这相当于事务没有commit成功),此时MySQL下次再恢复的时候就没有必要考虑这个事务的完整性,因为状态并不是commit,都写入磁盘上才表示redo log写成功,状态才变成commit,状态变成commit后需要维护事务的ACID特性。

是不是commit的时候,buffer poll里面的脏数据(数据有被修改)同步被写入磁盘?

并不需要等commit的时候才开始。事务可能修改的数据量比较大,而缓存容量有限,对于buffer poll缓存的数据,会有专门的线程在合适的时间,往磁盘上去刷新,如果出现掉电,下一次MySQL启动后,会根据redo log里面记录的数据,对数据进行恢复。所以当事务commit后,最重要的是redo log要写成功。

undo log本身也是记录在redo log中

undo log支持事务回滚,也不是一瞬间就能完整,最终要修改的也是磁盘上的数据,回滚过程也会出现异常,下次服务重启时,需要使用undo log重新回滚,所以undo log要记录在redo log里面。事务commit成功或者rollback成功,对于底层,都是成功的把操作写到磁盘上的redo log里面。

什么是真正的事务commit成功

不是把数据全部刷到磁盘,而是把记录事务完整操作的redo log从log buffer写入磁盘,再把被修改数据的状态置为commit才算是实现了事务commit成功。此时虽然数据还在buffer poll,但只要我们的redo log保存完整,数据就可以恢复,会有专门的线程去负责把buffer poll里的数据写入磁盘。

什么叫事务commit成功

事务执行commit命令后,mysqld将记录了数据修改的Log Buffer写入磁盘上的redo log,然后将这部分数据的状态修改为commit,写完redo log后才算是事务提交成功,MySQL Server需要保持commit状态的数据的持久性。如果写redo log失败,数据的状态还是prepare,尽管事务执行了commit命令,这依然不算commit成功。

事务进行操作的时候,永远是先写log buffer,然后才是写buffer pool;事务成功commit,就是要保证redo log完整记录到磁盘上。

至于表数据的更改,buffer pool的脏数据页是不是刷新到磁盘上,都不用太担心,只要redo log完整的写到磁盘上,就可以随时通过redo log(重做日志)来恢复事务成功commit的数据状态(数据库最重要的是日志,而不是数据)。

---------------------------
Server Log Maintenance


MySQL 可以创建一些不同log 文件来帮助我们查看相关的信息,但是我们必须定时的清理这些log 信息,以防止他们占用太多的磁盘空间。

当启用log 后,需要经常备份和移除旧的log file,并且让MySQL 从头开始记录log。仅当binary log 已经被所有的slaves 处理以后才可以删除binary logs。对于binary log,可以设置expire_logs_days 参数来设置过期时间。 可以使用purgebinary logs语句来清除binary logs。可以通过flush log来强制MySQL 使用newlog files。

日志直接性能损耗数据库系统中最为昂贵的IO资源,在默认情况下,系统仅打开错误日志,关闭了其他所有日志,以达到尽可能减少IO 损耗提高系统性能的目的。但在实际应用场景中,都至少需要打开二进制日志,因为这是MySQL很多存储引擎进行增量备份的基础,也是MySQL 实现复制的基本条件。有时候为了进一步的性能优化,定位执行较慢的SQL语句,很多系统也会打开慢查询日志来记录执行时间超过特定数值的SQL语句。

一般情况下,在生产系统中很少有系统会打开查询日志。因为查询日志打开之后会将MySQL 中执行的每一条Query 都记录到日志中,会该系统带来比较大的IO 负担,而带来的实际效益却并不是非常大。一般只有在开发测试环境中,为了定位某些功能具体使用了哪些SQL语句的时候,才会在短时间段内打开该日志来做相应的分析。所以,在MySQL系统中,会对性能产生影响的MySQL日志(不包括各存储引擎自己的日志)主要是Binlog 。