news 2026/5/1 10:32:14

MySQL 在哪些场景下不会写 binlog

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL 在哪些场景下不会写 binlog

背景

在 MySQL 中,慢日志不仅可以记录在文件中,还可以记录在表中。具体是记录在文件还是表中是由log_output参数决定的。

该参数默认为FILE,即慢日志默认会记录在文件中。如果参数中包含TABLE,则慢日志还会记录在mysql.slow_log中,而mysql.slow_log使用的是 CSV 存储引擎。

最初研究这一问题,是为了确认在主从复制以及组复制(MGR)环境下,mysql.slow_log表中的慢日志是否会同步到其他节点。

随着分析的深入,发现 MySQL 实际上提供了多种机制和开关,用于确保操作不会写入 binlog。

由于ROW 格式是目前最常用的 binlog 格式,本文将从ROW 模式下 MySQL 判断操作是否写入 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)) { ... if (likely(!(error = write_locked_table_maps(thd)))) { boolconst has_trans = thd->lex->sql_command == SQLCOM_CREATE_TABLE || table->file->has_transactions(); // 根据操作类型,将行镜像写入 binlog error = (*log_func)(thd, table, has_trans, before_record, after_record); } } return error ? HA_ERR_RBR_LOGGING_FAILED : 0; }

首先调用check_table_binlog_row_based判断当前操作是否需要写入 binlog,若需要,则会针对不同的操作类型,调用不同的函数来处理。具体来说:

  • INSERT:Write_rows_log_event::binlog_row_logging_function

  • UPDATE:Update_rows_log_event::binlog_row_logging_function

  • DELETE:Delete_rows_log_event::binlog_row_logging_function

接下来,重点看看check_table_binlog_row_based函数的处理逻辑。

static bool check_table_binlog_row_based(THD *thd, TABLE *table) { if (table->s->cached_row_logging_check == -1) { int const check(table->s->tmp_table == NO_TMP_TABLE && !table->no_replicate && binlog_filter->db_ok(table->s->db.str)); table->s->cached_row_logging_check = check; } assert(table->s->cached_row_logging_check == 0 || table->s->cached_row_logging_check == 1); 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,只需满足以下任意一个条件:

  • 当前 SQL 语句不能以 ROW 格式记录到 binlog 中:thd->is_current_stmt_binlog_format_row()为 false,例如 DDL 语句。

  • 表不允许写入 binlog:table->s->cached_row_logging_check为 false。

  • 当前线程未启用 binlog:thd->variables.option_bits & OPTION_BIN_LOG为 false。

  • binlog 未打开:mysql_bin_log.is_open()为 false。

因为第一个条件和第四个条件为 false 的情况并不常见,下面将重点分析table->s->cached_row_logging_checkthd->variables.option_bits & OPTION_BIN_LOG为 false 时的场景。

cached_row_logging_check 为 false 的场景

table->s->cached_row_logging_check的赋值逻辑如下:

if (table->s->cached_row_logging_check == -1) { int const check(table->s->tmp_table == NO_TMP_TABLE && !table->no_replicate && binlog_filter->db_ok(table->s->db.str)); table->s->cached_row_logging_check = check; }

要使其为 false,必须满足以下任意一个条件:

  1. 当前表是临时表:table->s->tmp_table == NO_TMP_TABLE为 false。

  2. 库名不满足 --replicate-do-db、--replicate-ignore-db 复制规则:binlog_filter->db_ok(table->s->db.str)为 false。

  3. 表设置了 no_replicate。该属性是在open_table_from_share()函数中根据表的类型和存储引擎能力标志设置的。

no_replicate 的设置逻辑如下:

if ((share->table_category == TABLE_CATEGORY_LOG) || (share->table_category == TABLE_CATEGORY_RPL_INFO) || (share->table_category == TABLE_CATEGORY_GTID)) { outparam->no_replicate = true; } else if (outparam->file) { const handler::Table_flags flags = outparam->file->ha_table_flags(); outparam->no_replicate = !(flags & (HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE)) || (flags & HA_HAS_OWN_BINLOGGING); } else { outparam->no_replicate = false; }

可以看到,no_replicate 会在以下几种情况设置为 true。

一、特殊类别的表。包括:

  • TABLE_CATEGORY_LOG 类别的表,具体包括 mysql.general_log, mysql.slow_log。

  • TABLE_CATEGORY_RPL_INFO 类别的表,具体包括 mysql.slave_relay_log_info,mysql.slave_master_info,mysql.slave_worker_info。

  • TABLE_CATEGORY_GTID 类别的表,具体包括 mysql.gtid_executed。

二、根据存储引擎的能力标志判断。

这些标志是每个存储引擎单独设置的,一般是在m_int_table_flagstable_flags函数中定义的,主要是用来向 Server 层声明:这个存储引擎的表,支持哪些能力/约束。与复制相关的标志有三个:

  • HA_BINLOG_STMT_CAPABLE:支持 STATEMENT 格式 binlog。

  • HA_BINLOG_ROW_CAPABLE:支持 ROW 格式 binlog

  • HA_HAS_OWN_BINLOGGING:该引擎自己管理 binlog(如 NDB Cluster)。

在 MySQL 支持的存储引擎中,只有 perfschema(对应 performance_schema)和 temptable(MySQL 8.0 引入的内部临时表存储引擎,主要用来替代老的 MEMORY/MyISAM 内部临时表)不会设置 HA_BINLOG_STMT_CAPABLE 或 HA_BINLOG_ROW_CAPABLE。

所以,针对 performance_schema 表的操作不会写入 binlog。

# ls mysql-8.4.3/storage/ archive blackhole csv example federated heap innobase myisam myisammrg ndb perfschema secondary_engine_mock temptable

OPTION_BIN_LOG 为 false 的场景

thd->variables保存当前线程的会话级系统变量状态。其中,option_bits 是一个位图(bitmap),用于记录多个线程级选项标志,OPTION_BIN_LOG 则表示是否将当前线程的操作写入 binlog。

以下是几种典型场景。

一、显式关闭会话级 binlog

SET SESSION sql_log_bin = 0;

该参数对应的回调函数是fix_sql_log_bin_after_update

sql_log_bin = 1时,打开 OPTION_BIN_LOG,反之,则清除 OPTION_BIN_LOG。

static bool fix_sql_log_bin_after_update(sys_var *, THD *thd, enum_var_type type [[maybe_unused]]) { assert(type == OPT_SESSION); if (thd->variables.sql_log_bin) thd->variables.option_bits |= OPTION_BIN_LOG; else thd->variables.option_bits &= ~OPTION_BIN_LOG; return false; }

二、从库未启用 log_replica_updates

当实例作为从库运行,且未开启 log_replica_updates 时,从库 SQL 线程重放的操作默认不写 binlog。

void set_slave_thread_options(THD *thd) { ... ulonglong options = thd->variables.option_bits | OPTION_BIG_SELECTS; if (opt_log_replica_updates) options |= OPTION_BIN_LOG; else options &= ~OPTION_BIN_LOG; ... }

三、使用Disable_binlog_guard临时关闭 binlog

Disable_binlog_guard用于在特定代码块内临时关闭 binlog,并在离开作用域时自动恢复原状态。

class Disable_binlog_guard { public: explicit Disable_binlog_guard(THD *thd) : m_thd(thd), m_binlog_disabled(thd->variables.option_bits & OPTION_BIN_LOG) { thd->variables.option_bits &= ~OPTION_BIN_LOG; } ~Disable_binlog_guard() { if (m_binlog_disabled) m_thd->variables.option_bits |= OPTION_BIN_LOG; } private: THD *const m_thd; constbool m_binlog_disabled; };

Disable_binlog_guard 被调用的场景有:

3.1 实例初始化(--initialize

static bool handle_bootstrap_impl(handle_bootstrap_args *args) { ... if (opt_initialize) { assert(thd->system_thread == SYSTEM_THREAD_SERVER_INITIALIZE); sysd::notify("STATUS=Initialization of MySQL system tables in progress\n"); const Disable_binlog_guard disable_binlog(thd); const Disable_sql_log_bin_guard disable_sql_log_bin(thd); Compiled_in_command_iterator comp_iter; rc = process_iterator(thd, &comp_iter, true); thd->system_thread = SYSTEM_THREAD_INIT_FILE; sysd::notify("STATUS=Initialization of MySQL system tables ", rc ? "unsuccessful" : "successful", "\n"); if (rc != 0) { returntrue; } } ... returnfalse; }

3.2 实例升级

bool upgrade_system_schemas(THD *thd) { Disable_autocommit_guard autocommit_guard(thd); Bootstrap_error_handler bootstrap_error_handler; Server_option_guard<bool> acl_guard(&opt_noacl, true); Server_option_guard<bool> general_log_guard(&opt_general_log, false); Server_option_guard<bool> slow_log_guard(&opt_slow_log, false); Disable_binlog_guard disable_binlog(thd); Disable_sql_log_bin_guard disable_sql_log_bin(thd); ... bootstrap_error_handler.set_log_error(false); bool err = fix_mysql_tables(thd) || fix_sys_schema(thd) || upgrade_help_tables(thd); if (!err) { /* Initialize structures necessary for federated server from mysql.servers table. */ servers_init(thd); err = (DBUG_EVALUATE_IF("force_fix_user_schemas", true, dd::bootstrap::DD_bootstrap_ctx::instance() .is_server_upgrade_from_before( bootstrap::SERVER_VERSION_80011)) ? check.check_all_schemas(thd) : check.check_system_schemas(thd)) || check.repair_tables(thd) || dd::tables::DD_properties::instance().set( thd, "MYSQLD_VERSION_UPGRADED", MYSQL_VERSION_ID); } ... return dd::end_transaction(thd, err); }

3.3 CREATE SERVER, ALTER SERVER 和 DROP SERVER 操作。

3.4 INSTALL COMPONENT, UNINSTALL COMPONENT 操作。

3.5 INSTALL PLUGIN, UNINSTALL PLUGIN 操作。

3.6 一些内部操作,例如 ALTER TABLE 过程中创建/删除临时表、DROP DATABASE 时清理数据库对象、更新数据字典表、后台线程自动更新列直方图。

除了上面介绍的这些场景,通过将thd->lex->no_write_to_binlog设置为truethd->lex表示当前 SQL 语句的语法解析上下文),可以在语句级别控制该语句不写入 binlog。

NO_WRITE_TO_BINLOG 为 true 的场景

以下场景会将 no_write_to_binlog 设置为 true。

  1. SHUTDOWN、RESTART 命令。

  2. RESET 系列命令,包括:RESET MASTER, RESET SLAVE, RESET PERSIST。

  3. 显式指定NO_WRITE_TO_BINLOGLOCAL。部分维护类 SQL 命令(OPTIMIZE, ANALYZE, REPAIR, FLUSH)支持在语句中显式指定不写 binlog,如,

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

需要注意的是,对于FLUSH命令,即使未显式指定NO_WRITE_TO_BINLOG,以下命令默认也不会写入 binlog:NO_WRITE_TO_BINLOG,FLUSH LOGS、FLUSH BINARY LOGS、FLUSH TABLES WITH READ LOCK、FLUSH TABLES tbl_name ... FOR EXPORT。

总结

虽然上面列举的场景较多,但实际上并不需要大家刻意去记。

简单来说,

  • 凡是 MySQL 内部自动执行的操作(即非用户手动执行的操作),通常不会写入 binlog。 典型场景包括:实例初始化与升级、mysql.slow_log表的写入、数据字典的维护、performance_schema表数据的更新等。

  • 对 mysql 库下的表进行 DML 操作,只要不属于上面提到的特殊类别的表,基本都会写入 binlog。

    但若执行的是 DDL 操作(如 truncate),基本都会写入 binlog。

  • 对 performance_schema 中的表进行 DML、DDL 操作会提示权限不足,即便是用 root 用户执行。但部分表允许执行 truncate 操作,且 truncate 操作不会写入 binlog。

文章转载自:iVictor

原文链接:https://www.cnblogs.com/ivictor/p/19382809

体验地址:http://www.jnpfsoft.com/?from=001YH

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 7:17:49

生态学中的R语言实战:数据探索、回归与排序分析详解

R语言作为新兴的统计软件&#xff0c;以开源、自由、免费等特点风靡全球。生态环境领域研究内容广泛&#xff0c;数据常多样而复杂。利用R语言进行多元统计分析&#xff0c;从复杂的现象中发现规律、探索机制正是R的优势。为此&#xff0c;本课程以鱼类、昆虫、水文、地形等多样…

作者头像 李华
网站建设 2026/5/1 8:39:06

设计模式:责任链模式

目录 二、核心角色 三、典型结构与代码示例 1. 极简代码实现&#xff08;通用模板&#xff09; 2. 关键特性 四、责任链模式的两种实现方式 1. 纯责任链&#xff08;严格传递&#xff09; 2. 不纯责任链&#xff08;灵活处理&#xff09; 五、典型应用场景 六、优缺点…

作者头像 李华
网站建设 2026/5/1 8:40:12

外部群自动化中的“静默心跳”存活检测

在部署大规模 RPA 集群执行外部群任务时&#xff0c;最棘手的问题不是脚本报错&#xff0c;而是“软失效”——即企微窗口看似正常&#xff0c;但由于网络微断开或底层通信挂钩&#xff08;Hook&#xff09;失效&#xff0c;导致 RPA 发出的指令被静默丢弃。为此&#xff0c;需…

作者头像 李华
网站建设 2026/5/1 5:03:34

个人开发者接入拼多多开放平台

大家好&#xff0c;我是小悟。 一、拼多多开放平台概述 拼多多开放平台&#xff08;Pinduoduo Open Platform&#xff09;是拼多多为第三方开发者提供的一套API接口服务体系&#xff0c;允许开发者获取拼多多的商品、订单、物流、营销等数据&#xff0c;并实现与拼多多系统的集…

作者头像 李华
网站建设 2026/5/1 10:02:29

1043 Is It a Binary Search Tree

#include<iostream> #include<vector> using namespace std; bool isMirror; vector<int>pre,post; void getpost(int root,int tail){if(root>tail)return;int iroot1,jtail;//按普通情况处理if(!isMirror){//左子树while(i<tail&&pre[i]<…

作者头像 李华