前言
MySQL 是全球知名的开源关系型数据库,一直以来因其稳定性、可靠性和易用性而广受欢迎。一个重要的特性,也是 MySQL 受到高度关注的理由,是其较早期推出的二进制日志功能 —— binlog。
MySQL binlog,即二进制日志,是 MySQL 中用于记录数据库更改的日志文件。自 MySQL 数据库较早地引入了 binlog,这项功能就为它赢得了开发者和企业用户的青睐。MySQL 的二进制日志以高效、易读的二进制格式存储了所有影响数据库状态变化的 SQL 语句,从而为数据库提供了数据集成、复制等多种强大的能力。多年积累下来,MySQL 生态也基于 binlog 逻辑复制能力做出了一些比较成熟的增量解析系统并被广泛使用在数据集成中,比如 Canal、Debezium 等。
OceanBase 数据库早期就推出了 MySQL 兼容模式,使得一些希望替换 MySQL 的用户能够以较小的成本切换到 OceanBase 数据库。考虑到 MySQL 生态系统中一些成熟的 binlog 增量解析系统已经被广泛使用,当用户从 MySQL 转向 OceanBase 数据库时,可以直接利用现有的 binlog 增量解析系统,这也加快了 OceanBase Binlog Service 的诞生。
OceanBase Binlog Service 提供了类似于 MySQL 的 binlog 功能,可以记录数据库的变更日志。这对于实现数据迁移、同步以及数据一致性检查至关重要。该服务使得 MySQL 用户能够利用熟悉的 binlog 方案来监控、备份,并通过数据复制保持 OceanBase 数据库实时更新。
MySQL binlog 逻辑复制架构
MySQL 复制机制基于 binlog 来实现,具体来说,binlog 主要在 SQL 引擎层记录数据的逻辑更改,而非在事务引擎层通过 redo log 进行物理更改的记录。这样的设计思路背后有着明确的目的。
MySQL 旨在支持多种存储引擎,如 InnoDB、MyISAM 等,每种存储引擎都有其独特的特征和优势。通过在 SQL 引擎层以 binlog 形式记录数据变更,MySQL 实现了一种灵活的复制机制,它不仅支持多种存储引擎间的无缝数据复制,还允许不同存储引擎之间进行异构同步复制。换句话说,这种机制为数据的一致性和兼容性带来了极大的便利。
这种设计展现了其在保证数据复制灵活性和系统兼容性方面的优势。通过逻辑而非物理记录数据变更,MySQL 成功地整合了多样化的存储引擎特性,并在全球范围内被广泛应用于需要高度兼容性和灵活性的复制架构中。
另外,在大数据领域,MySQL binlog 起着至关重要的作用,因为它提供一种机制来实现数据变更捕获(Change Data Capture,CDC),这是数据处理和分析的关键组成部分。
以下是 binlog 在大数据环境中的重要作用。
- 数据同步:binlog 允许持续地监控和记录 MySQL 数据库中的所有数据更改,包括插入、更新和删除操作。这些实时数据流可以被大数据工具捕获并同步到数据湖、数据仓库或其它任何大数据处理系统中,以进行进一步分析。
- 实时分析:通过从 binlog 中捕获实时变化,企业可以将这些数据推送到流处理引擎(如 Apache Kafka、Apache Flink 或 Apache Storm)进行实时分析,从而实现即时的业务洞察和决策。
- 跨平台数据整合:在多种数据库和存储系统中维护数据的一致性是大数据环境中的一个挑战。binlog 通过CDC 工具,如 Canal 或 Debezium,将数据无缝地同步到 Hadoop、Apache Hive、Elasticsearch 或任何其他目标平台,解决了数据整合的需求。
- 系统解耦:binlog 允许基于事件的数据架构设计,这意味着数据生产者(数据库)和消费者(如大数据处理服务)可以松耦合、独立扩展,既增加了系统的灵活性,也提高了系统的稳定性。
- 历史数据审计和回溯:binlog 为审计过去的数据变更提供了机会,分析历史数据走势和模式对于预测分析以及审计跟踪都是必要的。
MySQL binlog 特点
MySQL 在处理事务型存储引擎和非事务型存储引擎时 binlog 的行为实际是稍有些差异的,本文仅会讨论事务型存储引擎 InnoDB 的相关 binlog 行为。一是因为 MySQL 虽然支持多种存储引擎,但目前默认的存储引擎以及普遍被用户使用的都是 InnoDB 存储引擎;二是因为 OceanBase 数据库本身支持事务,在设计 OceanBase Binlog Service 兼容 MySQL binlog 时,也需要按 InnoDB 存储引擎的 binlog 行为来进行处理。
MySQL binlog 文件支持两种格式:Statement-Based Replication(SBR)和 Row-Based Replication(RBR)。SBR 格式记录了 SQL 语句本身及其相关上下文信息,其优势在于较低的磁盘空间需求。然而在特定情况下,SBR 可能导致复制的同步问题。相对地,RBR 格式记录了数据变更前后的具体值,无需附加会话上下文信息,避免了 SBR 所面临的问题,并且确保了复制的一致性。
针对如 Canal/Debezium 这类的 binlog 解析系统,仅支持 RBR 格式,考虑到 Canal/Debezium 等系统的兼容性及其在实现数据同步与集成方面的广泛应用,本文将专注于 RBR 格式下的 binlog,目的是实现对 Canal/Debezium 等增量解析系统的全面支持。
MySQL 的 binlog 由两组文件组成:
- 一组是 binlog 文件本身,由 1 至多个编号连续的 binlog 文件组成,如上图中的 mysql-bin.000001、mysql-bin.000002、mysql-bin.000003 等。
- 另一组是 binlog 的索引文件,即上图中的 mysql-bin.index 。该文件仅仅是一个文本文件,记录了当前所有的 binlog 文件名,
SHOW BINARY LOGS
命令即是通过读取该索引文件而实现。
MySQL 的 binlog 文件是由不同类型的 event 组成,在 5.7 版本中约支持 40 种 event 类型。从 CDC 增量解析的角度来看,主要需关注 Format_desc、Previous_gtids、Gtid, Rotate、Query、Xid、Table_map、Write_rows、Update_rows、Delete_rows 等 event 类型,从事务的角度来说,一个事务的所有相关 binlog events 在 binlog 文件中都是连续的,即不同事务间的 events 不会有交叉,此外一个事务的所有 binlog events 也不会跨 binlog 文件。以下是通过SHOW BINLOG EVENTS
列出的 binlog events 示例:
mysql> SHOW BINLOG EVENTS IN 'mysql-bin.000009';
+------------------+------+----------------+------------+-------------+--------------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+------+----------------+------------+-------------+--------------------------------------------------------------------+
| mysql-bin.000009 | 4 | Format_desc | 1147473732 | 123 | Server ver: 5.7.35-log, Binlog ver: 4 |
| mysql-bin.000009 | 123 | Previous_gtids | 1147473732 | 194 | ebd2d3b0-6399-11ec-86ea-0242ac110004:1-38 |
| mysql-bin.000009 | 194 | Gtid | 1147473732 | 259 | SET @@SESSION.GTID_NEXT= 'ebd2d3b0-6399-11ec-86ea-0242ac110004:39' |
| mysql-bin.000009 | 259 | Query | 1147473732 | 353 | create database test |
| mysql-bin.000009 | 353 | Gtid | 1147473732 | 418 | SET @@SESSION.GTID_NEXT= 'ebd2d3b0-6399-11ec-86ea-0242ac110004:40' |
| mysql-bin.000009 | 418 | Query | 1147473732 | 543 | use `test`; CREATE TABLE t1(id int primary key, v varchar(30)) |
| mysql-bin.000009 | 543 | Gtid | 1147473732 | 608 | SET @@SESSION.GTID_NEXT= 'ebd2d3b0-6399-11ec-86ea-0242ac110004:41' |
| mysql-bin.000009 | 608 | Query | 1147473732 | 733 | use `test`; CREATE TABLE t2(id int primary key, v varchar(30)) |
| mysql-bin.000009 | 733 | Gtid | 1147473732 | 798 | SET @@SESSION.GTID_NEXT= 'ebd2d3b0-6399-11ec-86ea-0242ac110004:42' |
| mysql-bin.000009 | 798 | Query | 1147473732 | 870 | BEGIN |
| mysql-bin.000009 | 870 | Table_map | 1147473732 | 918 | table_id: 114 (test.t1) |
| mysql-bin.000009 | 918 | Write_rows | 1147473732 | 963 | table_id: 114 flags: STMT_END_F |
| mysql-bin.000009 | 963 | Table_map | 1147473732 | 1011 | table_id: 115 (test.t2) |
| mysql-bin.000009 | 1011 | Write_rows | 1147473732 | 1056 | table_id: 115 flags: STMT_END_F |
| mysql-bin.000009 | 1056 | Xid | 1147473732 | 1087 | COMMIT /* xid=57 */ |
| mysql-bin.000009 | 1087 | Gtid | 1147473732 | 1152 | SET @@SESSION.GTID_NEXT= 'ebd2d3b0-6399-11ec-86ea-0242ac110004:43' |
| mysql-bin.000009 | 1152 | Query | 1147473732 | 1224 | BEGIN |
| mysql-bin.000009 | 1224 | Table_map | 1147473732 | 1272 | table_id: 114 (test.t1) |
| mysql-bin.000009 | 1272 | Update_rows | 1147473732 | 1328 | table_id: 114 flags: STMT_END_F |
| mysql-bin.000009 | 1328 | Table_map | 1147473732 | 1376 | table_id: 115 (test.t2) |
| mysql-bin.000009 | 1376 | Update_rows | 1147473732 | 1432 | table_id: 115 flags: STMT_END_F |
| mysql-bin.000009 | 1432 | Xid | 1147473732 | 1463 | COMMIT /* xid=61 */ |
| mysql-bin.000009 | 1463 | Gtid | 1147473732 | 1528 | SET @@SESSION.GTID_NEXT= 'ebd2d3b0-6399-11ec-86ea-0242ac110004:44' |
| mysql-bin.000009 | 1528 | Query | 1147473732 | 1600 | BEGIN |
| mysql-bin.000009 | 1600 | Table_map | 1147473732 | 1648 | table_id: 114 (test.t1) |
| mysql-bin.000009 | 1648 | Delete_rows | 1147473732 | 1693 | table_id: 114 flags: STMT_END_F |
| mysql-bin.000009 | 1693 | Xid | 1147473732 | 1724 | COMMIT /* xid=67 */ |
| mysql-bin.000009 | 1724 | Rotate | 1147473732 | 1771 | mysql-bin.000010;pos=4 |
+------------------+------+----------------+------------+-------------+--------------------------------------------------------------------+
28 rows in set (0.00 sec)
Canal/Debezium 的解析原理
Canal/Debezium 都是数据变更捕获(Change Data Capture,简称 CDC)工具,它们的核心作用是监控数据库中的变更并将这些变更以某种方式传播出去。尽管它们为不同的数据库生态系统提供服务(Canal 主要针对 MySQL,而 Debezium 支持多种数据库),但是它们都利用了相似的原理来解析和传递数据变更。
Canal/Debezium 模仿了 MySQL slave 的通信过程,对 MySQL 的 binlog 二进制格式进行解析,提取出数据的增加、删除、更新这些变更,并转换为统一的格式。
- 建立连接:Canal/Debezium 连接到 MySQL 主服务器,模拟从服务器的行为。
- 请求 binlog:Canal/Debezium 请求 MySQL 发送 binlog,并指定从特定的位置开始。
- 解析 binlog:Canal/Debezium 从连接中读取 binlog 流并将其解析为可识别的数据变更事件。
- 转换数据:解析后的事件被转换为通用的消息格式,供后续的系统消费。
- 传播变更:变更数据可以被发送到各种中间件或直接被其他系统使用,如消息队列(Kafka 等),监控系统,缓存或搜索引擎。
Canal/Debezium 会将自己伪装成一个 slave,设置自己的server_id
和server_uuid
,再通过COM_BINLOG_DUMP
或COM_BINLOG_DUMP_GTID
协议来定位和拉取 binlog events。另外为了支持对 binlog 的解析,还要求binlog_format
和binlog_row_image
两个系统变量必须为 ROW 和 FULL。
Canal/Debezium 做 binlog 解析时,主要关注事务本身的 DML 操作及 DDL 操作,所以对 binlog event 类型处理上主要以以下几种 binlog event 类型为主。此外还需要对一些控制类型的 binlog event 类型进行解析处理,比如 Format_desc、Rotate、Previous_gtids、Gtid 等。
event 类型 | 说明 |
Query | 即可以表示一个事务的 BEGIN,也可以表示 DDL 操作。 |
Xid | 表示一个事务的 COMMIT。 |
Table_map | 在一个事务内,一条 DML SQL 会对一到多张表的数据变更,在进行 binlog 记录时,会把涉及到的每张表都产生一个 Table_map event 并将其写到 binlog 文件用于记录内部 table id 和对应的表名。Table_map events 会先于 DML 操作产生的所有 Write_rows/Update_rows/Delete_rows events 出现在该事务的 binlog evens 中。 |
Write_rows | INSERT 产生的 event 类型:一条 event 可以包含多行插入的记录,但都属于同一 table id。若一条 INSERT 插入了多行,会产生多条连续的该 event 类型。 |
Update_rows | UPDATE 产生的 event 类型:一条 event 可以包含多行记录的更改,但都属于同一 table id。若一条 UPDATE 更改了多行,会产生多条连续的该 event 类型。 |
Delete_rows | DELETE 产生的 event 类型:一条 event 可以包含多行记录的删除,但都属于同一 table id。若一条 DELETE 删除了多行,会产生多条连续的该 event 类型。 |
虽然 Write_rows/Update_rows/Delete_rows 中记录的变更的前、后的值,但其中记录的元信息部分并不包含对应表中的列名,所以 Canal/Debezium 在做 binlog 解析时,还需要在启动初始化时先查询 MySQL 的元信息获取到所有相关表的 schema 定义。另外,MySQL 提供 SQL 服务和 binlog 服务都是相同的地址和端口(默认为 3306 端口)。
OceanBase Binlog Service 兼容 MySQL binlog 技术架构
OceanBase Binlog Service 为兼容 MySQL binlog 而诞生,支持现有的 MySQL binlog 生态工具来同步 OceanBase,现有的 MySQL binlog 生态工具可以平滑切换至 OceanBase 数据库。
技术要点
OceanBase Binlog Service 整套主要包含三部分:OceanBase 数据库、OBProxy 和 OBLogProxy,其中 binlog 服务主要主体为 OBLogProxy。
整套系统的技术要点包括:
- 按租户级别创建 binlog 服务(OceanBase 数据库为多租户设计,其中每一个“租户”对应一个 MySQL 实例)。
- OceanBase 数据库 clog 经 obcdc 获取后,需要转换为 ROW 格式、 FULL image 的 binlog events, 然后按 binlog 文件的组织方式存储到 binlog 文件中,支持被多个下游 MySQL binlog 解析系统所共用、支持回拉等,同时支持被 MySQL binlog 工具分析等。
- 支持 MySQL 通信协议。
- 用于 binlog dump 的
COM_BINLOG_DUMP
或COM_BINLOG_DUMP_GTID
本身也是 MySQL 通信协议的一部分,OBProxy 和 OBLogProxy 需识别和处理这两个协议指令。 - Canal/Debezium 做 binlog 解析时。需查询 MySQL 的元信息获取相关表的 schema 定义,也需要检查 binlog 相关系统变量确认 binlog 格式是否符合预期。此外,Debezium 在进行 binlog dump 前,还支持全量基准数据的快照导出。这三类场景会涉及
SELECT
和SHOW
两类 SQL,这些 SQL 将通过 OBProxy 进行转发。 - MySQL 的 SQL 服务和 binlog 服务都是通过相同的地址进行的(默认为 3306 端口),而 OceanBase 数据库的 SQL 服务是通过 OBProxy 地址进行访问(默认为 2883 端口),为了使 OceanBase 数据库的 binlog 服务和 SQL 服务统一完全兼容 MySQL 的能力,需要 OBProxy 支持 binlog dump 协议并将请求转发给对应的 OBLogProxy 实例。
术语
此处列出相关名词含义,以便更好地理解本文。
- OceanBase 数据库:OceanBase 数据库集群。
- OBProxy:OceanBase 的访问代理,SQL 和 binlog 协议及相关命令通过 OBProxy 作为统一入口进行访问。
- OBLogProxy:OceanBase binlog 服务的主体。
- MySQL binlog 生态工具:泛指 MySQL binlog 增量解析系统,如常见的 Canal/Debezium 等。
- BC:OBLogProxy 中 binlog converter 子模块,拉取并解析 clog,再按照 binlog 格式转换为 binlog 文件。
- BD:OBLogProxy 中 binlog dumper 子模块,对下游(MySQL binlog 生态系统)的订阅请求提供 binlog 订阅服务。
- BCM:OBLogProxy 中 BC 的管理模块。
- BDM:OBLogProxy 中 BD 的管理模块。
系统架构
上图所示为 OceanBase Binlog Service 兼容 MySQL binlog 系统的整体技术架构。整体的交互流程如下:
- 需要为 OceanBase 数据库租户创建 binlog 服务:相比于 MySQL 的 binlog 服务,这一步骤是 OceanBase Binlog Service 额外需要的操作,由用户在需要时进行创建。
- 使用 MySQL Client 连接 OBLogProxy 后,发起
CREATE BINLOG
SQL 请求。 - OBLogProxy 收到
CREATE BINLOG
请求后,通过 BCM 创建 BC 子模块。 - BC 子模块初始化完成后,使用 MySQL Client 通过 OBProxy 执行 binlog 相关 SQL 可以查看 binlog 的状态,这类 SQL 包括如下几种。OBProxy 需识别这些 SQL 并转发给对应的 OBLogProxy 实例,由 BCM 返回相关结果集。
SHOW MASTER STATUS
SHOW BINARY LOGS
SHOW BINLOG EVENTS
- 使用 MySQL Client 通过 OBProxy 执行非 binlog 相关 SQL 查询时,由 OBProxy 直接查询对应 OceanBase 数据库实现。
- MySQL binlog 生态工具(如 Canal/Debezium 等)向 OBproxy 发起
COM_BINLOG_DUMP
或COM_BINLOG_DUMP_GTID
命令时,OBProxy 识别到这两个命令后,需将请求转发到对应 OBLogProxy 实例。OBLogProxy 收到COM_BINLOG_DUMP
或COM_BINLOG_DUMP_GTID
请求后,然后通过 BDM 创建一个 BD 子模块提供 binlog dump 服务。
尾声
OceanBase Binlog Service 的出现,是对现代数据库技术和 MySQL 生态兼容性的一次显著进步。随着数据库技术发展,用户需求的扩展,以及对于实时数据处理和分析的迫切需求,OceanBase Binlog Service 的推出不仅拓展了 MySQL 的功能,而且创造了一个适应分布式、多租户数据库环境的灵活解决方案。通过在现有的 MySQL binlog 生态中无缝地整合 OceanBase 数据库,用户能够在享受 OceanBase 数据库高性能、高扩展性、高可用性等优点的同时,还能够无缝地维持与现有基于 MySQL binlog 的应用和工具之间的兼容性,保持以往 MySQL binlog 的使用习惯。
技术角度来看,OceanBase Binlog Service 简化了数据变更捕捉和数据复制流程,使得数据迁移、同步、集成、审核和灾难恢复等流程更加平滑、高效。