登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

MySQL BLACKHOLE 存储引擎适合什么复制中转场景

来源:17golang原创

时间:2026-11-11 04:10:36 484浏览 收藏

如果你把 MySQL BLACKHOLE 当成普通业务表,最容易得到两个错误结论:一是以为插入成功就能再查出来,二是以为没有表数据就不会参与复制。它真正适合的是“让一个中转 mysqld 进程接收并过滤复制事件,再把新的二进制日志交给下游副本”的架构。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/blackhole-storage-engine.html

BLACKHOLE 表本身不落盘,读取总是空结果;但在启用 statement-based binary logging 时,写入语句仍可以进入二进制日志并复制到下游。因此它更像复制链路中的“无数据中转层”,而不是缓存、归档表或业务存储。下面按实际决策顺序说明它适合什么场景,以及上线前需要避开的边界。

先确认 BLACKHOLE 的读写语义

先把表的两个事实分开:表数据不保存,复制日志可以保存事件。官方文档给出的行为是,INSERT 能返回成功,但随后执行 SELECT 不会得到刚才写入的行。这个特征正是它能做中转节点的原因,也是它不能替代 InnoDB 的原因。

-- 创建一个只保留表定义、不保留行数据的表
CREATE TABLE relay_events (
    event_id BIGINT NOT NULL,
    payload  VARBINARY(1024) NOT NULL,
    PRIMARY KEY (event_id)
) ENGINE = BLACKHOLE;

-- 写入语句可以成功,但 BLACKHOLE 不保存行
INSERT INTO relay_events(event_id, payload)
VALUES (1001, X'010203');

-- 查询不会返回上面插入的记录
SELECT event_id, payload
FROM relay_events;

这里的“成功”只表示服务器接受了写入操作,不表示数据已经落在某个可查询的数据页中。即使表定义里声明了索引,也不能把索引当作可供查询的持久化数据结构。需要重放、查询、补偿或审计原始行时,应使用真正保存数据的引擎。

BLACKHOLE 表的 INSERT、SELECT 和 binary log 静态关系说明图
图1:BLACKHOLE 写入、读取与日志语义说明图,区分表数据不保存和复制日志可继续传播;这是静态说明图,不是运行截图。

把 BLACKHOLE 放进复制过滤链路

最典型的架构不是“业务应用直接连 BLACKHOLE”,而是在复制源旁边运行一个 dummy mysqld。源服务器先写自己的二进制日志,dummy mysqld 作为副本接收事件,再依据 replicate-do-* 或 replicate-ignore-* 规则应用需要的过滤,最后由这个中转进程写出新的二进制日志,供下游副本继续复制。

-- 下面是中转实例上的过滤意图示例;实际参数应写入该实例的启动配置
-- 只让指定业务库进入中转实例的复制链路
replicate-do-db=orders

-- 对不需要下发的表进行过滤,避免把无关事件交给下游
replicate-ignore-table=orders.tmp_import

-- 中转实例上的业务表使用 BLACKHOLE,表定义仍需与复制规则匹配
CREATE TABLE orders.order_events (
    id BIGINT NOT NULL,
    event_type VARCHAR(64) NOT NULL,
    created_at TIMESTAMP NOT NULL
) ENGINE = BLACKHOLE;

这个方案的价值是把过滤动作提前到靠近源端的位置:下游副本不必先接收所有二进制日志再丢弃,多个副本还可以复用同一个过滤后的日志出口。BLACKHOLE 负责“不保存表行”,dummy mysqld 负责“接收、过滤并重新产生日志”,两者不要混为一个概念。

源 MySQL、BLACKHOLE 中转节点和下游副本的复制过滤架构说明图
图2:BLACKHOLE 复制中转架构说明图,强调过滤规则位于中转 mysqld,连线表达静态架构关系,不是运行截图。

哪些复制中转场景适合使用

可以用下面的判断表快速缩小范围:

