MySQL 二进制日志

一、概述

二进制日志记录数据库更改和可能更改的事件,以及每个语句的执行时长。

  • 不记录的操作:类似 showselect 这类不更改数据的操作。
  • 密码处理:二进制中的密码会被重写。
  • 新文件创建场景:
    • 服务启动或重启
    • 服务端刷新日志
    • 当前日志文件达到 max_binlog_size 大小
  • 事务记录特点:仅记录或读取完整事务;写入大事务时,日志可能大于 max_binlog_size,因同一事务不会拆分到多个文件。

二、配置相关

  • 加密支持:8.0.14 版本开始,可对二进制日志文件和中继日志文件进行加密。
  • 同步保障:InnoDB 对 XA 事务中两阶段提交的支持,确保二进制日志和 InnoDB 数据文件同步。

三、用途

1. 主从复制

主节点记录二进制日志,从节点复制主节点的二进制日志并进行回放。

2. 数据恢复

  • 事务性表:二进制日志在事务完成后、提交之前(锁未释放时)记录。
  • 非事务性表:更新后立即记录二进制日志。

四、系统变量与状态变量

1. 系统变量

(1)二进制日志属性

  • log_bin:控制是否开启二进制日志及日志文件名;会话级变量为 sql_log_bin
  • log_bin_basename:二进制日志的目录和前缀,只读变量。
  • log_bin_index:二进制日志的索引文件,包含日志名称,用于跟踪日志,扩展名为 .index,与二进制日志在同一目录。
  • log_slave_updates:默认从节点不将从主节点接收的日志写入自身二进制日志;启用后可记录,适用于从节点同时作为其他主节点的场景。
  • binlog_format:二进制日志的格式。
  • max_binlog_size:二进制日志的大小。

(2)缓存

  • binlog_cache_size:二进制日志缓冲区大小;处理事务的线程启动时为语句分配缓冲区,若语句大于缓冲区,会创建临时文件存储事务,线程结束后删除。
  • max_binlog_cache_size:限制缓存事务的总大小;若事务大于此值,会失败并回滚;默认 4G,范围 4096-4G。

(3)同步机制

  • sync_binlog:二进制日志的同步机制(双 1 参数之一);值为 1(默认)时,每次写入都同步到磁盘,最安全但最慢。

(4)过时变量

  • --innodb_support_xa:已过时;默认值 1 时,确保二进制日志和 InnoDB 数据文件同步。

2. 状态变量

  • binlog_cache_use:显示使用缓冲区和临时文件存储语句的事务数。
  • binlog_cache_disk_use:显示实际使用临时文件的事务数。

五、事件校验

  • 默认验证:服务器记录事件长度和事件本身,通过事件长度验证是否正确写入。
  • 校验和验证:可设置校验和,相关系统变量包括 binlog_checksummaster_verify_checksumslave_sql_verify_checksum

六、评估方式

  • --binlog-do-db--replicate-do-db 评估方式相同。
  • --binlog-ignore-db--replicate-ignore-db 评估方式相同。

七、写入二进制日志的变量

  • 约束检查

foreign_key_checksunique_checks

  • 字符集和排序规则

character_set_clientcollation_connectioncollation_databasecollation_server

  • 其他

sql_modeNO_DIR_IN_CREATE 模式不复制)、sql_auto_is_null

八、对二进制日志的操作

1. 查看二进制日志/中继日志内容

shell> mysqlbinlog log_file | mysql -h server_name
shell> mysqlbinlog -d zabbix --base64-output=decode-rows -vvv log_file

2. 清理二进制日志

(1)自动清理

  • 系统变量 expire_logs_days:设置二进制日志的过期时间,默认值为 0。

    mysql> show variables like '%expire_log%';
    +------------------+-------+
    | Variable_name    | Value |
    +------------------+-------+
    | expire_logs_days | 0     |
    +------------------+-------+
    
  • 设置依据:建议保留 2 个全备周期的天数 + 1。

(2)手动清理

  • 清理所有二进制日志:

    mysql> reset master;
    
  • 清理部分二进制日志:

    mysql> PURGE BINARY LOGS TO 'mysql-bin.000004';
    mysql> PURGE BINARY LOGS BEFORE '2019-04-02 22:46:26';
    

    Note:purge binary logs 语句会更新二进制日志的索引文件。

3. 日志轮换

(1)生成新日志

立即将脏数据刷新到磁盘,并启用新的二进制日志:

mysql> flush logs;  -- 刷新所有日志
mysql> flush binary logs;  -- 只刷新二进制日志

(2)重置日志编号

使日志编号从 1 开始重新计数:

mysql> reset master;

4. 截取二进制日志

