MySQL训练营-binlog和性能

基础知识回顾

binlog存储与格式

SHOW MASTER STATUS 是 MySQL 中用于显示主服务器(Master)二进制日志状态信息的命令。执行该命令后,通常会返回类似以下的结果:

+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 |      785 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

各字段含义

  1. File - 当前正在使用的二进制日志文件名

    • 格式通常为 mysql-bin.xxxxxx(数字递增)
    • 这是主服务器记录所有数据更改的文件
  2. Position - 当前二进制日志文件中的位置

    • 表示下一个事件将被写入的位置
    • 从服务器复制时需要指定这个位置
  3. Binlog_Do_DB - 指定只记录哪些数据库的二进制日志

    • 如果为空,表示记录所有数据库的更改
  4. Binlog_Ignore_DB - 指定不记录哪些数据库的二进制日志

    • 如果为空,表示没有数据库被排除
  5. Executed_Gtid_Set - 已执行的全局事务标识符集合(GTID)

    • 仅在使用GTID复制时显示
    • 格式为 source_id:transaction_id 的集合

GTID(Global Transaction Identifier,全局事务标识符)是 MySQL 5.6 版本引入的一种复制机制,它为每个事务分配一个全局唯一的标识符,极大地简化了主从复制的配置和管理。

GTID 的组成格式

GTID 的标准格式为:

source_id:transaction_id
  • source_id:产生事务的服务器唯一标识(通常是服务器的 server_uuid)
  • transaction_id:事务序列号,是一个递增的数字

示例:3E11FA47-71CA-11E1-9E33-C80AA9429562:23

GTID 的工作原理

  1. 主库执行事务时,会为每个事务分配一个 GTID
  2. GTID 会写入二进制日志(binlog)
  3. 从库读取主库的 binlog 时,会记录已执行的 GTID
  4. 从库重新连接时,会根据已执行的 GTID 自动定位复制位置

如何查看 GTID 相关信息

  1. 查看服务器 UUID(source_id 部分):

    SHOW VARIABLES LIKE 'server_uuid';
    
  2. 查看已执行的 GTID 集合:

    SHOW MASTER STATUS;
    SHOW SLAVE STATUS\G
    
  3. 查看所有已执行的 GTID:

    SELECT * FROM mysql.gtid_executed;
    

binlog_format 参数详解

binlog_format 是 MySQL 中控制二进制日志(binlog)记录格式的重要参数,它决定了数据库如何将数据更改事件写入二进制日志。

参数可选值

binlog_format 有三种可选格式:

  1. STATEMENT(语句模式)

    • 记录实际执行的 SQL 语句
    • 日志量较小
    • 可能出现主从不一致(如使用了不确定函数:NOW(), RAND()等)
  2. ROW(行模式,MySQL 5.7+ 默认值)

    • 记录每行数据的变化(修改前和修改后的值)
    • 日志量较大
    • 主从数据一致性最高
    • 可以配合 binlog_row_image 参数进一步控制
  3. MIXED(混合模式)

    • 默认使用 STATEMENT 模式
    • 对可能引起不一致的语句自动切换为 ROW 模式
    • 平衡了日志量和一致性需求

查看当前设置

SHOW VARIABLES LIKE 'binlog_format';
-- 或
SELECT @@global.binlog_format;

修改设置方法

  1. 临时修改(重启后失效):

    SET GLOBAL binlog_format = 'ROW';
    
  2. 永久修改(需在配置文件中修改):

    在 my.cnf/my.ini 的 [mysqld] 段添加:

    [mysqld]
    binlog_format = ROW
    

    然后重启 MySQL 服务

正确设置 binlog_format 对数据库复制、备份恢复和性能都有重要影响,应根据实际业务需求选择最合适的格式。

使用 mysqlbinlog 查看binlog

使用docker启动的mysqlbinlog没有该工具。

可以在宿主机中安装:sudo apt install mysql-server-core-8.0。在docker启动时,将log文件映射到宿主机上:

在宿主机上使用mysqlbinlog工具查看文件内容:

