找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1499

积分

0

好友

190

主题
发表于 14 小时前 | 查看: 2| 回复: 0

在 MySQL 的运维与开发中,binlog(二进制日志)是实现主从复制、数据恢复的核心组件。了解哪些操作不会被记录到 binlog 对于深入理解复制机制、排查数据一致性问题以及进行性能优化至关重要。本文将深入 MySQL 源码,系统剖析在 ROW 格式(当前最常用的格式)下,判断一个操作是否需要写入 binlog 的核心逻辑,并全面梳理导致 binlog 写入被“静默”的各类场景。

ROW 格式下判断操作是否写入 binlog 的实现逻辑

在 ROW 格式下,将数据行的变更记录到 binlog 的核心函数是 binlog_log_row。其简化逻辑如下:

int binlog_log_row(TABLE *table, const uchar *before_record,
                   const uchar *after_record, Log_func *log_func) {
  bool error = false;
  THD *const thd = table->in_use;

  // 关键判断:当前操作是否需要写入 binlog
  if (check_table_binlog_row_based(thd, table)) {
    // ... 执行具体的binlog写入操作
  }
  return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
}

函数首先调用 check_table_binlog_row_based 进行判断。若返回 true,则根据操作类型(INSERT/UPDATE/DELETE)调用相应的处理函数将行数据镜像写入 binlog。因此,我们的分析重点就在于 check_table_binlog_row_based 函数。

static bool check_table_binlog_row_based(THD *thd, TABLE *table) {
  // ... (缓存检查逻辑,后文详述)
  return (thd->is_current_stmt_binlog_format_row() &&
          table->s->cached_row_logging_check &&
          (thd->variables.option_bits & OPTION_BIN_LOG) &&
          mysql_bin_log.is_open());
}

该函数返回 false(即不写binlog)的条件非常清晰,只需满足以下任意一项:

  1. 当前语句非ROW格式thd->is_current_stmt_binlog_format_row()false(如DDL语句)。
  2. 表不允许复制table->s->cached_row_logging_checkfalse
  3. 线程级binlog被关闭(thd->variables.option_bits & OPTION_BIN_LOG)false
  4. binlog文件未打开mysql_bin_log.is_open()false

条件1和4通常不构成常见例外,因此下文将重点解析条件2和条件3为 false 的各种具体场景,这也是理解 MySQL主从复制机制 异常行为的关键。

场景一:cached_row_logging_check 为 false

该标志的赋值逻辑如下,关键在于三个子条件的“与”关系:

int const check(table->s->tmp_table == NO_TMP_TABLE &&
                !table->no_replicate &&
                binlog_filter->db_ok(table->s->db.str));
1. 操作对象为临时表

table->s->tmp_table != NO_TMP_TABLE,即当前操作针对的是用户创建的临时表(CREATE TEMPORARY TABLE),则不会写入 binlog。这是为了隔离会话间的临时数据。

2. 库名被复制过滤器排除

如果库名不满足 --replicate-do-db--replicate-ignore-db 等参数设置的复制规则,binlog_filter->db_ok() 将返回 false。这属于主动配置的复制过滤行为。

3. 表属性 no_replicate 被设置为 true

这是最核心且情况最复杂的部分。no_replicate 属性在 open_table_from_share() 函数中根据表的类别和存储引擎能力进行设置。

A. 特殊类别的系统表
以下类别的表天生就被标记为 no_replicate

  • TABLE_CATEGORY_LOG:包括 mysql.general_logmysql.slow_log。这正是文章开篇问题的答案:对慢日志表的DML操作(如插入慢查询记录)不会写入 binlog,因此不会复制到从库或MGR节点。
  • TABLE_CATEGORY_RPL_INFO:复制信息表,如 mysql.slave_master_info, mysql.slave_relay_log_info, mysql.slave_worker_info
  • TABLE_CATEGORY_GTID:如 mysql.gtid_executed

B. 存储引擎能力声明
存储引擎通过标志位向Server层声明其复制支持能力。相关标志有:

  • HA_BINLOG_STMT_CAPABLE:支持STATEMENT格式。
  • HA_BINLOG_ROW_CAPABLE:支持ROW格式。
  • HA_HAS_OWN_BINLOGGING:引擎自行管理binlog(如NDB Cluster)。