(1)未开启 GTID

  • 记录单元:事件(event),是二进制日志的最小单元。

    • DDL 语句:每个语句为一个事件。
    • DML 语句:一个事务包含多个事件(如 begin、语句、commit)。
  • 事件的开始和结束:通过 pos(开始位置)和 end_log_pos(结束位置)标识,方便截取。

  • 截取方法:

    • 查看日志列表:

      mysql> show binary logs;
      +------------------+-----------+
      | Log_name         | File_size |
      +------------------+-----------+
      | mysql-bin.000001 |       177 |
      | mysql-bin.000002 |       201 |
      | mysql-bin.000003 |       154 |
      +------------------+-----------+
      
    • 查看正在使用的二进制日志:

      mysql> show master status;
      +------------------+----------+--------------+------------------+-------------------+
      | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
      +------------------+----------+--------------+------------------+-------------------+
      | mysql-bin.000003 |      414 |              |                  |                   |
      +------------------+----------+--------------+------------------+-------------------+
      
    • 查看事件列表:

      mysql> show binlog events in 'mysql-bin.000003';
      +------------------+-----+----------------+-----------+-------------+-------------------------------------------------------+
      | Log_name         | Pos | Event_type     | Server_id | End_log_pos | Info                                                  |
      +------------------+-----+----------------+-----------+-------------+-------------------------------------------------------+
      | mysql-bin.000003 |   4 | Format_desc    |         6 |         123 | Server ver: 5.7.31-log, Binlog ver: 4                 |
      | mysql-bin.000003 | 123 | Previous_gtids |         6 |         154 |                                                       |
      | mysql-bin.000003 | 154 | Anonymous_Gtid |         6 |         219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'                  |
      | mysql-bin.000003 | 219 | Query          |         6 |         291 | BEGIN                                                 |
      | mysql-bin.000003 | 291 | Table_map      |         6 |         339 | table_id: 109 (test.t1)                               |
      | mysql-bin.000003 | 339 | Write_rows     |         6 |         383 | table_id: 109 flags: STMT_END_F                       |
      | mysql-bin.000003 | 383 | Xid            |         6 |         414 | COMMIT /* xid=19 */                                   |
      | mysql-bin.000003 | 414 | Anonymous_Gtid |         6 |         479 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'                  |
      | mysql-bin.000003 | 479 | Query          |         6 |         551 | BEGIN                                                 |
      | mysql-bin.000003 | 551 | Table_map      |         6 |         599 | table_id: 109 (test.t1)                               |
      | mysql-bin.000003 | 599 | Write_rows     |         6 |         644 | table_id: 109 flags: STMT_END_F                       |
      | mysql-bin.000003 | 644 | Xid            |         6 |         675 | COMMIT /* xid=24 */                                   |
      | mysql-bin.000003 | 675 | Anonymous_Gtid |         6 |         740 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'                  |
      | mysql-bin.000003 | 740 | Query          |         6 |         855 | use `test`; create table t2(id int, name varchar(20)) |
      +------------------+-----+----------------+-----------+-------------+-------------------------------------------------------+
      
    • 截取事件(如截取事务,需包含 begincommit):

      mysqlbinlog --start-position=740 --stop-position=855 mysql-bin.000003 /tmp/binlog.sql
      

(2)已开启 GTID

  • 记录单元:事务。

  • 截取方法:

    shell> mysqlbinlog --skip-gtids --include-gtids=2dac652a-5705-11eb-9e3a-000c29a8b526:3 /mdata/binlog/mysql-bin.000004 /tmp/gtid.sql
    
    • --skip-gtids:导出时忽略 GTID,恢复时系统自动生成新 GTID。
    • --include-gtids:指定要截取的事务。
    • --exclude-gtids:指定要排除的事务。
    • --sync-binlog=1:每次事务提交立即刷写 binlog 到磁盘。

九、二进制日志的格式

1. 系统变量

(1)binlog_format

  • 作用:定义二进制日志的记录格式。
  • 有效值:
    • STATEMENT(基于语句模式):主从复制时可能存在问题,如 insert into t1 values(now()); 执行时间可能不同。
    • ROW(默认,基于行模式):复制结构中表必须有主键;并发插入会转换为普通插入(如 CREATE ... SELECT),确保备份恢复时表的精确性。
    • MIXED(混合模式):默认使用基于语句模式,某些情况自动切换为基于行模式。

(2)binlog_row_event_max_size

  • 作用:基于行模式下,二进制日志的最大大小。
  • 说明:行以块大小存储,块大小不超过该值(单位 bytes),必须是 256 的倍数,默认 8192。

2. 设置二进制日志格式

mysql> SET GLOBAL binlog_format = 'STATEMENT';
mysql> SET SESSION binlog_format = 'STATEMENT';

