从MySQL到Doris:手把手教你迁移第一个数据库和表(附完整SQL示例)

从MySQL到Doris:手把手教你迁移第一个数据库和表(附完整SQL示例) 从MySQL到Doris技术栈迁移实战指南与避坑手册如果你已经习惯了MySQL的舒适区现在正准备迈入Apache Doris的世界这篇文章就是为你量身定制的迁移指南。我们将从实际案例出发通过一个电商用户行为分析系统的迁移过程带你体验两种数据库在设计哲学和实现细节上的关键差异。1. 迁移前的认知重构理解Doris的核心设计理念在动手写第一行SQL之前我们需要先打破几个MySQL思维定式。Doris虽然兼容MySQL协议但骨子里是完全不同的存在。存储引擎的差异MySQL的InnoDB是行式存储适合OLTP场景Doris采用列式存储(OLAP)针对分析型查询优化Doris特有的预聚合模型(AGGREGATE KEY)可以显著提升统计查询性能分布式架构的思维转变单机MySQL不需要考虑数据分布问题Doris作为分布式系统必须理解分区分桶策略PARTITION BY按时间或枚举值划分大区间DISTRIBUTED BY决定数据在节点间的分布方式副本数(replication_num)影响可用性和存储成本提示在Doris中良好的分区分桶设计能使查询性能提升10倍以上这需要根据业务查询模式精心设计。2. 从零开始构建你的第一个Doris数据库让我们从一个真实的电商场景开始迁移用户行为分析系统。假设在MySQL中已有如下表结构-- MySQL原始表 CREATE TABLE user_events ( event_id BIGINT PRIMARY KEY, user_id INT NOT NULL, event_time DATETIME, page_url VARCHAR(255), device_type ENUM(mobile,desktop,tablet), province_id SMALLINT, duration_sec INT, INDEX idx_user (user_id), INDEX idx_time (event_time) ) ENGINEInnoDB;在Doris中我们需要这样重构-- Doris等效实现 CREATE DATABASE IF NOT EXISTS ecommerce_analysis; USE ecommerce_analysis; CREATE TABLE user_events ( event_id BIGINT, user_id INT NOT NULL, event_time DATETIME, page_url VARCHAR(255), device_type VARCHAR(10), province_id SMALLINT, duration_sec BIGINT SUM DEFAULT 0 ) ENGINEOLAP AGGREGATE KEY(event_id, user_id, event_time, page_url, device_type, province_id) PARTITION BY RANGE(event_time) ( PARTITION p202301 VALUES LESS THAN (2023-02-01), PARTITION p202302 VALUES LESS THAN (2023-03-01) ) DISTRIBUTED BY HASH(user_id) BUCKETS 32 PROPERTIES ( replication_num 3, storage_medium SSD );关键差异解析特性MySQL实现Doris实现原因分析主键显式PRIMARY KEYAGGREGATE KEYDoris采用聚合模型索引二级索引无传统索引列存依靠物化视图优化查询枚举类型原生ENUMVARCHARDoris更倾向灵活性时间分区无内置支持PARTITION BY RANGE分析场景按时间查询是常态3. 数据迁移实战双写与验证策略实际迁移通常采用双写过渡方案以下是推荐步骤结构迁移使用Schema转换工具(如Apache SeaTunnel)自动转换DDL初始全量同步mysqldump -h mysql_host db_name user_events | \ mysql -h doris_fe -P 9030 -u admin ecommerce_analysis增量同步阶段在应用层实现双写逻辑或使用CDC工具如Canal监听MySQL binlog数据一致性验证-- 在Doris中运行 SELECT COUNT(*) AS total_count, SUM(duration_sec) AS total_duration FROM user_events WHERE event_time BETWEEN 2023-01-01 AND 2023-01-31; -- 与MySQL结果对比常见踩坑点MySQL的DATETIME精度可能丢失Doris默认到秒枚举类型需要手动转换为字符串自增ID处理需要特别注意Doris不推荐使用4. 查询模式的重构从OLTP到OLAP思维迁移后最大的挑战是查询模式的转变。我们通过几个典型场景来说明场景1用户行为分析-- MySQL写法OLTP风格 SELECT user_id, COUNT(*) AS event_count FROM user_events WHERE event_time NOW() - INTERVAL 7 DAY GROUP BY user_id ORDER BY event_count DESC LIMIT 100; -- Doris优化写法利用预聚合 -- 先创建物化视图 CREATE MATERIALIZED VIEW user_event_count_mv DISTRIBUTED BY HASH(user_id) BUCKETS 32 REFRESH ASYNC AS SELECT user_id, DATE_TRUNC(DAY, event_time) AS day, COUNT(*) AS event_count, SUM(duration_sec) AS total_duration FROM user_events GROUP BY user_id, DATE_TRUNC(DAY, event_time); -- 查询时直接命中预聚合结果 SELECT user_id, SUM(event_count) AS weekly_events FROM user_event_count_mv WHERE day DATE_SUB(CURRENT_DATE(), 7) GROUP BY user_id ORDER BY weekly_events DESC LIMIT 100;场景2跨表JOIN优化-- 原始MySQL JOIN SELECT u.user_id, u.user_name, SUM(e.duration_sec) FROM users u JOIN user_events e ON u.user_id e.user_id GROUP BY u.user_id, u.user_name; -- Doris最佳实践 -- 方案1使用BROADCAST JOIN维表较小 SELECT /* BROADCAST(u) */ u.user_id, u.user_name, SUM(e.duration_sec) FROM users u JOIN user_events e ON u.user_id e.user_id GROUP BY u.user_id, u.user_name; -- 方案2使用Colocate Group常关联的大表 CREATE TABLE users ( user_id INT, user_name VARCHAR(50) ) DISTRIBUTED BY HASH(user_id) BUCKETS 32 PROPERTIES ( colocate_with user_events );5. 高级调优释放Doris的真正潜力当基本迁移完成后这些技巧能让你获得额外性能提升内存管理-- 针对复杂查询临时增加内存限制 SET exec_mem_limit 8589934592; -- 8GB -- 全局设置需管理员权限 SET GLOBAL exec_mem_limit 17179869184; -- 16GB查询超时控制-- 默认300秒大数据量查询可能需要调整 SET query_timeout 600; -- 查看正在运行的查询 SHOW PROC /current_queries;分区管理自动化-- 动态添加新分区 ALTER TABLE user_events ADD PARTITION p202303 VALUES LESS THAN (2023-04-01); -- 设置自动分区Doris 1.2 ALTER TABLE user_events SET ( dynamic_partition.enable true, dynamic_partition.time_unit MONTH, dynamic_partition.start -12, dynamic_partition.end 3 );在实际项目中我们发现最大的性能瓶颈往往来自最初的分区分桶设计。一个用户日志表经过三次迭代后查询延迟从最初的12秒降到了0.8秒关键是把分区粒度从按月调整为按周同时将分桶数从10增加到64以更好地利用集群资源。