ROW 格式是 MySQL binlog 的三种格式之一(其他两种是 STATEMENT 和 MIXED),它记录的是表中行的实际更改内容,而不是执行的 SQL 语句。

  1. 基本查看命令

    mysqlbinlog mysql-bin.000014
    
  2. 查看 ROW 格式的详细内容

    mysqlbinlog -v mysql-bin.000014
    

    或更详细的:

    mysqlbinlog -vv mysql-bin.000014
    

如何理解 ROW 格式的输出

执行insert into users values(4,4);后产生的binlog

BEGIN
/*!*/;
# at 17135
#250706 15:38:16 server id 1  end_log_pos 17188 CRC32 0x024c80eb        Table_map: `mysql`.`users` mapped to number 130
# has_generated_invisible_primary_key=0
# at 17188
#250706 15:38:16 server id 1  end_log_pos 17232 CRC32 0xc708d774        Write_rows: table id 130 flags: STMT_END_F

BINLOG '
6CdqaBMBAAAANQAAACRDAAAAAIIAAAAAAAEABW15c3FsAAV1c2VycwACAwMAAwEBAOuATAI=
6CdqaB4BAAAALAAAAFBDAAAAAIIAAAAAAAEAAgAC/wAEAAAABAAAAHTXCMc=
'/*!*/;
### INSERT INTO `mysql`.`users`
### SET
###   @1=4 /* INT meta=0 nullable=1 is_null=0 */
###   @2=4 /* INT meta=0 nullable=1 is_null=0 */
# at 17232
#250706 15:38:16 server id 1  end_log_pos 17263 CRC32 0xe36ca2a4        Xid = 843
COMMIT/*!*/;

这段 binlog 记录了一个在 mysql.users 表上的 INSERT 操作,下面我将逐部分解释:

  1. 事务开始
BEGIN
/*!*/;

表示一个新事务的开始。

  1. 表映射信息
# at 17135
#250706 15:38:16 server id 1  end_log_pos 17188 CRC32 0x024c80eb        Table_map: `mysql`.`users` mapped to number 130
# has_generated_invisible_primary_key=0
  • 位置信息:位于 binlog 的 17135 位置
  • 时间戳:2025年7月6日 15:38:16
  • 服务器ID:1
  • 表映射:mysql.users 表被映射为内部编号 130
  • has_generated_invisible_primary_key=0 表示该表没有使用 MySQL 8.0 的隐式主键特性
  1. 写入行事件
# at 17188
#250706 15:38:16 server id 1  end_log_pos 17232 CRC32 0xc708d774        Write_rows: table id 130 flags: STMT_END_F

BINLOG '
6CdqaBMBAAAANQAAACRDAAAAAIIAAAAAAAEABW15c3FsAAV1c2VycwACAwMAAwEBAOuATAI=
6CdqaB4BAAAALAAAAFBDAAAAAIIAAAAAAAEAAgAC/wAEAAAABAAAAHTXCMc=
'/*!*/;
  • 这是一个 Write_rows 事件,表示插入操作
  • 表ID是130(对应前面映射的 mysql.users 表)
  • flags: STMT_END_F 表示这是语句的结束
  • 二进制数据部分(BASE64编码)包含了实际插入的行数据
  1. 插入的具体数据
### INSERT INTO `mysql`.`users`
### SET
###   @1=4 /* INT meta=0 nullable=1 is_null=0 */
###   @2=4 /* INT meta=0 nullable=1 is_null=0 */
  • mysql.users 表插入了一行数据
  • 第一列(@1)的值是4,类型是INT,可为空但实际不为空
  • 第二列(@2)的值也是4,类型是INT,可为空但实际不为空
  • 注意:@1@2表示列的位置,具体对应哪些列需要查看表结构
  1. 事务提交
# at 17232
#250706 15:38:16 server id 1  end_log_pos 17263 CRC32 0xe36ca2a4        Xid = 843
COMMIT/*!*/;
  • 事务提交,Xid是843(事务ID)
  • 所有更改被持久化

如何理解 STATEMENT 格式的输出

执行insert into users values(5,5);后产生的binlog

