标签 binlog 下的文章

摘要:
爱奇艺卡券业务原采用 “MySQL 分库分表 + ES 异步同步” 架构,面临 TP/AP 分离导致的架构复杂、AP 查询分钟级延迟、数据一致性隐患等问题。如今借助 OceanBase 的 HTAP 能力,将 AP、TP 业务融合到一个数据库,在架构简化、成本控制与效率提升方面均取得了突破。

爱奇艺是国内知名的在线视频平台,每年都会推出上百部优质长视频内容,其中不乏如 2023 年现象级爆款《狂飙》这样的佳作。近两年,随着短视频、微剧的兴起,平台年处理视频内容数量级从上百部直接跃升至上万部。

每一部内容从立项、拍摄、生产、制作到上线播出,均需经过复杂流程,并依赖上百个业务模块协同支持。卡券业务是出于整个业务生态里面的中台位置,对上提供中台能力,如为创新型业务会员、云影院提供商业化变现和用户转化营销工具。

过去,卡券业务系统将 AP(分析处理)与 TP(事务处理)业务分开处理,架构复杂,需要较高的维护成本;如今,借助 OceanBase 的 HTAP 能力,将 AP、TP 业务融合到一个数据库,在架构简化、成本控制与效率提升方面均取得了突破。

解构卡券业务的数据架构困境

卡券是爱奇艺核心的营销与促销工具,贯穿爱奇艺的会员购买、云影院观影等商业化变现全链路。其底层数据库的性能直接关乎用户体验与业务敏捷性。

例如,促销发券、会员领券等场景均需要业务系统提供高并发事务处理(TP)能力。而在运营侧,则需要实时统计和分析发券数量、会员领券等指标,以便评估活动效果、优化营销策略,这依赖于高效的数据分析(AP)能力。

过去,卡券业务系统采用的是“MySQL 分库分表+ES 异步同步”的复杂架构:分库分表的 MySQL 来承载 TP 业务,以应对高并发海量请求;Elasticsearch(ES)来完成统计分析等 AP 类业务。

“这个卡券业务的架构基本上能为业务需求提供足够的支撑。不过,我们并不满意,一直在寻找新的解决方案。”爱奇艺高级总监张冲表示。

其最重要的原因就在于系统架构过于复杂,虽能满足业务需求,但每一个节点都需要研发投入大量精力维护,顶峰的时候研发资源占比近 80%,严重挤占了业务创新资源。

具体而言,在 TP 业务方面,通过分库分表的 MySQL 集群支撑高并发交易,但实际日常资源利用率仅约 10%,资源冗余明显。在 AP 业务方面,ES 进行运营统计分析时的数据源自订阅多个 MySQL 实例的 binlog,经消息队列 RMQ 异步同步至 ES,链路冗长,存在分钟级延迟。并且 ES 的清理归档代价较高,Reindex 开销也比较大。

此外,数据的一致性与准确性面临挑战,在异步同步过程中,甚至出现过 UV(访客数量)超过页面点击的异常,统计准确率难以保障。

张冲坦言,对现有架构进行升级更深层的原因,源于对技术进步的持续追求。“我们希望技术上再往前走一走,要和互联网行业最先进的技术保持对齐。”

从“分库分表+ES”到“单库双擎”,爱奇艺 HTAP 架构升级实践

随着新技术趋势的出现,爱奇艺也开始寻找能够简化架构、支撑业务更好发展的新发展。数据库的升级是这次架构升级的关键。

对于新一代数据库,爱奇艺提出了明确的要求:

第一,必须是一款兼顾 TP、AP,具备 HTAP 能力的数据库产品,无需管 MQ,无需处理异构的数据,尽量减少对数据平台的依赖,以简化数据底座;

第二,总体成本可控,和现有架构相比成本不能上升,符合公司降本增效的目标;

第三,云中立,在遇到故障的时候可以实现云逃逸,且在不同的云上均可提供一致性的服务。

