MySQL 迁移到 Apache Doris 生产实践:从评估到落地的完整指南

一、背景与动机

很多公司在业务初期使用 MySQL 作为主数据库,随着数据量增长(单表从几百万到几亿),复杂的分析查询(GROUP BY、多表 JOIN)变得越来越慢,甚至影响在线事务。同时,MySQL 的主从复制在高并发下会出现延迟,故障切换也颇为头疼。

这个时候,很多团队会考虑 引入一个专门的 OLAP 数据库 来分担分析负载。Apache Doris 作为开源的 MPP 架构分析型数据库,凭借高性能、易用性、与 MySQL 协议的高度兼容,成为了许多企业替换或补充 MySQL 的优选。

那么,如何平滑地将 MySQL 中的大数据量分析场景迁移到 Doris?本文将从 场景评估、表设计、数据同步、改造切换、运维监控 五个方面,分享一套可直接落地的生产实践。

二、先搞清楚:Doris 不是要干掉 MySQL

在开始之前,必须明确一点:

MySQL 是 OLTP 数据库,负责高并发、强事务的在线业务;Doris 是 OLAP 数据库,负责海量数据的复杂分析查询。

二者不是替代关系,而是 互补关系 。生产中最常见的架构是:MySQL 处理写和点查,Doris 通过实时同步处理分析查询

特性 MySQL Apache Doris
数据模型 行存 列存
事务 完整ACID 不支持事务
查询类型 高并发点查、简单查询 复杂聚合、大表JOIN
数据量 TB级 PB级
写入方式 INSERT/UPDATE/DELETE 批量导入(Stream Load等)
索引 B+树、全文等 前缀索引、BloomFilter、倒排索引

适合迁移到 Doris 的场景:

  • 报表系统、BI大屏、用户画像分析、漏斗分析、订单明细统计。
  • 数据量大(千万级以上),且以追加写入批量更新为主。

不适合的场景:

  • 高频单条点查(如"根据主键查用户余额")。
  • 需要事务保证的写操作。
  • 非常频繁的 UPDATE/DELETE。

三、迁移前的评估:你的业务准备好了吗?

在动手写代码前,先问自己三个问题:

  1. 数据量有多大?

    如果 MySQL 核心分析表只有几百万行,而且查询还能接受,没有必要迁移。只有当单表达到 千万级甚至亿级,且查询明显变慢时,Doris 的价值才显现。

  2. 查询模式是什么?

    主要看是否包含 SUMCOUNTGROUP BY、多表 JOIN、窗口函数等。这些正是 Doris 的强项。

  3. 数据如何写入?

    • 如果现有的数据是 流式追加(如埋点日志、订单流),非常契合。
    • 如果频繁更新某些列(如订单状态),需要设计 Unique 模型 或采用 DELETE + INSERT 方式,也可以接受。

评估结论:确认要迁移后,就可以进入实战了。

四、生产实践:一步一步教你做

4.1 表结构设计(最关键的一步)

Doris 是列存,且支持三种数据模型:Duplicate (明细)、Aggregate (聚合)、Unique(唯一)。根据业务场景选择。

示例: 假设 MySQL 有一张用户行为表

sql 复制代码
-- MySQL 表
CREATE TABLE user_events (
    event_id BIGINT PRIMARY KEY,
    user_id INT,
    event_type VARCHAR(50),
    event_time DATETIME,
    properties JSON
);

迁移到 Doris 的建表语句:

sql 复制代码
-- Doris 明细模型,按天分区,按 user_id 分桶
CREATE TABLE doris_user_events (
    event_id BIGINT,
    user_id INT,
    event_type VARCHAR(50),
    event_time DATETIME,
    properties STRING   -- Doris 推荐用 STRING 存 JSON
) ENGINE = OLAP
DUPLICATE KEY(event_id, user_id)   -- 排序列,用于前缀索引
PARTITION BY RANGE(event_time) ()   -- 分区先不指定,后续动态添加
DISTRIBUTED BY HASH(user_id) BUCKETS 16
PROPERTIES (
    "replication_num" = "3",
    "bloom_filter_columns" = "user_id,event_type"
);

设计要点:

  • 分区:通常按时间(天/月)分区,方便数据生命周期管理,并利用分区裁剪加速查询。
  • 分桶:分桶键选择查询中经常用于过滤或 JOIN 的字段,单桶数据量建议 100MB~1GB。
  • 索引:对高基数列(user_id)建立 BloomFilter,可大幅提升点查性能。

4.2 数据迁移:全量 + 实时同步

生产环境需要平滑迁移,不能停业务。推荐 Flink CDC 方案。

步骤1:全量同步

使用 DataXmysqldump + Stream Load 将 MySQL 历史数据导入 Doris。

bash 复制代码
# 使用 Stream Load 导入 CSV
curl --location-trusted -u root: \
-H "column_separator:," \
-H "label:load_user_events_20250101" \
-T /path/to/user_events.csv \
http://fe_host:8030/api/db_name/doris_user_events/_stream_load

步骤2:实时增量同步

用 Flink CDC 读取 MySQL binlog,写入 Doris。示例 Flink SQL:

sql 复制代码
-- 源表(MySQL)
CREATE TABLE mysql_user_events (
    event_id BIGINT,
    user_id INT,
    event_type STRING,
    event_time TIMESTAMP(3),
    properties STRING,
    PRIMARY KEY(event_id) NOT ENFORCED
) WITH (
    'connector' = 'mysql-cdc',
    'hostname' = 'mysql_host',
    'port' = '3306',
    'username' = 'root',
    'password' = 'xxx',
    'database-name' = 'mydb',
    'table-name' = 'user_events'
);