BEGIN
/*!*/;
# at 603
#250706 17:31:51 server id 1  end_log_pos 711 CRC32 0x59a4fe78  Query   thread_id=16    exec_time=0     error_code=0
use `mysql`/*!*/;
SET TIMESTAMP=1751794311/*!*/;
insert into users values(7,7)
/*!*/;
# at 711
#250706 17:31:51 server id 1  end_log_pos 742 CRC32 0xae5b01bf  Xid = 920
COMMIT/*!*/;

解析这段 MySQL binlog (STATEMENT 格式)

这段 binlog 记录了一个在 mysql.users 表上的 INSERT 操作,使用的是 STATEMENT 格式(与 ROW 格式不同,它记录的是实际执行的 SQL 语句)。下面是详细解析:

  1. 事务开始
BEGIN
/*!*/;

表示一个新事务的开始。

  1. 查询事件头信息
# at 603
#250706 17:31:51 server id 1  end_log_pos 711 CRC32 0x59a4fe78  Query   thread_id=16    exec_time=0     error_code=0
  • 位置信息:位于 binlog 的 603 位置
  • 时间戳:2025年7月6日 17:31:51
  • 服务器ID:1
  • 事件类型:Query(SQL语句执行事件)
  • 线程ID:16(执行该操作的客户端连接线程)
  • 执行时间:0秒
  • 错误代码:0(表示成功执行)
  1. 执行的SQL语句
use `mysql`/*!*/;
SET TIMESTAMP=1751794311/*!*/;
insert into users values(7,7)
/*!*/;

这部分包含实际执行的SQL语句序列:

  1. use mysql`` - 切换到 mysql 数据库

  2. SET TIMESTAMP=1751794311 - 设置时间戳(Unix时间戳1751794311对应2025-07-06 17:31:51)

  3. insert into users values(7,7) - 向 users 表插入一行数据,两列的值都是7

  4. 事务提交

# at 711
#250706 17:31:51 server id 1  end_log_pos 742 CRC32 0xae5b01bf  Xid = 920
COMMIT/*!*/;
  • Xid = 920:事务ID为920
  • COMMIT:提交事务,使更改永久生效

binlog.999999 之后再 flush binary logs 会怎么样?

创建binlog.1000000,6位数字不是真实大小。

MySQL 会在以下几种情况下清理二进制日志(binlog)文件:

  1. 自动清理机制

基于过期时间

  • 通过 expire_logs_daysbinlog_expire_logs_seconds 参数控制
  • 默认情况下,MySQL 会定期检查并删除超过指定时间的 binlog
  • 清理时机:
    • 服务器启动时
    • 执行 FLUSH LOGS
    • binlog 文件轮转时

基于空间限制 (MySQL 8.0+)

  • 通过 binlog_expire_logs_auto_purgebinlog_space_limit 控制
  • 当日志总大小超过限制时自动清理最旧的 binlog
  1. 手动清理命令

PURGE BINARY LOGS

-- 清理指定时间前的日志
PURGE BINARY LOGS BEFORE '2023-10-01 00:00:00';

-- 清理到指定日志文件之前的所有日志
PURGE BINARY LOGS TO 'binlog.000123';

RESET MASTER (谨慎使用)

RESET MASTER;  -- 删除所有binlog文件并重置编号
  1. 复制相关清理
  • 当配置了主从复制时,MySQL 会保留所有尚未被所有从库应用的 binlog
  • 通过 SHOW SLAVE STATUS 查看从库读取的 binlog 位置
  • 可以使用 PURGE BINARY LOGS 安全地清理已被所有从库应用的日志

服务一直开着,binlog 会不会把磁盘打爆?

binlog_expire_logs_auto_purge |  ON
binlog_expire_logs_seconds     | 2592000 |