场景是否适合原因
源端先过滤,再给多个下游副本适合中转实例可以生成过滤后的二进制日志,减少下游接收无关事件。
只测量二进制日志和复制开销适合存储引擎不落盘,便于把存储层开销从测量目标中剥离。
临时验证 dump 文件中的 SQL 语法适合可以接受写入而不保留数据,适合做语句层面的检查。
需要查询、补偿或恢复业务数据不适合写入不会留下可读取的行,无法承担数据恢复职责。
依赖 UPDATE/DELETE 行触发器不适合BLACKHOLE 没有实际行,UPDATE 和 DELETE 的行级触发器不会按普通表工作。

所以,标题中的“中转”必须指复制日志的中转,而不是业务数据的中转。若下游需要完整的行数据,应该让过滤后的事件落到合适的持久化引擎,再由下游消费。

上线前要避开的复制边界

自增列不能按普通表理解

BLACKHOLE 是 no-op 引擎,不会自动维护自增值,也不保留自增状态。如果源端的 BLACKHOLE 表依赖自增主键,而下游表使用 MyISAM 等实际存储引擎,且 INSERT 没有显式设置自增值,复制可能因为重复主键而失败。中转表最好使用业务侧明确生成的主键,或者在复制协议中明确处理 INSERT_ID,不要把自增状态交给 BLACKHOLE。

-- 中转表显式接收业务生成的唯一 ID,避免依赖 BLACKHOLE 的自增状态
CREATE TABLE relay_events (
    event_id BIGINT NOT NULL,
    body     VARBINARY(2048) NOT NULL,
    PRIMARY KEY (event_id)
) ENGINE = BLACKHOLE;

-- 写入时由上游明确提供 ID,复制两端看到的键保持一致
INSERT INTO relay_events(event_id, body)
VALUES (9000001, X'0A0B0C');

触发器和表结构差异要单独评估

BLACKHOLE 表上的 INSERT 触发器可以按预期工作,但 UPDATE 和 DELETE 触发器依赖实际行,不能按普通 InnoDB 表推断。使用 row-based replication 时,列过滤也有明确边界:源端可以先准备公开列和敏感列,中转或可信副本使用不同的表定义配合过滤规则。这个设计需要把表结构、binlog_format 和复制选项一起审阅,不能只改 ENGINE。

一份可执行的落地清单

  1. 先执行 SHOW ENGINES,确认当前实例的 BLACKHOLE 状态可用,再决定是否部署中转实例。
  2. 把中转实例和业务源实例分开看:前者负责复制接收、过滤和日志输出,后者负责真实业务数据。
  3. 逐张核对中转表定义,尤其是主键、自增列、触发器、列顺序和下游表引擎。
  4. 明确复制格式与过滤参数,记录哪些库表会进入中转日志,哪些事件会在源端被排除。
  5. 把“查询为空”写进运维说明和告警判断,避免值班人员把它误判成复制延迟或数据丢失。
-- 上线前快速确认存储引擎是否被当前实例启用
SHOW ENGINES;

-- 只检查表定义,不把空查询误认为表不可用
SHOW CREATE TABLE relay_events;

最终可以这样做选择:如果目标是靠近源端过滤复制事件、减少下游无关日志,BLACKHOLE 适合做轻量中转;如果目标是保存、查询或恢复数据,就应选择真正持久化的存储引擎。它的优势来自“不保存”,风险也恰恰来自“不保存”。

相关问题

BLACKHOLE 表为什么 INSERT 成功后 SELECT 为空?

因为它接受写入后直接丢弃行数据,查询没有可读取的记录;成功不等于保存。

BLACKHOLE 能不能当临时缓存表?

不能。它没有可供后续读取的行,也不保留自增状态,不具备缓存表的基本语义。

为什么 BLACKHOLE 还能参与复制?

在适用的二进制日志配置下,写入语句仍可以进入 binary log;复制链路传播的是日志事件,不是 BLACKHOLE 表里已经保存的行。

复制中转表应该使用自增主键吗?

不建议把主键生成依赖 BLACKHOLE 的自增状态。更稳妥的方式是由上游明确生成并写入唯一 ID,再让复制两端保持一致。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>