no_replicate 在以下情况被设为 true

outparam->no_replicate =
    !(flags & (HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE)) ||
    (flags & HA_HAS_OWN_BINLOGGING);

这意味着,如果一个存储引擎既不支持STATEMENT也不支持ROW格式,或者宣称自己管理binlog,那么对它的操作就不会被Server层的binlog记录。

在MySQL内置引擎中,performance_schematemptable(用于内部临时表)存储引擎没有设置上述任一能力标志。因此,对所有 performance_schema 下表的DML操作都不会写入 binlog。这也是为什么即使用root用户,通常也无法直接对 performance_schema 中的表进行DML操作(会报权限错误),但部分表允许的 TRUNCATE 操作同样不写binlog。

场景二:OPTION_BIN_LOG 为 false

OPTION_BIN_LOG 是线程级变量 option_bits 中的一个位标志,控制当前会话的操作是否写入 binlog

1. 会话级显式关闭

通过 SET SESSION sql_log_bin = 0; 命令,可以临时关闭当前会话的binlog记录。其回调函数会清除 OPTION_BIN_LOG 标志。

2. 从库SQL线程默认行为

当实例作为从库运行时,其SQL线程(或replica线程)默认不将重放的应用事件写入自身的binlog,除非显式开启了 log_replica_updates(或旧版的 log_slave_updates)参数。这主要是为了避免循环复制和节省空间。

3. 内部使用 Disable_binlog_guard

MySQL源码中广泛使用 Disable_binlog_guard 这个RAII守卫类,在特定代码块内临时关闭binlog记录,离开作用域后自动恢复。主要场景包括:

  • 实例初始化 (--initialize):创建系统表时。
  • 实例升级:升级系统表结构时。
  • 插件与组件管理INSTALL/UNINSTALL PLUGIN/COMPONENT
  • 服务器对象管理CREATE/ALTER/DROP SERVER
  • 内部维护操作:如 ALTER TABLE 过程中创建/删除中间临时表、DROP DATABASE 清理元数据、更新数据字典、后台自动更新列统计信息(直方图)等。这些操作是MySQL为了维护数据一致性和 数据库性能 而自动执行的。

附加场景:语句级控制 no_write_to_binlog

除了上述基于线程和表的控制,MySQL还支持在语句级别通过将 thd->lex->no_write_to_binlog 设置为 true 来避免写入binlog。

1. 特定的管理命令

SHUTDOWNRESTARTRESET MASTERRESET SLAVERESET PERSIST 等。

2. 显式关键字

部分维护类SQL支持 NO_WRITE_TO_BINLOG 或它的别名 LOCAL 关键字:

OPTIMIZE NO_WRITE_TO_BINLOG TABLE t1;
ANALYZE LOCAL TABLE t1;
REPAIR NO_WRITE_TO_BINLOG TABLE t1;
FLUSH LOCAL PRIVILEGES;

需要注意的是,某些 FLUSH 命令即使不指定关键字,默认也不写binlog,例如 FLUSH LOGSFLUSH BINARY LOGSFLUSH TABLES WITH READ LOCK

总结与实践指导

综上所述,MySQL通过多层级的精细控制来决定是否将操作写入 binlog。尽管场景列举较多,但可以遵循一个核心原则来记忆:

凡是MySQL服务器内部自动执行的操作(非用户主动发起的DML/DDL),通常都不会写入binlog。

这包括了系统表的维护(如slow_log)、复制元信息更新、数据字典变更、后台统计信息收集等。反之,用户对普通表发起的DML操作,在未主动关闭binlog且无复制过滤的情况下,都会正常记录。

理解这些机制,有助于我们:

  1. 正确解读主从数据不一致问题,排除因系统表未复制导致的误判。
  2. 安全地进行实例初始化和升级,知晓这些操作不会影响复制链路。
  3. 在需要进行 大规模数据维护 时,合理使用 sql_log_binNO_WRITE_TO_BINLOG 关键字来提升效率并控制binlog体积。



上一篇:GPU SDC静默数据错误深度解析:大模型训练与自动驾驶场景的影响与应对
下一篇:scikit-image图像预处理实战:10个提升计算机视觉模型鲁棒性的工程技巧
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-24 17:19 , Processed in 0.150628 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表