MySQL提供了以下机制来防止这种情况:

  1. binlog_expire_logs_seconds (MySQL 8.0+)
  • 作用:设置 binlog 文件的过期时间(秒)
  • 默认值:30天(2592000秒)
  • 效果:超过这个时间的 binlog 文件会被自动删除
  • 优先级:高于 expire_logs_days(如果同时设置)
  1. binlog_expire_logs_auto_purge (MySQL 8.0.29+)
  • 作用:控制是否启用自动清理过期 binlog 的功能
  • 默认值:ON(启用)
  • 效果:当设置为 OFF 时,即使 binlog 过期也不会自动删除
  1. expire_logs_days (较旧版本)
  • 作用:设置 binlog 文件的过期天数
  • 替代方案:在 MySQL 8.0 中建议使用 binlog_expire_logs_seconds

工作机制

  1. 触发时机

    • 当 binlog 轮换(达到 max_binlog_size)时
    • 服务器启动时
    • 手动执行 FLUSH LOGS 时

    所以到达2592000秒不会进行切换,只有上诉三个时机。

  2. 清理过程

    • MySQL 会检查 binlog 文件的最后修改时间
    • 删除所有早于过期阈值的 binlog 文件
    • 保留至少一个 binlog 文件(即使已过期)
  3. 监控方法

    SHOW VARIABLES LIKE 'binlog_expire%';
    SHOW VARIABLES LIKE 'expire_logs%';
    SHOW BINARY LOGS;  -- 查看现有binlog文件
    

statement格式要被官方弃用了,本单元只讨论 binlog_format=row 的情况。

​​主要问题​​:

  • ​​安全性问题​​:非确定性函数可能导致主从不一致(如UUID(), NOW())
    ​- ​依赖问题​​:表结构必须完全一致
    ​- ​功能限制​​:不支持某些DDL操作的无损复制

MySQL主备同步

MySQL 主备同步(主从复制)是通过二进制日志(binlog)实现的异步数据复制过程,以下是完整的工作流程:

主库(Master)操作流程

  1. 事务执行

    • 应用程序向主库写入数据
    • 主库执行事务并更新存储引擎数据
  2. 写入binlog

    • 事务提交时,主库将更改记录到二进制日志(binlog)
    • 记录格式取决于binlog_format设置(ROW/STATEMENT/MIXED)
  3. binlog同步

    • 主库的binlog dump线程检测到新binlog事件
    • 根据从库请求的位置发送相应binlog内容

从库(Slave)操作流程

  1. I/O线程工作

    • 从库I/O线程连接主库,请求指定位置的binlog
    • 接收主库发送的binlog事件
    • 将事件写入从库的relay log(中继日志)
  2. SQL线程工作

    • 从库SQL线程读取relay log中的事件
    • 解析并执行这些事件,重放主库的操作
    • 更新从库数据使其与主库保持一致

影响主库性能的binlog参数

binlog_cache_size

binlog_checksum

binlog_group_commit_sync_delay

binlog_group_commit_sync_no_delay_count

binlog_order_commits

binlog_row_image

semi-sync...

binlog_cache_size

binlog_cache_size太小

当全局 binlog 缓存不足时,MySQL 会使用临时文件来存储 binlog 事件,具体机制如下:

缓存层级与溢出处理

  1. 缓存层级结构

    • 第一层:线程专属缓存(binlog_cache_size控制)
    • 第二层:全局共享缓存(binlog_stmt_cache_size控制)
    • 第三层:磁盘临时文件(当内存缓存不足时)
  2. 溢出到临时文件的触发条件

    • 当事务产生的 binlog 事件超过线程缓存大小
    • 并且全局缓存也无法满足需求时
    • 系统会自动创建临时文件存储超出的部分

相关状态变量

可以通过以下命令查看缓存使用情况:

SHOW GLOBAL STATUS LIKE 'Binlog_cache%';

关键指标:

  • Binlog_cache_disk_use:使用临时文件的次数
  • Binlog_cache_use:成功使用内存缓存的次数

性能影响

临时文件的影响

  • 显著增加I/O操作
  • 降低事务执行速度
  • 可能成为系统瓶颈

binlog_cache_size太大