根据上述三个基本要求,经过对多款主流数据库产品的调研与测试,OB Cloud 一体化云数据库凭借其卓越的性能与高度契合的需求满足度,赢得了爱奇艺的青睐,并且在高并发、高可用、安全、数据治理、低成本等方面的技术积累,都被浓缩到 OB Cloud 一体化云数据库的产品中。

张冲表示:“OceanBase 不仅提供了真正的 HTAP 融合能力,其原生分布式架构还与我们的云原生战略高度契合。同时,OB Cloud 在百度云上开服也是一个重要契机,因为爱奇艺的系统平台就部署在百度云上。”

完成数据库选型之后,爱奇艺迅速开始了数据和架构升级的准备工作。

升级工作分为两个阶段:

AP 升级:将 ES 集群中的百亿文档升级至 OB Cloud 集群。通过双写、迁移历史数据、切读、停双写等步骤,不仅完成数据升级,还从业务层面进行了逻辑去冗余和简化。最终,资源成本下降 60%,运营查询类 SQL 基本在 1 秒内返回。

TP 升级:将 16 个物理机数据库从原生 MySQL 分库分表形态升级至 OB Cloud 集群。借助 OceanBase 的 OMS 同步工具,顺利完成海量数据同步与校验工作。最终,存储成本下降 80%,且系统具备弹性伸缩能力,无需为大促提前预备资源。

张冲表示,为了尽量减少对业务的干扰,保持业务稳定性,升级过程尽可能少地修改代码,他们采取了一些措施:

  1. 汇聚到 OceanBase 的分区表、分区键与原来的分片逻辑一致,使得业务系统零改造切换;
  2. 保持 AP 业务不变,仅修改数据源订阅,通过全兼容的 binlog 直接订阅到同租户的 AP 表。此时还是多份存储,但依靠高压缩比,整体存储成本没有上升;
  3. 将 TP 业务的表异步修改为行列混存,不影响业务稳定性,同时运营统计只需简单修改库和表名即可快速上线。通过多副本读写分离,最终实现了单库双擎、支撑实时在线业务与数据分析的简洁架构。

化繁为简,打造卡券业务的现代化数据库底座

经过升级改造后,卡券业务系统架构变得非常简洁,只有基本的业务服务和数据中台的数据交互,不再需要维护额外的数据流服务,也无需担心存储不足等问题,归档清理的周期也相应延长,研发人员得以更加聚焦于业务需求的开发。

张冲表示,卡券业务系统架构升级带来了如下好处:

首先,链路极大简化。 去除了 MySQL 到 ES 的异步同步链路,消除了 ES 集群的运维与成本;张冲特别感慨此次架构的升级带来的简化,他表示“简单到只有计算、只有存储,简单到有点像互联网刚开始发展的那个阶段。”

其次,分析效率提升。 常规 AP 查询可直接在 OB Cloud 中完成,时间也从原来的准实时变成了实时,所有统计 SQL 的响应时间(RT)均小于 1 秒,性能大幅提升。而 BI 类需求以前需要数据中台部门支持,属于跨部门协作,最快也需数天;现在本部门内部就能完成,时间缩短为 2-3 天;

第三,存储成本显著节省。 借助 OceanBase 的高压缩能力,相比 MySQL 的存储成本下降了 80%,并且它可以弹性伸缩,不再需要提前为大促预备过量资源。

“相对于之前的架构,现在的架构非常简洁。这要得益于 OceanBase 把高并发、高可用、安全、数据治理、低成本等各种技术积累都浓缩到了这个数据库产品中。”张冲这样评价。

小结

张冲表示,未来爱奇艺计划将 OceanBase 的实践推广至更多在线交易型业务,如订单支付、会员中心等,并逐步探索其在 KV 存储、向量检索等场景的应用。

面对 AI 浪潮,张冲也提出了对未来数据库的期待:“知识图谱、AI 工作流等复杂场景,需要更智能的底层数据支撑。希望 OceanBase 能在这些方向持续演进,成为企业智能化转型的数据基石。借助 OceanBase 技术的不断完善和应用场景的拓展,爱奇艺将在科技创新的道路上走得更远。”

