从 MySQL 迁移到 ProtonBase 指南
概述
ProtonBase 提供数据同步服务,可以方便、快捷地将 MySQL 数据一站式迁移至 ProtonBase。本文档将详细介绍从 MySQL 迁移到 ProtonBase 的完整过程,包括数据类型映射、语法差异、实际迁移步骤和最佳实践。
如需了解如何同步 MySQL 数据,请参考文档 Teleport 同步 MySQL 数据。
准备工作
环境要求
- MySQL 5.6 或更高版本
- ProtonBase 数据库实例
- 网络连通性(建议内网连接以获得最佳性能)
- Teleport 数据同步工具访问权限
权限配置
在 MySQL 中创建专门用于数据同步的用户:
-- 使用具有足够权限的账户登录 MySQL
mysql -h ${mysql_host} -u root -p
 
-- 创建同步专用用户
CREATE USER 'teleport_user'@'%' IDENTIFIED BY 'secure_password';
 
-- 授予必要的权限
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'teleport_user'@'%';
 
-- 刷新权限
FLUSH PRIVILEGES;在 ProtonBase 中创建目标用户:
-- 在 ProtonBase 中创建用户
CREATE USER "migration_user" WITH PASSWORD 'secure_password';
 
-- 授予数据库权限
GRANT ALL PRIVILEGES ON DATABASE ecommerce TO "migration_user";网络配置
为确保最佳性能,建议配置内网连接:
- 确保源 MySQL 实例和目标 ProtonBase 实例在网络层面可达
- 配置安全组规则允许相应端口通信
- 如需公网访问,请配置白名单并确保传输安全
数据类型映射
MySQL 和 ProtonBase 之间的数据类型映射关系如下:
| MySQL 数据类型 | ProtonBase 数据类型 | 备注 | 
|---|---|---|
| TINYINT | SMALLINT | 范围不同,MySQL: -128 到 127, ProtonBase: -32768 到 32767 | 
| BOOL / BOOLEAN | BOOLEAN | ProtonBase 使用 BOOLEAN,值为 TRUE 或 FALSE | 
| SMALLINT | SMALLINT | 一致 | 
| MEDIUMINT | INTEGER | ProtonBase 无 MEDIUMINT,通常用 INTEGER 替代 | 
| INT / INTEGER | INTEGER | 一致 | 
| BIGINT | BIGINT | 一致 | 
| FLOAT | REAL | 精度略有不同 | 
| DOUBLE | DOUBLE PRECISION | 一致 | 
| DECIMAL(p, s) | NUMERIC(p, s) | 一致,精度和范围相同 | 
| DATE | DATE | 一致 | 
| DATETIME | TIMESTAMP [WITHOUT TIME ZONE] | ProtonBase 使用 TIMESTAMP,并支持时区 | 
| TIMESTAMP | TIMESTAMP | 一致 | 
| TIME | TIME | 一致 | 
| YEAR | SMALLINT / INTEGER | ProtonBase 无 YEAR 类型,需转换为整数表示 | 
| CHAR(n) | CHAR(n) | 一致 | 
| VARCHAR(n) | VARCHAR(n) | 一致 | 
| TINYTEXT | VARCHAR | ProtonBase 无 TINYTEXT,用 VARCHAR 替代 | 
| TEXT | TEXT | 一致 | 
| MEDIUMTEXT | TEXT | ProtonBase 无 MEDIUMTEXT,用 TEXT 替代 | 
| LONGTEXT | TEXT | ProtonBase 无 LONGTEXT,用 TEXT 替代 | 
| BINARY(n) | BYTEA | ProtonBase 无 BINARY,用 BYTEA 处理二进制数据 | 
| VARBINARY(n) | BYTEA | 同上 | 
| TINYBLOB | BYTEA | ProtonBase 无 TINYBLOB,用 BYTEA 替代 | 
| BLOB | BYTEA | 一致 | 
| MEDIUMBLOB | BYTEA | ProtonBase 无 MEDIUMBLOB,用 BYTEA 替代 | 
| LONGBLOB | BYTEA | ProtonBase 无 LONGBLOB,用 BYTEA 替代 | 
| ENUM | VARCHAR / CHECK 约束 | ProtonBase 无 ENUM,通常用 VARCHAR 并添加 CHECK 约束实现 | 
| SET | 无直接对应 | 可以用 ARRAY 或通过表关联来实现 | 
| JSON | JSONB | ProtonBase 推荐使用 JSONB,支持更高效的操作 | 
SQL 语法差异
如果你使用的是 ORM(Object-Relational Mapping) 框架,例如 Java 的 Hibernate、Python 的 SQLAlchemy 等,那么迁移过程会相对简单。你只需要修改 ORM 配置中的数据库驱动和连接字符串,切换到 ProtonBase 的驱动程序,大部分查询语句都无需改动,因为 ORM 会根据目标数据库自动生成正确的 SQL 语法。
当然,仍然可能存在一些 ORM 不能很好地抽象和转换的数据库特有语法和功能,这些你可能仍需要手动调整。下面是一些常见的语法差异:
标识符引号
- MySQL中使用反引号`来引用数据库、表、列等标识符
- ProtonBase 使用双引号"来引用标识符
字符串引号
- MySQL中使用单引号'和双引号"来引用字符串
- ProtonBase只使用单引号'来引用字符串
LIMIT分页语法
- MySQL支持LIMIT {偏移量}, {行数}的语法进行分页,如LIMIT 20, 10
- ProtonBase使用OFFSET {偏移量} LIMIT {行数}的语法进行分页,如OFFSET 20 LIMIT 10
NULL 判断
- MySQL 使用= NULL或!= NULL判断是否等于 NULL
- ProtonBase 则是 IS NULL或IS NOT NULL。必须使用IS NULL,不能使用=或!=比较
字符串拼接
- MySQL 使用 CONCAT()函数
- ProtonBase 可以使用 ||,如'aa' || 'bb'
GROUP BY 限制
MySQL 里允许查询 ungrouped 字段,ProtonBase 里会报错:
-- MySQL 中可以执行
SELECT a, b, sum(c) FROM t GROUP BY a;
 
-- ProtonBase 中需要修改为
SELECT a, b, sum(c) FROM t GROUP BY a, b;
-- 或者使用聚合函数
SELECT a, any(b), sum(c) FROM t GROUP BY a;Union/Union all 不同字段类型
MySQL在UNION查询中,不同字段类型会被自动转换为相容类型。ProtonBase在UNION查询中,如果字段类型不完全匹配,需要做类型转换。
-- MySQL 中可以执行
CREATE TABLE t1(a int);
CREATE TABLE t2(b text);
SELECT a FROM t1 UNION ALL SELECT b FROM t2;
 
-- ProtonBase 中需要显式转换类型
CREATE TABLE t1(a int);
CREATE TABLE t2(b text);
SELECT a::text FROM t1 UNION ALL SELECT b FROM t2;常见函数对比
| MySQL 函数 | ProtonBase 函数 | 备注 | 
|---|---|---|
| CURDATE() | CURRENT_DATE | 返回当前日期,没有时间部分 | 
| DATABASE() | CURRENT_DATABASE() | ProtonBase 使用 CURRENT_DATABASE() 返回当前数据库名称 | 
| DATE_ADD(date, interval) | date + INTERVAL 'n unit' | ProtonBase 使用 INTERVAL 语法,支持日期加法 | 
| DATE_FORMAT(date, format) | TO_CHAR(date, format) | ProtonBase 使用 TO_CHAR 格式化日期 | 
| DATE_SUB(date, interval) | date - INTERVAL 'n unit' | ProtonBase 使用 INTERVAL 语法,支持日期减法 | 
| DATEDIFF(date1, date2) | date1 - date2 | ProtonBase 使用日期减法运算,返回天数差值 | 
| FORMAT(number, decimals) | TO_CHAR(number, 'FM999999.00') | ProtonBase 使用 TO_CHAR 格式化数字 | 
| FROM_UNIXTIME(unix_timestamp) | TO_TIMESTAMP(unix_timestamp) | TO_TIMESTAMP 将 UNIX 时间戳转换为时间 | 
| IF(condition, true_val, false_val) | CASE WHEN condition THEN true_val ELSE false_val END | ProtonBase 使用 CASE 语句代替 IF 函数 | 
| IFNULL(expr1, expr2) | COALESCE(expr1, expr2) | COALESCE 返回第一个非 NULL 的值 | 
| INSTR(str, substr) | POSITION(substr IN str) | ProtonBase 使用 POSITION 查找子串的位置 | 
| JSON_EXTRACT(json, path) | jsonb_extract_path(json, path) | ProtonBase 使用 jsonb_extract_path 提取 JSON 对象的值 | 
| JSON_UNQUOTE(json) | jsonb_extract_path_text(json, path) | ProtonBase 使用 jsonb_extract_path_text 提取 JSON 文本值 | 
| LAST_INSERT_ID() | RETURNING id 或 CURRVAL('seq_name') | ProtonBase 使用 RETURNING 返回插入后的自动增量主键值 | 
| MONTH(date) | EXTRACT(MONTH FROM date) | ProtonBase 使用 EXTRACT 获取日期的月份部分 | 
| RAND() | RANDOM() | ProtonBase 使用 RANDOM() 生成随机数 | 
| STR_TO_DATE(str, format) | TO_TIMESTAMP(str, format) | ProtonBase 使用 TO_TIMESTAMP 转换字符串为时间类型 | 
| UNIX_TIMESTAMP() | EXTRACT(EPOCH FROM NOW()) | ProtonBase 使用 EXTRACT(EPOCH FROM timestamp) 获取 UNIX 时间戳 | 
| YEAR(date) | EXTRACT(YEAR FROM date) | ProtonBase 使用 EXTRACT 获取日期的年份部分 | 
ProtonBase MySQL 兼容
为了方便用户从 MySQL 兼容的数据库迁移至 ProtonBase,我们提供了一系列兼容功能。这些功能主要涵盖 MySQL 常用函数、类型转换以及一些 MySQL 特性,使得从 MySQL 到 ProtonBase 的迁移过程更加顺利。
快速启用兼容功能:
-- 创建扩展
CREATE EXTENSION mysql;
CREATE EXTENSION
 
-- 验证扩展是否生效(注:部分功能需要重连才会生效)
SELECT ifnull(null, 2);
 ifnull
--------
      2
 
-- 删除扩展
DROP EXTENSION mysql;MySQL 函数兼容
| 函数名 | 示例 | 
|---|---|
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
| 
 |  | 
如果所需的函数未在列表中提供,可以通过 CREATE FUNCTION 语句自定义创建所需的函数:
test=> CREATE FUNCTION add(integer, integer) RETURNS integer
    AS 'select $1 + $2;'
    LANGUAGE SQL
    IMMUTABLE
    RETURNS NULL ON NULL INPUT;
CREATE FUNCTION
test=> SELECT add(1,1);
 add
-----
   2
(1 row)类型转换兼容
| 源类型 | 目标类型 | 
|---|---|
| smallint | boolean | 
| smallint | text | 
| int | text | 
| bigint | text | 
| timestamp | text | 
| numeric | text | 
| text | numeric | 
| int | boolean | 
如果所需的隐式转换未在列表中提供,可以通过 CREATE CAST 语句自定义创建所需的类型转换:
CREATE CAST (smallint AS boolean) WITH INOUT AS IMPLICIT;支持 SELECT 非 GROUP BY 字段
行为说明:
- MySQL 允许 SELECT 未在 GROUP BY 中声明的字段(隐式选择任意值)。
- ProtonBase 默认行为与 PostgreSQL 一致,启用 mysql 扩展后,将兼容此特性。
示例:
-- 启用扩展后可执行
SELECT department, name, AVG(salary)
FROM employees
GROUP BY department;  -- name 未在 GROUP BY 中除零操作返回 NULL
行为说明:
- MySQL 中除零操作返回 NULL 并产生警告。
- ProtonBase 启用兼容模式后,除零结果返回 NULL 而非抛出错误。
SELECT 10 / 0;  -- 返回 NULL注意事项
- 扩展依赖:所有兼容功能需依赖 mysql 扩展,删除扩展将恢复默认行为。
- 类型安全:隐式转换可能导致意外结果,建议迁移后验证关键业务逻辑。
- 函数冲突:若存在同名 PostgreSQL 函数,优先调用 mysql 扩展中的实现。
通过以上功能,ProtonBase 显著降低了从 MySQL 迁移的代码改造成本。如需进一步支持,请联系 ProtonBase 技术支持团队。
实际迁移步骤
以下是一个完整的 MySQL 到 ProtonBase 迁移示例,展示如何进行端到端的数据迁移。
1. 评估和准备阶段
评估源数据库
-- 检查MySQL数据库大小
SELECT 
    table_schema AS "Database",
    ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS "Size (MB)"
FROM information_schema.tables
GROUP BY table_schema;
 
-- 检查表结构
SHOW CREATE TABLE users;
SHOW CREATE TABLE orders;准备目标环境
-- 在ProtonBase中创建数据库
CREATE DATABASE ecommerce;
 
-- 创建用户和权限
CREATE USER "migration_user" WITH PASSWORD 'secure_password';
GRANT ALL PRIVILEGES ON DATABASE ecommerce TO "migration_user";2. 数据结构迁移
创建表结构
-- MySQL中的表结构
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
 
-- 对应的ProtonBase表结构
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);处理特殊数据类型
-- MySQL中的ENUM类型
CREATE TABLE order_status (
    id INT AUTO_INCREMENT PRIMARY KEY,
    status ENUM('pending', 'processing', 'shipped', 'delivered') NOT NULL
);
 
-- ProtonBase中的等效实现
CREATE TABLE order_status (
    id SERIAL PRIMARY KEY,
    status VARCHAR(20) NOT NULL CHECK (status IN ('pending', 'processing', 'shipped', 'delivered'))
);3. 数据迁移
使用Teleport进行数据同步
- 登录ProtonBase控制台
- 导航到"数据同步" -> "数据导入"
- 创建新的同步任务
- 配置MySQL源连接信息
- 配置ProtonBase目标连接信息
- 选择要同步的数据库和表
- 配置同步策略(全量+增量)
- 启动同步任务
验证数据迁移
-- 检查表行数
SELECT COUNT(*) FROM users;
SELECT COUNT(*) FROM orders;
 
-- 检查数据完整性
SELECT * FROM users LIMIT 10;
SELECT * FROM orders WHERE created_at >= CURRENT_DATE - INTERVAL '7 days';4. 应用程序适配
更新连接配置
# 原MySQL配置
# jdbc:mysql://mysql-host:3306/ecommerce
 
# 新ProtonBase配置
jdbc:postgresql://protonbase-host:5432/ecommerce处理SQL语法差异
// 原MySQL分页查询
String mysqlQuery = "SELECT * FROM users LIMIT 20, 10";
 
// ProtonBase分页查询
String protonbaseQuery = "SELECT * FROM users OFFSET 20 LIMIT 10";5. 测试和验证
功能测试
-- 测试基本查询
SELECT u.username, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.username;
 
-- 测试聚合查询
SELECT 
    DATE_TRUNC('month', created_at) as month,
    COUNT(*) as user_count
FROM users
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY month;性能测试
-- 测试查询性能
EXPLAIN ANALYZE 
SELECT * FROM orders 
WHERE user_id = 12345 
AND created_at >= '2024-01-01';迁移最佳实践
1. 迁移前准备
- 
详细评估: - 评估数据量大小
- 识别特殊数据类型
- 检查复杂查询和存储过程
- 评估应用程序依赖
 
- 
制定迁移计划: - 确定迁移时间窗口
- 准备回滚方案
- 制定测试计划
- 安排团队培训
 
2. 迁移过程中
- 
分阶段迁移: - 先迁移非关键业务数据
- 逐步迁移核心业务数据
- 并行运行新旧系统
 
- 
监控和验证: - 实时监控迁移进度
- 验证数据一致性
- 记录迁移过程中的问题
 
3. 迁移后优化
- 
性能调优: - 根据查询模式创建索引
- 优化表存储模式(行存/列存)
- 调整系统参数
 
- 
安全加固: - 配置IP白名单
- 设置用户权限
- 启用审计日志
 
常见问题和解决方案
1. 数据类型不兼容
问题:MySQL的TINYINT在ProtonBase中映射为SMALLINT,可能导致数据范围问题。
解决方案:
-- 在应用层处理数据范围验证
-- 或者在ProtonBase中添加CHECK约束
ALTER TABLE my_table 
ADD CONSTRAINT valid_tinyint 
CHECK (my_column >= -128 AND my_column <= 127);2. SQL语法差异
问题:LIMIT语法不兼容导致查询失败。
解决方案:
-- 使用ORM框架自动处理语法差异
-- 或者在应用中适配SQL语法
-- MySQL: LIMIT 10, 20
-- ProtonBase: OFFSET 10 LIMIT 203. 字符集和排序规则
问题:字符集和排序规则差异导致数据比较异常。
解决方案:
-- 在ProtonBase中明确指定字符集和排序规则
CREATE TABLE my_table (
    name VARCHAR(100) COLLATE "en_US.utf8"
);通过遵循这些步骤和最佳实践,您可以顺利完成从MySQL到ProtonBase的迁移,并充分利用ProtonBase的分布式数据库优势。