binlog_cache_size 设置过大会带来多方面的负面影响,需要根据实际业务场景谨慎配置:

  1. 内存资源浪费
  • 每个连接预分配:每个客户端连接都会预先分配指定大小的缓存内存,即使实际不使用
  • 内存占用公式
    总内存占用 ≈ 连接数 × binlog_cache_size
    
  • 示例:1000连接 × 1MB设置 = 1GB内存常驻占用
  1. 系统稳定性风险
  • OOM风险:高并发连接时可能导致内存耗尽
  • 交换内存使用:可能触发系统使用swap,严重降低性能
  • 影响其他组件:挤占InnoDB缓冲池等关键组件内存
  1. 性能反优化
问题类型 影响表现
CPU缓存失效 过大的缓存降低CPU缓存命中率
TLB压力 内存页表项增加,导致TLB抖动
NUMA问题 跨NUMA节点访问内存延迟增加

binlog_checksum

binlog_checksum 是 MySQL 中控制二进制日志校验和的参数,它会在 binlog 事件写入时计算并存储校验和,在读取时进行验证,这对系统性能有一定影响。

  • 可选值

    • NONE:不计算校验和(MySQL 5.6.6 之前默认)
    • CRC32:使用 CRC32 算法计算校验和(5.6.6+ 默认)
    • SHA1/SHA256:使用更安全的哈希算法(较少使用)
  1. 写入性能影响
场景 影响程度 说明
高并发写入环境 中等 每个事务需要额外计算校验和,增加CPU开销
批量插入/大事务 较小 校验和计算是流式的,对大事务影响相对小
低负载系统 几乎无感 额外开销占比很小

典型性能下降:在极端高并发写入场景下,可能造成 2-5% 的吞吐量下降

  1. 读取/复制性能影响
场景 影响程度 说明
主从复制 中等 从库需要验证每个事件的校验和
基于binlog的恢复操作 较小 校验时间远小于实际应用事件时间
日常查询 无影响 不影响普通查询性能
  1. 不同算法的比较
算法 安全性 CPU开销 推荐场景
NONE 无保护 零开销 仅测试环境
CRC32 中等 低开销 生产环境默认
SHA1 较高开销 高安全性要求

最佳实践建议

  1. 生产环境建议保持默认CRC32

    SET GLOBAL binlog_checksum='CRC32';
    

    在数据安全性和性能之间取得良好平衡

  2. 性能敏感场景的优化

    • 如果确实需要极致性能且能接受潜在数据风险,可设为NONE
    • 配合定期校验工具替代实时校验
  3. 监控指标

    • 观察Com_binlog_*状态变量
    • 监控主从延迟情况
  4. 与其他参数的协同

    • sync_binlog配合使用(建议sync_binlog=1或100)
    • binlog_group_commit_sync_delay协同优化

在我们刚刚看到的binlog中就存在CRC32校验值:

#250706 15:38:16 server id 1  end_log_pos 17188 CRC32 0x024c80eb        Table_map: `mysql`.`users` mapped to number 130
# has_generated_invisible_primary_key=0
# at 17188
#250706 15:38:16 server id 1  end_log_pos 17232 CRC32 0xc708d774        Write_rows: table id 130 flags: STMT_END_F

sync_binlog

sync_binlog 参数决定了 MySQL 服务器在多少次二进制日志写入后将日志同步到磁盘。这个参数直接影响数据安全性和系统性能。

  • 0​:由操作系统决定何时同步到磁盘,性能最好但安全性最低。只write,不fsync。
  • 1​​:每次事务提交时都同步到磁盘,最安全但性能开销最大。每次都fsync。
  • N​​(大于1的数):每N次事务提交后同步一次,平衡安全性和性能。累计N个事务后才fsync。

sync_binlog非1时,OMM不会丢数据。因为已经提交给操作系统,操作系统不崩溃就能安全写入磁盘。

group_commit相关

在 MySQL 中,当使用 Group Commit(组提交)机制时,多个事务的提交操作可以被合并,从而减少实际的磁盘同步(sync)次数。

binlog_group_commit_sync_delay

1. 参数基本概念

binlog_group_commit_sync_delay 是 MySQL 5.7 及更高版本引入的一个二进制日志(binlog)组提交(group commit)优化参数,用于控制事务提交时二进制日志的同步延迟时间。