欢迎访问 OceanBase 官网获取更多信息:https://www.oceanbase.com/

在 MySQL CDC 任务中,很多用户都会遇到这样的问题:任务失败后该从哪里恢复?只知道一个时间点,却拿不到对应的 binlog 位点怎么办?Apache SeaTunnel 2.3.12 通过引入按时间启动(Timestamp Startup)功能,给出了更直观的答案。

本文围绕该能力的设计背景、配置方式与实现机制展开解析,帮助读者理解如何基于时间语义更高效地进行 CDC 任务恢复与数据回溯。

功能概述

Problem:CDC 启动点配置“技术正确,但使用困难”

在 Apache SeaTunnel 2.3.12 之前,MySQL CDC 连接器主要支持从指定 binlog 位点(file + position)或 GTID 启动数据同步任务。这种方式在实现上是精确且可靠的,但在真实生产与运维场景中,往往并不符合用户的使用习惯。

在实际 CDC 运维过程中,用户更容易掌握的是 “时间”,而非底层 binlog 细节,例如:

  • 任务异常中断后,希望从
    “2024-04-01 10:00:00” 之后继续同步
  • 对某一时间窗口的数据进行回溯或补采
  • 只知道“昨天 08:00 之后的变更需要重新同步”,但无法定位对应的 binlog 文件和偏移量

如果仍要求用户手动将时间反推为 binlog 位点,不仅配置复杂,而且极易出错,也显著增加了运维成本。这种“技术友好、但用户不友好”的启动方式,已经成为 CDC 任务恢复和回溯场景中的常见痛点。

Solution:引入按时间启动

为解决上述问题,Apache SeaTunnel 在 2.3.12 版本中为 MySQL CDC 连接器引入了按时间启动功能

该功能允许用户直接指定一个 Unix 时间戳(毫秒级) 作为同步起始点。MySQL CDC 连接器会在启动阶段自动完成以下工作:

  1. 根据指定时间戳定位对应的 binlog 文件与偏移量
  2. 从该 binlog 位置开始读取变更事件
  3. 自动跳过所有早于该时间点的历史事件

通过引入“时间”这一更符合业务语义的维度,SeaTunnel 将 CDC 启动方式从面向底层 binlog 细节,提升为面向业务时间语义,显著降低了 CDC 任务在恢复、回溯和运维场景下的使用门槛。

配置参数

要启用按时间启动功能,需要配置以下两个关键参数:

参数名类型必填说明
startup.modeEnum设置为 "timestamp" 启用时间模式 2
startup.timestampLongUnix 时间戳(毫秒),指定启动时间点 3

配置示例

env {
  parallelism = 1
  job.mode = "STREAMING"
  checkpoint.interval = 10000
}

source {
  MySQL-CDC {
    url = "jdbc:mysql://localhost:3306/testdb"
    username = "root"
    password = "root@123"
    table-names = ["testdb.table1"]
    
    # 启用按时间启动
    startup.mode = "timestamp"
    startup.timestamp = 1672531200000  # 2023-01-01 00:00:00 UTC
  }
}

sink {
  Console {
  }
}

技术实现

启动模式枚举

MySqlSourceOptions 类中定义了所有支持的启动模式,包括新增的 TIMESTAMP 模式:

public static final SingleChoiceOption<StartupMode> STARTUP_MODE =
    (SingleChoiceOption)
        Options.key(SourceOptions.STARTUP_MODE_KEY)
            .singleChoice(
                StartupMode.class,
                Arrays.asList(
                    StartupMode.INITIAL,
                    StartupMode.EARLIEST,
                    StartupMode.LATEST,
                    StartupMode.SPECIFIC,
                    StartupMode.TIMESTAMP))

时间戳过滤实现

核心实现在 MySqlBinlogFetchTask 类中,当检测到启动模式为 TIMESTAMP 时,会使用 TimestampFilterMySqlStreamingChangeEventSource 来处理 binlog 事件:

