一、背景与动机
很多公司在业务初期使用 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。
三、迁移前的评估:你的业务准备好了吗?
在动手写代码前,先问自己三个问题:
数据量有多大?
如果 MySQL 核心分析表只有几百万行,而且查询还能接受,没有必要迁移。只有当单表达到千万级甚至亿级,且查询明显变慢时,Doris 的价值才显现。查询模式是什么?
主要看是否包含SUM、COUNT、GROUP BY、多表JOIN、窗口函数等。这些正是 Doris 的强项。数据如何写入?
- 如果现有的数据是流式追加(如埋点日志、订单流),非常契合。
- 如果频繁更新某些列(如订单状态),需要设计Unique 模型或采用DELETE + INSERT方式,也可以接受。
评估结论:确认要迁移后,就可以进入实战了。
四、生产实践:一步一步教你做
4.1 表结构设计(最关键的一步)
Doris 是列存,且支持三种数据模型:Duplicate(明细)、Aggregate(聚合)、Unique(唯一)。根据业务场景选择。
示例:假设 MySQL 有一张用户行为表
-- MySQL 表CREATETABLEuser_events(event_idBIGINTPRIMARYKEY,user_idINT,event_typeVARCHAR(50),event_timeDATETIME,properties JSON);迁移到 Doris 的建表语句:
-- Doris 明细模型,按天分区,按 user_id 分桶CREATETABLEdoris_user_events(event_idBIGINT,user_idINT,event_typeVARCHAR(50),event_timeDATETIME,properties STRING-- Doris 推荐用 STRING 存 JSON)ENGINE=OLAPDUPLICATEKEY(event_id,user_id)-- 排序列,用于前缀索引PARTITIONBYRANGE(event_time)()-- 分区先不指定,后续动态添加DISTRIBUTEDBYHASH(user_id)BUCKETS16PROPERTIES("replication_num"="3","bloom_filter_columns"="user_id,event_type");设计要点:
- 分区:通常按时间(天/月)分区,方便数据生命周期管理,并利用分区裁剪加速查询。
- 分桶:分桶键选择查询中经常用于过滤或 JOIN 的字段,单桶数据量建议 100MB~1GB。
- 索引:对高基数列(user_id)建立 BloomFilter,可大幅提升点查性能。
4.2 数据迁移:全量 + 实时同步
生产环境需要平滑迁移,不能停业务。推荐Flink CDC方案。
步骤1:全量同步
使用DataX或mysqldump + Stream Load将 MySQL 历史数据导入 Doris。
# 使用 Stream Load 导入 CSVcurl--location-trusted-uroot:\-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:
-- 源表(MySQL)CREATETABLEmysql_user_events(event_idBIGINT,user_idINT,event_type STRING,event_timeTIMESTAMP(3),properties STRING,PRIMARYKEY(event_id)NOTENFORCED)WITH('connector'='mysql-cdc','hostname'='mysql_host','port'='3306','username'='root','password'='xxx','database-name'='mydb','table-name'='user_events');-- 目标表(Doris)CREATETABLEdoris_user_events(event_idBIGINT,user_idINT,event_type STRING,event_timeTIMESTAMP(3),properties STRING)WITH('connector'='doris','fenodes'='fe_host:8030','table.identifier'='mydb.doris_user_events','username'='root','sink.buffer-flush.max-rows'='10000');-- 写入INSERTINTOdoris_user_eventsSELECT*FROMmysql_user_events;💡注意:Doris 的 Unique 模型支持 UPSERT,所以如果 MySQL 有 UPDATE,可以设置 Doris 表为 UNIQUE KEY。
4.3 应用改造:查询路由
代码层面需要区分写/点查和分析查询。
好的实践:
// 事务性操作 -> MySQL@AutowiredprivateUserEventMySQLMappermysqlMapper;publicUserEventgetById(Longid){returnmysqlMapper.selectById(id);}// 分析查询 -> Doris@AutowiredprivateUserEventDorisMapperdorisMapper;publicList<ChannelStat>getChannelStats(Datestart,Dateend){returndorisMapper.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 同步。
六、总结与避坑指南
✅ 成功要点
- 明确职责划分:OLTP 归 MySQL/TiDB,OLAP 归 Doris。
- 表设计要贴合查询:善用分区、分桶、BloomFilter 和物化视图。
- 实时同步首选 Flink CDC:稳定、低延迟、Exactly-Once。
- 充分压测:用生产级数据量测试,避免上线后性能翻车。
❌ 常见陷阱
| 陷阱 | 后果 | 解决方案 |
|---|---|---|
| 直接把 MySQL 宽表导入 Doris | 列多导致存储膨胀、查询慢 | 按星型模型拆分,或只迁移必要列 |
| 频繁的小批量写入 | compaction 跟不上,查询变慢 | 攒批写入(1万条/10秒) |
| 误用 Unique 模型频繁大规模更新 | 读写性能极差 | 评估是否适合,或改用 Duplicate + 下游合并 |
| 忽略删除操作同步 | 数据不一致 | Flink CDC 中处理 DELETE 事件,Doris 中用逻辑删除或支持删除的导入方式 |
最后:MySQL 迁移到 Doris 是一场“取长补短”的工程实践,而不是简单的替换。遵循本文的步骤,你可以高效、平滑地构建一个强大的实时分析平台。