2. 参数定义
  • 作用:指定在同步二进制日志到磁盘前等待的微秒数,以便将更多事务分组提交
  • 默认值:0(无延迟)
  • 取值范围:0~1000000(即最大可设置1秒延迟)
  • 动态修改:支持运行时修改(SET GLOBAL)
  • 适用版本:MySQL 5.7+
3. 工作机制

3.1 基本工作流程

  1. 事务进入提交阶段
  2. 检查当前是否有其他事务正在提交
  3. 如果有,则等待加入组提交
  4. 等待 binlog_group_commit_sync_delay 指定的微秒数
  5. 将组内所有事务的二进制日志一次性写入并同步到磁盘
  6. 通知所有事务提交完成

3.2 详细机制

当设置 binlog_group_commit_sync_delay=N(N>0)时:

  1. 事务收集阶段

    • 第一个到达提交阶段的事务成为"领导者"(leader)
    • 后续到达的事务成为"跟随者"(follower),加入当前组
    • 系统启动一个计时器,等待N微秒
  2. 等待策略

    • 在等待期间,新到达的事务可以不断加入当前组
    • 如果等待期间组内事务数达到 binlog_group_commit_sync_no_delay_count 设置的值,则立即提交,不等待完整延迟时间
    • 如果没有任何其他事务加入,单个事务也会在延迟结束后提交
  3. 磁盘同步

    • 等待结束后,整个组的事务二进制日志被一次性写入并同步到磁盘
    • 然后所有事务被标记为提交完成
4. 实测
mysql> set global binlog_group_commit_sync_delay=1000000;
Query OK, 0 rows affected (0.00 sec)

mysql> create table t12(id int);
Query OK, 0 rows affected (1.03 sec)

mysql> insert into t12 values(1);
Query OK, 1 row affected (1.01 sec)

由于 binlog_group_commit_sync_delay=1000000(1秒),且没有并发事务,每个操作都等待了完整的 1 秒才提交。

binlog_group_commit_sync_no_delay_count

binlog_group_commit_sync_no_delay_count 是 MySQL 5.7+ 引入的二进制日志组提交(group commit)优化参数,与 binlog_group_commit_sync_delay 协同工作,共同控制事务提交的合并行为。

特性 binlog_group_commit_sync_delay=10000 + no_delay_count=10 sync_binlog=10
控制维度 基于 时间+事务数 的动态组提交 基于 固定事务数 的同步
同步触发条件 (1)等待10ms (2)积累10个事务 每10个事务强制同步
延迟机制 主动等待更多事务合并(最多10ms) 无主动等待
事务合并能力 更强(可跨多个"逻辑组"合并) 较弱(严格按N事务分组)
数据安全性 较高(可配置较短等待时间) 较低(可能丢失最后N-1个事务)
适用版本 MySQL 5.7+ 所有版本

binlog_order_commits

binlog_order_commits 是 MySQL 中控制事务提交顺序的重要参数,它影响事务的提交顺序保证。

1. 参数基本概念

  • 作用:控制是否按照二进制日志(binlog)中的顺序提交存储引擎层的事务
  • 默认值:ON (MySQL 5.7+), OFF (MySQL 5.6)
  • 动态修改:支持运行时修改(SET GLOBAL)
  • 适用版本:MySQL 5.6+

2. 工作机制

2.1 参数开启(ON)时
  • 严格顺序提交

    • 事务在存储引擎(InnoDB)中的提交顺序
    • 严格与binlog中的写入顺序保持一致
    • 通过内部队列机制保证顺序
  • 工作流程

    1. 事务写入binlog
    2. 进入提交队列(按binlog顺序)
    3. 依次在InnoDB层提交
2.2 参数关闭(OFF)时
  • 并行提交
    • 存储引擎层的事务提交可以并行执行
    • 可能快于binlog的写入顺序
    • 仅保证单个事务的原子性

3. 性能影响

场景 binlog_order_commits=ON binlog_order_commits=OFF
高并发写入 吞吐量较低(顺序限制) 吞吐量更高(并行提交)
低并发 影响不明显 影响不明显
从库应用 更有序,减少冲突 可能增加并行度
崩溃恢复 更易确定恢复点 恢复顺序可能不一致