StartupMode startupMode = startupConfig.getStartupMode();
if (startupMode.equals(StartupMode.TIMESTAMP)) {
    log.info(
        "Starting MySQL binlog reader,with timestamp filter {}",
        startupConfig.getTimestamp());

    mySqlStreamingChangeEventSource =
        new TimestampFilterMySqlStreamingChangeEventSource(
            sourceFetchContext.getDbzConnectorConfig(),
            sourceFetchContext.getConnection(),
            sourceFetchContext.getDispatcher(),
            sourceFetchContext.getErrorHandler(),
            Clock.SYSTEM,
            sourceFetchContext.getTaskContext(),
            sourceFetchContext.getStreamingChangeEventSourceMetrics(),
            startupConfig.getTimestamp());
}

偏移量计算

MySqlSourceFetchTaskContext 中实现了根据时间戳查找对应 binlog 偏移量的逻辑:

private Offset getInitOffset(SourceSplitBase mySqlSplit) {
    StartupMode startupMode = getSourceConfig().getStartupConfig().getStartupMode();
    if (startupMode.equals(StartupMode.TIMESTAMP)) {
        long timestamp = getSourceConfig().getStartupConfig().getTimestamp();
        try (JdbcConnection jdbcConnection =
                getDataSourceDialect().openJdbcConnection(getSourceConfig())) {
            return findBinlogOffsetBytimestamp(jdbcConnection, binaryLogClient, timestamp);
        } catch (Exception e) {
            throw new SeaTunnelException(e);
        }
    } else {
        return mySqlSplit.asIncrementalSplit().getStartupOffset();
    }
}

启动模式对比与适用场景

为了更好地理解按时间启动功能在整体 CDC 启动体系中的定位,下面对 MySQL CDC 当前支持的几种启动模式进行对比说明:

启动模式启动依据优点适用场景
INITIAL全量 + 当前 binlog一次性完成历史与增量同步首次接入数据源
EARLIEST最早可用 binlog不依赖具体位点binlog 保存周期较长的场景
LATEST当前最新 binlog启动快仅关注未来增量数据
SPECIFIC指定 binlog file + position精确可控已明确掌握 binlog 位点的场景
TIMESTAMP指定时间戳(毫秒)配置直观、符合业务语义任务恢复、数据回溯、按时间窗口同步

可以看到,TIMESTAMP 模式并不是替代 SPECIFIC 或 GTID 的“更底层”方案,而是为了解决“用户只知道时间、不知道 binlog”的典型问题,是一种以可用性和运维友好性为核心的补充能力

测试验证

该功能在集成测试中得到了充分验证,测试用例 MysqlCDCSpecificStartingOffsetIT 验证了按时间戳启动的正确性 7

使用注意事项

  1. 版本要求:需要 SeaTunnel 2.3.12 或更高版本
  2. 时间戳格式:必须使用 Unix 时间戳,单位为毫秒
  3. binlog 可用性:确保指定时间点对应的 binlog 文件仍然可用
  4. 时区考虑:时间戳基于 UTC 时区,需要注意时区转换

总结

SeaTunnel MySQL CDC 的按时间启动功能为数据同步提供了更精确的控制能力,特别适用于需要从特定时间点恢复数据同步的场景。该功能通过时间戳到 binlog 偏移量的转换,实现了高效的时间点定位和数据过滤。

Notes

  • 该功能在工厂类 MySqlIncrementalSourceFactory 中通过条件配置规则进行参数验证
  • 除了 MySQL CDC,其他 CDC 连接器如 SQL Server CDC 也支持类似的时间戳启动功能

关于 gh-ost

gh-ost 是 GitHub 开发的一个 MySQL 在线表结构变更工具(online schema migration tool)。它的全称是 "GitHub's Online Schema Translator"。

gh-ost 现在已经是大型互联网公司进行数据库运维的重要工具。

主要作用

gh-ost 允许在不锁表、不影响业务的情况下,对 MySQL 数据库表进行结构变更(如添加列、修改索引等)。

