数据库迁移从来都不是简单的数据搬运工。最近刚完成一个从SQL Server到MySQL的完整迁移项目,整个过程踩了不少坑,也积累了一些实战经验。这类迁移在传统企业数字化转型中非常常见,特别是当业务需要从Windows生态转向Linux环境,或者需要降低数据库授权成本时。
SQL Server和MySQL虽然都是关系型数据库,但底层架构差异巨大。SQL Server作为微软的旗舰级数据库产品,深度集成Windows生态,提供全套企业级功能;而MySQL作为开源数据库的代表,更注重轻量化和跨平台能力。这种差异导致数据类型、SQL语法、存储过程等关键要素都存在不兼容情况。
重要提示:迁移前必须评估业务对SQL Server特有功能的依赖程度。比如CTE递归查询、窗口函数、特定GIS函数等,在MySQL中可能需要重构实现方案。
我们评估了三种主流迁移方案:
| 方案 | 适用场景 | 优势 | 风险点 |
|---|---|---|---|
| 原生工具导出导入 | 小型数据库(<10GB) | 无需第三方工具 | 数据类型转换需手动处理 |
| SSMA(SQL Server迁移助手) | 中型数据库(10-100GB) | 自动类型映射 | 复杂存储过程转换可能失败 |
| ETL工具+自定义脚本 | 超大型数据库(>100GB) | 可并行处理、增量迁移 | 开发成本高 |
最终选择了SSMA+自定义脚本的混合方案。SSMA 8.12版本对MySQL 8.0的支持较好,能自动处理约70%的对象转换。但实际测试发现,以下场景需要特别注意:
迁移前需要准备以下资源:
中间服务器:
网络配置:
软件版本:
首先使用SSMA的Assessment Report功能生成兼容性报告。关键要看这几个指标:
sql复制-- SQL Server特有的需要改造的功能示例
SELECT
COUNT(*) OVER(PARTITION BY dept_id) -- 窗口函数
FROM employees;
-- 需要改为MySQL语法
SELECT
dept_id, COUNT(*) as emp_count
FROM employees
GROUP BY dept_id;
常见映射规则:
数据类型转换:
约束处理:
存储过程重写:
连接配置:
powershell复制# SSMA命令行模式启动迁移
SSMAforMySQL.exe -s <source_conn> -t <target_conn>
-m "schema_only" -c "D:\mapping.xml"
分阶段执行:
大表处理技巧:
sql复制-- 在SQL Server端分页提取
SELECT * FROM large_table
ORDER BY id
OFFSET 100000 ROWS FETCH NEXT 50000 ROWS ONLY;
-- MySQL端使用LOAD DATA INFILE加速导入
LOAD DATA LOCAL INFILE '/tmp/chunk.csv'
INTO TABLE large_table
FIELDS TERMINATED BY '|';
迁移完成后必须验证:
数据一致性检查:
sql复制-- 行数比对
SELECT 'SQL Server', COUNT(*) FROM source_table
UNION ALL
SELECT 'MySQL', COUNT(*) FROM target_table;
-- 抽样校验
SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*))
FROM source_table TABLESAMPLE (1000 ROWS);
索引优化:
参数调整:
ini复制# my.cnf关键参数
innodb_flush_log_at_trx_commit=2 # 迁移期间可牺牲部分持久性
bulk_insert_buffer_size=256M
max_allowed_packet=1G
当看到????乱码时,按以下步骤排查:
确认源列是否包含非BMP字符(如emoji)
sql复制-- 检测四字节字符
SELECT column FROM table
WHERE LENGTH(column) != CHAR_LENGTH(column);
确保MySQL使用utf8mb4:
sql复制ALTER DATABASE target_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
连接字符串添加参数:
code复制jdbc:mysql://host/db?useUnicode=true&characterEncoding=UTF-8
SSMA转换存储过程常见问题处理:
游标处理差异:
sql复制-- SQL Server语法
DECLARE @name VARCHAR(100)
DECLARE cur CURSOR FOR...
-- 需改为MySQL语法
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR...
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
临时表作用域:
错误处理机制:
sql复制-- 替换TRY/CATCH
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE;
ROLLBACK;
END;
迁移后查询变慢的常见原因:
执行计划差异:
隐式类型转换:
sql复制-- 坏的写法(导致索引失效)
SELECT * FROM table WHERE int_column = '123';
-- 好的写法
SELECT * FROM table WHERE int_column = 123;
事务隔离级别:
sql复制-- SQL Server默认READ COMMITTED
-- MySQL可尝试调整为:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET GLOBAL tx_isolation='READ-COMMITTED';
监控调整:
架构改进:
备份方案:
bash复制# 使用mysqldump进行逻辑备份
mysqldump --single-transaction --routines --triggers db > backup.sql
# 物理备份建议使用Percona XtraBackup
整个迁移过程中最大的体会是:前期评估越充分,后期踩坑越少。特别是对存储过程、复杂视图的兼容性检查,最好能在测试环境完整跑通所有业务流程。对于超大型数据库,建议采用"结构迁移→基础数据迁移→增量同步→应用切换"的分阶段方案,可以最大限度减少停机时间。