Note:二进制日志的记录格式还可通过存储引擎设置或限制,有助于消除不同存储引擎间主从复制的问题。

(1)设置会话级变量的原因

  • 多小更改的会话:适合基于行模式。
  • 一次性多行更新:基于语句模式效率更高。
  • 长时少量更改语句:基于行模式更有利。

(2)无法动态修改的情况

  • 在函数和触发器中。
  • 启用了 NDB 存储引擎。
  • 当前启用基于行模式且有打开的临时表(临时表仅在基于语句模式和混合模式下记录,切换格式可能导致复制问题;主从复制中主从节点格式必须一致;基于行模式下 DDL 语句仍用基于语句模式)。
  • 复制通道有打开的临时表。
  • 当前正在运行任何复制通道应用程序线程。

3. 混合模式自动切换格式

混合模式下,以下情况自动从基于语句模式切换到基于行模式:

  • DML 语句更新 NDBCLUSTER 表。
  • 函数中包含 UUID()
  • 更新含自增列的表且调用触发器或存储过程。
  • 视图主体需基于行模式时,创建视图的语句也使用该模式。
  • 调用自定义函数。
  • 语句按行记录且用到临时表时,后续语句均按行记录(直到临时表删除);8.0 版本中临时表操作不影响日志模式。
  • 用到 found_row()row_count()
  • 用到 user()current_user()current_user 变量。
  • 语句引用一个或多个系统变量。
  • 涉及 mysql 数据库中的日志表。
  • 用到 load_file()

十、二进制日志的事务压缩

1. 基本说明

启用后,使用 zstd 算法压缩事务有效负载,作为单个事件(Transaction_payload_event)写入二进制日志;以压缩状态发送到副本并写入中继日志,节省存储和带宽,但增加 CPU 和内存负载;仅在查看或回放时解压缩;可与连接压缩结合使用。

2. 不记入事务压缩的事件

  • 与事务的 GTID 相关的事件。
  • 其他控制事件(如视图更改事件、心跳事件)。
  • 突发事件及包含这些事件的所有事务。
  • 非事务性事件及包含它们的所有事务。
  • 基于语句的二进制日志记录的事件(仅适用于基于行的格式)。

3. 启用二进制日志压缩的动作

半同步复制中,副本收到完整的 Transaction_payload_event 时确认事务。

4. 合并压缩和未压缩的事务

支持压缩的 MySQL 版本可处理压缩和未压缩事务有效载荷的混合;复制/组复制中,不同主机的压缩相关系统变量可不同。

5. 配置

(1)系统变量

  • binlog_transaction_compression:启用压缩,默认 OFF。
  • binlog_transaction_compression_level_zstd:zstd 压缩级别,默认 3,范围 1-22。

(2)主从复制

  • 取代 CHANGE MASTER TOCHANGE REPLICATION SOURCE TO 包含:SOURCE_COMPRESSION_ALGORITHMSSOURCE_ZSTD_COMPRESSION_LEVEL

(3)组复制

  • group_replication_compression_threshold:组复制的压缩阈值。
  • group_replication_recovery_compression_algorithm:组复制恢复的压缩算法。
  • group_replication_recovery_zstd_compression_level:组复制恢复的 zstd 压缩级别。

6. 监控二进制事务压缩

(1)性能表

  • performance_schema.binary_log_transaction_compression_stats:统计压缩率及最后一个事务的影响,可通过 truncate 重置。

    mysql> select * from performance_schema.binary_log_transaction_compression_stats\G
    *************************** 1. row ***************************
                                LOG_TYPE: BINARY
                        COMPRESSION_TYPE: NONE
                     TRANSACTION_COUNTER: 1
                COMPRESSED_BYTES_COUNTER: 107
              UNCOMPRESSED_BYTES_COUNTER: 107
                  COMPRESSION_PERCENTAGE: 0
                    FIRST_TRANSACTION_ID: ANONYMOUS
      FIRST_TRANSACTION_COMPRESSED_BYTES: 107
    FIRST_TRANSACTION_UNCOMPRESSED_BYTES: 107
             FIRST_TRANSACTION_TIMESTAMP: 2021-03-17 08:15:07.809186
                     LAST_TRANSACTION_ID: ANONYMOUS
           LAST_TRANSACTION_COMPRESSED_BYTES: 107
         LAST_TRANSACTION_UNCOMPRESSED_BYTES: 107
                  LAST_TRANSACTION_TIMESTAMP: 2021-03-17 08:15:07.809186
    
  • performance_schema.events_stages_current:显示事务在压缩/解压缩阶段的状态及进度。

(2)查看帮助

mysqlbinlog --verbose --help | more