核心特点

  1. 无触发器设计 - 不像传统工具使用触发器来同步数据,gh-ost 通过解析 binlog 来捕获变更
  2. 可暂停/恢复 - 可以随时暂停迁移过程,对生产环境更友好
  3. 可测试 - 支持在从库上测试变更,确认无误后再应用到主库
  4. 动态调整 - 可以实时调整迁移速度,避免影响线上服务

工作原理

  1. 创建一个与原表结构相同的"影子表"(ghost table)
  2. 在影子表上执行 DDL 变更
  3. 通过 binlog 将原表的增量数据同步到影子表
  4. 数据同步完成后,快速切换表名完成迁移

使用方法

  1. 安装
    gh-ost 可以直接从最新的 发布页面 下载二进制文件,支持 Linux 和 macOS。
  2. 基本命令

    • 测试迁移

      gh-ost --test-on-replica --database=mydb --table=mytable --alter="ADD COLUMN new_col INT" --execute
    • 真实迁移

      gh-ost --database=mydb --table=mytable --alter="ADD COLUMN new_col INT" --execute

实际例子

假设你有一个用户表需要添加新字段:

gh-ost \
  --host=localhost \
  --user=root \
  --password=password \
  --database=mydb \
  --table=users \
  --alter="ADD COLUMN age INT DEFAULT 0" \
  --execute

场景说明:

  • 原表 users 有 1000 万条数据
  • 使用传统 ALTER TABLE 可能需要锁表数小时
  • 使用 gh-ost 可以在后台逐步完成变更,期间用户可以正常读写数据
  • 最后只需要几秒钟的短暂切换时间即可完成迁移

适用场景

  • 大表的结构变更(百万级以上数据)
  • 需要保证高可用性的生产环境
  • 需要精确控制数据库负载的情况

数据库支持范围

gh-ost 目前只适用于 MySQL(包括 Percona Server 和 MariaDB)。它依赖 MySQL 的 binlog 机制,因此不支持 PostgreSQL、Oracle 等其他数据库。

常见的坑

1. 外键约束问题

gh-ost 不支持有外键的表。如果表有外键关系,迁移会失败。

解决办法: 需要先删除外键,迁移完成后再重新添加

2. binlog 格式要求

必须使用 ROW 格式的 binlog,STATEMENT 或 MIXED 格式不支持。

-- 检查 binlog 格式
SHOW VARIABLES LIKE 'binlog_format';

-- 如果不是 ROW,需要修改配置
SET GLOBAL binlog_format = 'ROW';

3. 主键要求

必须有主键或唯一索引,否则 gh-ost 无法正常工作。

4. 磁盘空间

会创建影子表,需要额外的磁盘空间(大约是原表的大小)。如果磁盘空间不足,迁移会失败。

5. 复制延迟

如果主从复制本身就有延迟,gh-ost 的迁移会进一步加重延迟。需要监控 --max-lag-millis 参数。

6. 触发器冲突

虽然 gh-ost 本身不用触发器,但如果原表上已有触发器,可能会导致数据不一致。

7. 字符集问题

影子表的字符集需要与原表一致,否则可能出现乱码或数据截断。

8. 长时间迁移中断

如果迁移过程很长(几天),期间 MySQL 重启或 binlog 被清理,会导致迁移失败需要重新开始。

实践建议

# 先在从库测试
gh-ost \
  --host=slave-host \
  --test-on-replica \
  --migrate-on-replica \
  --database=mydb \
  --table=users \
  --alter="ADD COLUMN age INT" \
  --execute

# 设置合理的限流参数
gh-ost \
  --max-load=Threads_running=25 \
  --critical-load=Threads_running=100 \
  --chunk-size=1000 \
  --throttle-query="SELECT HOUR(NOW()) BETWEEN 2 AND 6" \
  --execute

替代方案

如果 gh-ost 不适用,可以考虑:

  • pt-online-schema-change (Percona Toolkit)
  • 原生 Online DDL (MySQL 5.6+ 支持部分操作)
  • 对于其他数据库,PostgreSQL 可以用 pg_repack