-- 目标表(Doris)
CREATE TABLE doris_user_events (
    event_id BIGINT,
    user_id INT,
    event_type STRING,
    event_time TIMESTAMP(3),
    properties STRING
) WITH (
    'connector' = 'doris',
    'fenodes' = 'fe_host:8030',
    'table.identifier' = 'mydb.doris_user_events',
    'username' = 'root',
    'sink.buffer-flush.max-rows' = '10000'
);

-- 写入
INSERT INTO doris_user_events SELECT * FROM mysql_user_events;

💡 注意:Doris 的 Unique 模型支持 UPSERT,所以如果 MySQL 有 UPDATE,可以设置 Doris 表为 UNIQUE KEY。

4.3 应用改造:查询路由

代码层面需要区分 写/点查分析查询

好的实践:

java 复制代码
// 事务性操作 -> MySQL
@Autowired
private UserEventMySQLMapper mysqlMapper;
public UserEvent getById(Long id) {
    return mysqlMapper.selectById(id);
}

// 分析查询 -> Doris
@Autowired
private UserEventDorisMapper dorisMapper;
public List<ChannelStat> getChannelStats(Date start, Date end) {
    return dorisMapper.groupByChannel(start, end);
}

BI 工具(Tableau、Superset) 的数据源直接指向 Doris JDBC 地址:jdbc:mysql://doris_fe_host:9030/db

4.4 运维与监控

生产环境必须关注以下指标(Doris 自带 Prometheus + Grafana 监控):

  • FE 的 JVM 内存使用(避免 OOM)
  • BE 的磁盘空间(磁盘满会导致集群只读)
  • Stream Load 延迟与失败率(检验同步管道健康度)
  • 查询耗时 P95、P99

常见问题与优化:

  • 小文件问题 :Flink CDC 写入太频繁会生成大量小版本,导致 compaction 压力。解决:调大 sink.buffer-flush.max-rows 到 5~10 万,或者攒批 5~10 秒。
  • Unique 模型性能:如果更新非常频繁(如每秒万次),建议改为 Duplicate 模型 + 下游去重读,或使用聚合模型。
  • Schema 变更:Doris 支持轻量级 schema 变更(加列、减列),但比 MySQL 稍慢,最好在业务低峰操作。

五、扩展话题:TiDB 能替代 MySQL 吗?

在迁移过程中,你可能会考虑另一个选项------TiDB。这里简单对比一下,方便你做技术选型。

TiDB 定位 :一个分布式 NewSQL ,兼容 MySQL 协议,目标是替代 MySQL 解决水平扩展和高可用问题 。它本质上还是一个 OLTP 数据库,但通过 TiFlash 组件也可以做 OLAP。

Doris vs TiDB 的使用场景:

场景 推荐 理由
交易类业务,数据量大,需要分库分表 TiDB 完全兼容 MySQL,自动分片,强一致性
报表、BI、用户画像、海量日志分析 Doris 列存、MPP 架构,聚合性能远超 TiDB
既有实时交易又有实时分析,且不希望维护两套系统 TiDB + TiFlash 混合负载,但复杂分析性能不如专用 Doris

结论 :如果你的核心痛点是 MySQL 写扩展性和高可用 ,选 TiDB;如果是 分析查询慢,选 Doris。两者也可以共存:MySQL/TiDB 做事务,Doris 做分析,通过 Flink CDC 同步。

六、总结与避坑指南

✅ 成功要点

  1. 明确职责划分:OLTP 归 MySQL/TiDB,OLAP 归 Doris。
  2. 表设计要贴合查询:善用分区、分桶、BloomFilter 和物化视图。
  3. 实时同步首选 Flink CDC:稳定、低延迟、Exactly-Once。
  4. 充分压测:用生产级数据量测试,避免上线后性能翻车。

❌ 常见陷阱

陷阱 后果 解决方案
直接把 MySQL 宽表导入 Doris 列多导致存储膨胀、查询慢 按星型模型拆分,或只迁移必要列
频繁的小批量写入 compaction 跟不上,查询变慢 攒批写入(1万条/10秒)
误用 Unique 模型频繁大规模更新 读写性能极差 评估是否适合,或改用 Duplicate + 下游合并
忽略删除操作同步 数据不一致 Flink CDC 中处理 DELETE 事件,Doris 中用逻辑删除或支持删除的导入方式

最后 :MySQL 迁移到 Doris 是一场 "取长补短" 的工程实践,而不是简单的替换。遵循本文的步骤,你可以高效、平滑地构建一个强大的实时分析平台。

相关推荐
小羊Yveesss1 小时前
2026年商城小程序开发属于什么费用?
apache
跨境技工小黎1 小时前
亚马逊数据抓取怎么做?2026最新实战教程
java·大数据·数据库
lifewange2 小时前
afinfo 表设计
数据库·sql·mysql
m0_463672202 小时前
CSS如何定义可重用的阴影效果_通过CSS变量管理box-shadow参数
jvm·数据库·python
雨辰AI2 小时前
SpringBoot3 + 人大金仓 V9 全栈日志实战:Logback + Loki + Filebeat 构建统一日志平台
java·数据库·后端·云原生·eureka·logback·政务
Irene19912 小时前
SQL Developer 连接类型 (Connection Type) :SID 和 Service Name的区别
数据库·sql·连接方式
2501_901006472 小时前
c++如何利用C++23 std--print加速大量格式化数据的文件IO性能【进阶】
jvm·数据库·python
ljz20162 小时前
递归CTE查询优化方案
数据库
IT果果日记2 小时前
人大金仓使用Flink-CDC
大数据·数据库·后端