binlog_row_image

binlog_row_image 是 MySQL 中控制行格式二进制日志(binlog)记录内容的参数,它决定了在行格式复制(Row-Based Replication)时,binlog 中记录的行数据包含哪些列信息。

1. 参数基本概念

  • 作用:控制行格式二进制日志记录中包含的列信息
  • 默认值:FULL (MySQL 5.6+), 早期版本为FULL或NOBLOB
  • 可选值
    • FULL:记录所有列的值
    • MINIMAL:仅记录被修改的列和唯一索引列
    • NOBLOB:记录所有非BLOB列和修改的BLOB列
  • 动态修改:支持运行时修改(SET GLOBAL)
  • 适用版本:MySQL 5.6+

2. 不同模式详解

2.1 FULL(完全模式)
  • 记录内容:所有列的值(无论是否被修改)
  • 优点
    • 数据最完整
    • 从库应用更安全
    • 便于数据恢复
  • 缺点
    • binlog体积最大
    • 网络传输开销大
2.2 MINIMAL(最小模式)
  • 记录内容
    • 被修改的列
    • 所有唯一索引/主键列(用于行定位)
  • 优点
    • binlog体积最小
    • 网络传输效率高
  • 缺点
    • 从库应用时可能出现问题(如缺少非索引列)
    • 数据恢复可能不完整
2.3 NOBLOB(非BLOB模式)
  • 记录内容
    • 所有非BLOB列
    • 被修改的BLOB列
  • 优点
    • 平衡了体积和安全性
    • 减少大字段传输
  • 缺点
    • 未修改的BLOB列不会记录

3. 配置建议

场景 推荐设置 说明
生产环境(默认) FULL 保证数据完整性和安全性
大表频繁更新 NOBLOB 减少BLOB字段记录
带宽受限环境 MINIMAL 最小化日志体积
无BLOB字段表 FULL或MINIMAL 根据安全性需求选择

semi-sync

半同步复制是介于异步复制和全同步复制之间的一种复制方式,它确保至少一个从库接收到事务事件后,主库才会向客户端返回事务提交成功的响应。

如上图所示,一般的主备集群主库对外提供读写能力,同时将修改同步到备机,如果主机挂了会切换备机为主机继续工作。semi-sync的设置可以保证主机的修改至少同步到了一个备机。

课堂练习

在主库单线程执行纯 insert 语句的情况下, 开启下面这些参数, 对性能(TPS)的影响,从高到低是?

  1. binlog_checksum (on vs off)
  2. sync binlog (1 vs 0)
  3. binlog_group_commit_sync_delay (1000ms vs 0)
  4. binlog_order_commits (on vs off)
  5. binlog_row_image (full vs minimal)
  6. semi-sync (on vs off)

影响程度排序(高 → 低)

  1. binlog_group_commit_sync_delay (1000ms vs 0)

    • 直接影响=1000ms 强制每个事务等待1秒提交
    • 性能差异:理论TPS上限从1000降至1(单线程无法利用组提交优势)
  2. semi-sync (ON vs OFF)

    • 网络开销:需等待从库ACK,增加至少1个RTT时间
    • 性能差异OFFON30-50%(依赖网络延迟)
  3. sync_binlog (1 vs 0)

    • 最大影响sync_binlog=1 每次提交都触发磁盘同步(fsync),导致极高I/O延迟
    • 性能差异=0=110-100倍(机械硬盘最明显)
  4. binlog_checksum (ON vs OFF)

    • CPU开销ON 需计算CRC32校验和
    • 性能差异OFF 提升 2-5%(现代CPU影响较小)
  5. binlog_order_commits (ON vs OFF)

    • 并发限制:单线程场景下影响较小
  6. binlog_row_image (FULL vs MINIMAL)

    • 日志体积:单线程INSERT时两者记录内容相同

影响主备同步的binlog参数

  • replica_sql_verify_checksum:控制从库是否校验从主库接收到的binlog事件的校验和,默认开启(ON),确保数据传输完整性。
  • semi_sync):主库提交事务时需等待至少一个从库接收并确认binlog后才返回成功,平衡数据安全性与性能,默认关闭(OFF)。

主备延迟常见原因:

  1. 机器负载
  2. 备库读压力
  3. 大事务
  4. 并发度

一般不用处理,正常现象

主备同步流程图

MySQL 主备同步流程详解

1. 主库(Master A)处理流程

  1. 事务启动:用户发起数据变更(start
  2. 内存记录
    • 写入内存中的 undo log(undolog (mem))用于回滚
    • 更新内存数据页(data (mem)
  3. 日志持久化
    • Prepare阶段:写入 redo log(redo log (prepare)),标记事务准备状态
    • Binlog写入:同步记录二进制日志(binlog),用于主备同步
    • Commit阶段:提交 redo log(redo log (commit)),事务正式生效
  4. 后台刷盘
    • bg-thread 将内存中的 undo log 和数据页异步刷盘(undolog (disk) / data (disk)
  5. 日志传输
    • dump_thread 将 binlog 发送给从库

2. 从库(Slave B)同步流程

  1. 接收日志
    • io_thread 线程接收主库的 binlog,写入本地中转日志(relay log
  2. 重放事务
    • sql_thread 线程读取 relay log 中的事件,在从库执行相同操作,更新数据(DATA
  3. 状态反馈
    • 从库通过 ack 机制向主库确认日志接收位置(半同步复制场景)

早期版本 SQL 线程是单线程,可能成为复制瓶颈。MySQL 5.7+ 支持多线程回放(需配置 slave_parallel_workers)。

MySQL 并行复制策略详解

MySQL 的并行复制策略是提升主从复制性能的关键技术,不同版本逐步优化了并行复制的实现方式。以下是主要的并行复制策略及其演进过程:

1. 基于数据库的并行复制 (5.6版本)

策略:按数据库名并行

  • 实现方式:不同数据库的事务可以并行执行
  • 配置参数
    slave_parallel_workers=4  # 设置并行工作线程数
    slave_parallel_type=DATABASE
    
  • 优点:实现简单,减少锁冲突
  • 缺点:单库场景无效,不适用分库分表环境

2. 基于逻辑时钟的并行复制 (5.7版本)

策略:基于事务组并行

  • 实现方式:利用 LOGICAL_CLOCK 机制,无冲突事务可并行
  • 配置参数
    slave_parallel_workers=8
    slave_parallel_type=LOGICAL_CLOCK
    
  • 关键机制
    • 主库通过 binlog_group_commit_sync_delay 增加组提交机会
    • 同一组提交的事务可以并行执行
  • 优点:单库也能并行,提升30-50%性能
  • 缺点:对DDL操作仍需串行

3. 基于WRITESET的并行复制 (8.0版本)

策略:基于行哈希值并行

  • 实现方式:计算行数据的WRITESET哈希,无冲突事务可并行
  • 配置参数
    slave_parallel_workers=16
    slave_parallel_type=LOGICAL_CLOCK
    binlog_transaction_dependency_tracking=WRITESET
    
  • 关键优化
    • 自动识别无冲突事务(即使未在同一组提交)
    • 支持 WRITESET_SESSION 模式保证会话顺序
  • 优点:并行度更高,提升50-100%性能
  • 缺点:需要计算行哈希,略微增加CPU开销

4. 增强型并行复制 (8.0.23+)

策略:事务依赖关系优化

  • 新特性
    • 支持 COMMIT_ORDERWRITESET 混合模式
    • 自动适应不同负载特征
  • 配置示例
    binlog_transaction_dependency_history_size=25000
    

性能对比测试数据

策略 TPS提升 适用场景
单线程复制 基准 兼容所有版本
DATABASE并行 20-30% 多库环境
LOGICAL_CLOCK 30-50% 5.7+单库高并发
WRITESET 50-100% 8.0+高并发OLTP
WRITESET_SESSION 40-80% 需保持会话顺序的场景
posted @ 2025-07-06 17:15  余为民同志  阅读(52)  评论(0)    收藏  举报