尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SQL Server迁移MySQL实战:从表结构改造到数据校验的完整流程

SQL Server迁移MySQL实战:从表结构改造到数据校验的完整流程 接手过一两次 SQL Server 迁移到 MySQL 的活儿之后你会发现这事情本质上不是“搬运数据”而是“翻译数据”——把一套运行了很多年的业务逻辑、数据类型、存储过程、甚至是团队的思维习惯从一套体系搬到另一套体系里。我在做这类迁移时踩过不少坑也总结过一套比较顺的流程这篇就把完整过程拆开揉碎讲一遍从迁移前的决策分析到表结构改写、数据搬运、上线校验每一段都有实际可操作的步骤和亲身踩过的教训。1. 迁移动机盘点与核心差异预判先别急着导数据1.1 先搞清楚为什么要迁这决定了你迁到什么程度很多人找我咨询 SQL Server 迁移 MySQL 的问题第一句往往是“怎么把数据库导过去”。但真正接手后我通常会先反问一句你为什么要迁这个问题不是废话因为不同的动机会直接决定迁移方案的深度。常见的动机无非这几类成本压力SQL Server 企业版授权费确实不低很多中小公司为了合规只能买标准版但标准版在内存、CPU 核心数上都有限制。MySQL 社区版零授权费省下的预算够招一个运维了。技术栈统一团队后端本来全是 Java MySQL 的技术栈唯独数据库里躺着一套老的 SQL Server开发维护两条线培训成本和沟通成本都高。统一到 MySQL 后一套 ORM 映射、一套监控体系、一套备份策略全部复用。生态与扩展性MySQL 在互联网场景下的生态太成熟了主从复制、读写分离、分库分表、各类中间件随手一搜全是方案。SQL Server 在这块相比之下要保守一些。上云与容器化MySQL 在 Docker/K8s 里的部署方案非常成熟而 SQL Server 容器化虽然也有但资源占用和运维习惯都要特殊照顾。如果只是某一个模块想迁或者老板拍脑袋说“别人都迁了我们也迁”那你就应该在第一步把周期、风险、工作量讲清楚。迁移数据库不是换台服务器那么简单后面我会具体讲为什么。1.2 两种数据库的“底层脾气”完全不一样在正式开始迁移之前必须建立一个认知SQL Server 和 MySQL 虽然都支持 SQL 标准但底层的设计哲学差异非常大。理解这种差异能帮你预判后面会遇到多少坑。维度SQL ServerMySQL核心存储引擎统一引擎数据文件为主InnoDB / MyISAM 等多种引擎8.0 后默认 InnoDB事务与锁默认提交模式行锁为主快照隔离InnoDB 行锁 MVCC默认自动提交分页语法OFFSET ... FETCH 或 ROW_NUMBER()LIMIT offset, count 或 LIMIT ... OFFSET自增列IDENTITY(1,1)AUTO_INCREMENT字符串拼接 或 CONCAT()CONCAT() 或 CONCAT_WS()空值处理三值逻辑ISNULL()IFNULL() / COALESCE()权限管理登录名 用户 角色分层严格用户 主机 用户名授权粒度库表级存储过程功能强大支持事务内错误捕获支持但语法简化调试手段较弱索引组织聚集索引和非聚集索引概念清晰InnoDB 主键即聚集索引二级索引叶子带主键值这些差异不是说你改改语法就能完事的。比如 SQL Server 里的视图、索引视图、物化视图逻辑在 MySQL 中要重写SQL Server 的递归 CTE 语法在 MySQL 8.0 之前根本不支持8.0 才补上WITH RECURSIVE。如果你的老库里用了大量高级特性迁移的工作量会成倍增加。所以我在做任何迁移之前一定会先出一份“差异预判清单”——列出源库用到的所有特性再去目标库文档里逐一确认支持情况而不是拍脑袋说“应该差不多”。2. 迁移前必须搞透的语法与数据类型映射地基打不牢后面全是坑2.1 数据类型映射表照着抄也要抄对很多人导完表结构发现 MySQL 里字段类型一片乱——不是精度不对就是长度超限报错。这里有一份我实际项目中用过的映射关系表基本上是照着做就不会出大问题的方案SQL Server 类型MySQL 8.0 映射建议备注INT / INTEGERINT直接映射注意位数范围一致BIGINTBIGINT直接映射SMALLINT / TINYINTSMALLINT / TINYINTSQL Server 的 TINYINT 是 0~255MySQL 同样含义一致BITTINYINT(1) 或 BOOLEAN注意判断习惯MySQL 中 BOOLEAN 就是 TINYINT(1) 别名DECIMAL(p,s)DECIMAL(p,s)直接映射传值时注意精度NUMERIC(p,s)DECIMAL(p,s)数值家族建议统一成 DECIMALFLOAT / REALFLOAT / DOUBLEMySQL 的 FLOAT 是单精度DOUBLE 对应 SQL Server FLOAT(53) 场景MONEY / SMALLMONEYDECIMAL(19,4) / DECIMAL(10,4)金融字段慎用浮点必须定点DATETIMEDATETIME直接映射但 SQL Server DATETIME 精度到 3.33msMySQL 到 6 位微秒DATETIME2DATETIME(精度)需手动映射精度级别DATE / TIMEDATE / TIME直接映射SMALLDATETIMEDATETIME注意范围差异SMALLDATETIME 最小精度到分钟CHAR(n) / NCHAR(n)CHAR(n) / 建议 VARCHAR(n)统一用 UTF8MB4 时 CHAR 可能浪费空间VARCHAR(n) / NVARCHAR(n)VARCHAR(n)SQL Server 的 VARCHAR 按字节算MySQL 按字符算需注意含义TEXT / NTEXTTEXT 或 VARCHAR(大)新版建议直接用 VARCHAR 或 JSONVARBINARY / BINARYVARBINARY / BINARY直接映射UNIQUEIDENTIFIERCHAR(36) 或 JSON 存字符串MySQL 没有原生 UUID 类型用 CHAR(36) 比较现实XMLTEXT 或 JSON业务处理完再转换GEOGRAPHY / GEOMETRYGEOMETRY / 需要时用 PostGIS 或转坐标文本MySQL 空间扩展没有 SQL Server 精细这里特别提醒一句字符串长度是最容易出 bug 的点。SQL Server 里VARCHAR(10)表示最多 10 个字节英文 10 个、中文按编码可能占 2~4 个字节而 MySQL 8.0 里VARCHAR(10)默认是 10 个字符。如果你有一张老表存中文学名、地址之类的字段SQL Server 里VARCHAR(50)可能只能存 25 个中文迁移到 MySQL 后同一字段写 50 个中文没问题——这其实是“变宽松”了问题不大。但反过来如果原来的应用有校验“最多 10 个字符”MySQL 里这个字段会接受更多数据可能导致下游应用显示异常。最稳妥的做法是迁移后统计一下字段最大长度有没有变化再决定是否调整 DDL。2.2 函数和系统对象改写的量比想象中大数据类型的坑是结构性的函数和语法的坑是日常折磨人的。我列几个最常遇到的日期函数-- SQL Server SELECT GETDATE(), DATEADD(DAY, 1, GETDATE()), DATEDIFF(DAY, 2024-01-01, GETDATE()); -- MySQL SELECT NOW(), DATE_ADD(NOW(), INTERVAL 1 DAY), DATEDIFF(NOW(), 2024-01-01);这两个体系的日期函数最典型的差异是SQL Server 的DATEADD/DATEDIFF用单位字符串 数值MySQL 用INTERVAL关键字。你写存储过程时这种细节会密集出现。强烈建议迁移前先做一次代码扫描把GETDATE()、DATEADD()、DATEDIFF()、DATEPART()、CONVERT()全量列出来统一替换。字符串函数SQL Server 的SUBSTRING()是 1-based 索引MySQL 的SUBSTRING()同样 1-based这块还好但 SQL Server 的LEN()会忽略尾部空格MySQL 的LENGTH()是字节长度不是字符长度CHAR_LENGTH()才是字符数。你如果原来写了LEN(column)去做条件判断到 MySQL 里原样改成LENGTH(column)遇到中文就错了。应改成CHAR_LENGTH()。还有SQL Server 里ISNULL()、NULLIF()、COALESCE()都有但 MySQL 没有ISNULL()它只有IFNULL()NULLIF()有但用法略有差异。写习惯 SQL Server 的老程序员在 MySQL 里很容易踩这个。分页与 TOP-- SQL Server SELECT TOP 10 * FROM users ORDER BY id; -- MySQL SELECT * FROM users ORDER BY id LIMIT 10;MySQL 8.0 支持OFFSET FETCH了但LIMIT依然是全兼容的推荐写法。分页还有一个隐性问题SQL Server 的分页如果没写ORDER BYTOP返回的行是随机的MySQL 的LIMIT不写ORDER BY同样是随机的。但 SQL Server 的习惯一般都会写 ORDER BY这块没有额外坑。标识符与大小写SQL Server 在 Windows 上默认不区分表名大小写但 MySQL 在 Linux 上默认区分。迁移后如果原代码里用的表名大小写不一致会莫名其妙报Table doesnt exist。建议在 MySQL 的配置里把lower_case_table_names设为 1让表名统一转小写存储避免一串大小写问题。注意这个参数在初始化实例前就要确定后期改非常麻烦。2.3 字符集与排序规则中文不乱的最后防线我见过有人做迁移数据搬过来后中文显示????查了半天才发现建表时的字符集没设置默认落到了 latin1。MySQL 8.0 默认是utf8mb4但你在建库时最好显式设置CREATE DATABASE yourdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;排序规则也值得多说一句。SQL Server 的中文排序规则一般是Chinese_PRC_CI_AS按拼音排序。MySQL 里utf8mb4_unicode_ci是基于 Unicode 排序规则跟拼音排序不完全一致。如果业务里有“按名称首字母排序”之类的需求迁移后可能排序结果跟以前不一样。需要拼音排序的话可以在 MySQL 中加一个拼音字段或使用CONVERT(column USING gbk)这种近似方案。3. 表结构与对象迁移实操手写 DDL 比想象中要稳3.1 那些“一键迁移”工具为什么总在坑边试探很多人在第一步会想到用 Navicat 这类图形化工具直接同步表结构。怎么评价呢——快是快但要仔细检查生成结果。Navicat 在简单场景纯表、纯索引下能撑住但遇到以下情况就会出岔子默认值表达式差异SQL Server 的GETDATE()默认值不会被正确映射成 MySQL 的CURRENT_TIMESTAMP自增列SQL Server 的IDENTITY(1,1)有时会被映射成普通的 INT没有自增属性外键和索引命名工具会重新生成一堆带前缀的索引名后面排错很难受所以我宁可自己写一套 DDL 转换脚本用 Python 或者 Perl 批量处理CREATE TABLE语句也不完全信任图形化工具。具体做法是从 SQL Server 用生成脚本的方式导出表结构选项里选择“仅架构”然后把生成的 .sql 文件拿到文本编辑器里做替换。3.2 手工转换表结构的完整步骤假设你有一张这样的 SQL Server 表CREATE TABLE [dbo].[Order] ( [Id] INT IDENTITY(1,1) NOT NULL, [OrderNo] NVARCHAR(32) NOT NULL, [CustomerId] INT NOT NULL, [Status] TINYINT NOT NULL DEFAULT 0, [Amount] DECIMAL(12,2) NOT NULL, [CreatedAt] DATETIME NOT NULL DEFAULT GETDATE(), [UpdatedAt] DATETIME2(3) NULL, [Remark] NTEXT NULL, PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [UQ_OrderNo] UNIQUE NONCLUSTERED ([OrderNo] ASC) );转换后 MySQL 8.0 版本大致是这样CREATE TABLE Order ( Id INT NOT NULL AUTO_INCREMENT, OrderNo VARCHAR(32) NOT NULL, CustomerId INT NOT NULL, Status TINYINT NOT NULL DEFAULT 0, Amount DECIMAL(12,2) NOT NULL, CreatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UpdatedAt DATETIME(3) NULL, Remark TEXT NULL, PRIMARY KEY (Id), UNIQUE KEY UQ_OrderNo (OrderNo) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;这里有几个点要专门打理表名MySQL 没有dbo这个概念去掉[dbo]前缀。自增IDENTITY(1,1)→AUTO_INCREMENT。默认值GETDATE()→CURRENT_TIMESTAMP。字符串类型NVARCHAR(32)→ 因为库表统一 utf8mb4直接VARCHAR(32)即可。NTEXT→TEXT但你生产环境如果还有 NTEXT 字段建议业务侧先规划是否改成VARCHAR大字段因为 TEXT 类型在 MySQL 里做索引限制很多。如果表非常多比如上百张建议写一个简单的自动化脚本处理这些映射规则人工手改真的会疯。有一类工具叫 schema 转换工具比如开源社区的一些 converter但多数针对 PostgreSQL对 SQL Server 的支持参差不齐。最靠谱的还是自己维护一张替换表用 sed 或 Python 脚本批量跑。3.3 索引、主键和自增列的处理细节索引这块要特别小心聚集索引的选择。SQL Server 的物理结构是“堆 非聚集索引”或者“聚集索引表”你可以把非主键列设为聚集索引。MySQL InnoDB 里表本身就是按主键组织的 B 树主键选得好不好直接影响写入性能和空间占用。如果源表的主键是INT IDENTITY那迁移到 MySQL 基本都是用AUTO_INCREMENT主键没毛病。但如果源表用的是UNIQUEIDENTIFIERGUID主键迁移到 MySQL 就要想一下继续用CHAR(36)存 GUID好处是业务代码不需要变化改成一个自增主键 一个UNIQUE的 GUID 字段性能更好但要改代码我个人的建议是如果业务代码对 GUID 有强依赖比如前端拿到 GUID 做人脸识别啥的那就保留CHAR(36)主键但在 InnoDB 里数据量一大随机字符串主键会导致页分裂严重。要么改成自增主键要么用 UUID 的有序版本比如 MySQL 8.0 的UUID_TO_BIN(..., 1)。这块需要和业务侧沟通好再动手。自增列的迁移还有一个细节IDENTITY有IDENTITY_CURRENT迁移后需要对齐。比如原来 SQL Server 自增值已经跑到 1080你迁移数据时必须把 MySQL 的AUTO_INCREMENT设为 1081否则新插入数据时主键冲突。MySQL 中可以用ALTER TABLE Order AUTO_INCREMENT 1081;4. 数据搬运的三大路线与实测对比以及提速技巧4.1 路线一可视化工具直连传输适合中小库很多人对 Navicat 这类工具的数据传输功能非常依赖。操作方式简单源库选 SQL Server目标库选 MySQL勾选要迁移的表点开始。这个方案在5000 万行以下、单表不超过几 GB的场景下其实完全可行。但有一个体验很差的点Navicat 默认是逐表传输遇到大表时进度条看着像卡住了实际上可能在分批读取。如果中途失败不会做断点续传只能重来。而且它会自动创建目标表如果目标表已存在则清空数据这个“清空”动作要格外小心——选错目标库直接就白干了。建议是数据迁移前在目标库里建一个新的数据库名比如yourdb_migrate验证通过后再改名为正式库。4.2 路线二生成转储文件再用命令行导入适合大库对于大库我最常用也最推荐的是“源库导出 CSV / 转储文件 MySQL 命令行导入”这条路。核心思路是把数据流从 GUI 里解放出来充分利用 MySQL 的LOAD DATA INFILE。SQL Server 侧可以这样导出-- 使用 bcp 工具按 CSV 导出 bcp SELECT * FROM [yourdb].[dbo].[Order] queryout order.csv -c -t , -T -S yourserver或者用 SQL Server Management Studio 的“导出数据”功能选择平面文件目标。注意编码一定要选 UTF-8否则中文字段到 MySQL 侧会乱。MySQL 侧导入mysql -u root -p yourdb -e LOAD DATA INFILE /tmp/order.csv INTO TABLE Order FIELDS TERMINATED BY , ENCLOSED BY \ LINES TERMINATED BY \n IGNORE 1 ROWS;这里有几个比较折磨人的细节CSV 转义问题。如果源字段里本来就有逗号、换行符、引号CSV 导出时必须保持一致并且LOAD DATA的FIELDS ENCLOSED BY要和导出时一致。SQL Server 的 bcp 默认不带引号包裹遇到字段含逗号就会错列。导导出时宁可多传参数让每个字段都用引号包起来bcp SELECT ... queryout order.csv -c -t ^|^ -T -S yourserver把分隔符换成^|^这种不太可能出现在数据里的组合比逗号稳得多。对应的 MySQL 导入LOAD DATA INFILE /tmp/order.csv INTO TABLE Order FIELDS TERMINATED BY ^|^ ENCLOSED BY LINES TERMINATED BY \n;编码问题。MySQL 的 LOAD DATA 默认会按表的字符集解析。如果你的 CSV 是 UTF-8 编码而表是 utf8mb4那没问题。但如果 CSV 是 GBK 或 GB2312很多 Windows 上的 SQL Server 导出默认编码很混乱一定要先转码再导入。iconv -f GBK -t UTF-8 order.csv order_utf8.csv这步不能省。大表分批策略。单表几千万行时一次性导入会拖很长。我一般会按主键范围拆成若干文件并行导入。比如# 假设按 Id 拆成 10 个批次 seq 0 9 | xargs -P 4 -I {} mysql -u root -p yourdb \ -e LOAD DATA INFILE /data/order_part_{}.csv INTO TABLE Order ...但这里的并行度要控制好InnoDB 在写入时如果多个事务同时插入可能会造成锁竞争和 IO 飙升。我实测的经验是2~4 个并发导入进程效果最好再多反而会因为磁盘 IO 饱和导致整体时间变长。4.3 路线三自写脚本分批拉取最可控适用于复杂场景如果源库和目标库之间网络不稳定或者业务要求停机时间极短可视化工具和 CSV 文件都不太合适。这时候我倾向于写一个中间件脚本Python pyodbc读源 pymysql写目标按主键分批拉取并写入。这种方案的好处可以控制每秒写入行数给业务系统留出带宽中途失败可以从断点继续不用重来可以顺便做数据转换比如把 datetime 格式统一、把 GUID 改成字符串写脚本时需要注意一点一定要用服务器端游标或者流式读取不要在应用层把整张表载入内存。pyodbc 默认cursor.execute()是全量缓冲几千万行会直接内存打爆。要用类似cursor.execute(sql)for row in cursor这种流式方式或者设置select语句中每次只取前 N 行再根据条件续取。4.4 让迁移提速的几个关键配置不管走哪条路线在导入数据之前把 MySQL 的下面几个参数临时调大能明显减少导入时间。前提是导入的时候能扛住重启实例或者设置后不影响其他业务迁移窗口期一般可以接受innodb_buffer_pool_size8G # 尽量接近实际可用内存的 70% 左右 innodb_flush_log_at_trx_commit0 # 临时关闭每次提交都刷盘最后再改回 1 sync_binlog0 # 减少 IO 等待 innodb_autoinc_lock_mode2 # 也行8.0 默认是 2无需改 max_allowed_packet256M # 防止大行写入失败导入完成后记得把这几个参数恢复成生产环境的稳定值并且在业务低峰期平滑重启一次 MySQL让 buffer pool 里的数据重新预热。5. 迁移后的数据校验与联调排错迁移没验证完不敢说上线5.1 行数校验很简单陷阱却不简单数据搬完后的第一件事是数行数。但“SELECT COUNT(*)”对比两边一致不等于数据就是对的——这种粗粒度校验只能防“整表丢了”这类大灾难。我见过最有迷惑性的场景是行数一模一样但某列的部分字段值在转换中变成了 NULL或者金额精度被悄悄截断。所以校验要分级做第一级全表行数对比SELECT COUNT(*) FROM src_table; -- SQL Server 侧 SELECT COUNT(*) FROM dst_table; -- MySQL 侧第二级抽样校验关键字段对每个表抽取几条数据的全部字段做逐字段对比。用脚本自动做比较好比如 Python 读源库一条再读目标库同主键记录比较所有列。第三级统计特征校验对数值字段做SUM、MAX、MIN对比。比如金额字段 SUM 是否一致时间字段的最大最小值是否一致。这能发现“数据错位”类的问题。如果是订单表这种核心表最好再做一下分组统计的对比比如“按状态分组统计条数是否一致”。这一套三级校验走下来比单独一个 COUNT(*) 可靠太多了。5.2 最容易爆雷的三大类问题及排查思路第一类编码混乱如果迁移后发现中文字段出现????或者 “乱码”一般不是 MySQL 目标表的字符集错了就是源数据导出时编码不统一。排查方式SELECT HEX(column_name) FROM table_name LIMIT 1;正常中文 utf8mb4 的 HEX 应该是跳字节的。如果看到一堆3F3F3F即?的 ASCII 码说明数据在导入时就被转换成了问号源头上的原始值都已经丢失——这时候只能回源重新导出。第二类日期时间问题SQL Server 的DATETIME是从 1753 年开始的MySQL 的DATETIME支持范围是 1000-01-01 到 9999-12-31。一般来说没问题但有些人会把0000-00-00之类的字符串存进去做“未设置”标记。这种数据在 MySQL 里默认会被拒绝或者在某些 sql_mode 下被转成 NULL。建议迁移前先检查源库有哪些特殊日期值统一处理策略——是改成 NULL还是改成1970-01-01一定要和业务确认清楚。第三类SQL 语法不兼容迁移完数据应用层的 SQL 还有一堆没改完。最常见的报错是LIMIT分页问题SQL Server 的OFFSET ... FETCH在 MySQL 8.0 已经支持但老版本5.7 及以下不支持字符串拼接col1 col2在 SQL Server 里如果col1是数字且col2是字符串会走隐式转换在 MySQL 里只做算术运算想拼接必须用CONCAT()表别名与UPDATE语法差异SQL Server 支持UPDATE t SET ... FROM table t JOIN ...MySQL 的写法是UPDATE table t JOIN ... SET ...INSERT INTO ... SELECT的并发差异如果目标表有自增主键MySQL 5.7 之前加AUTO_INCREMENT字段时有个 известный 的坑低版本会阻塞并发8.0 已经优化排查手段我觉得最有效的还是让应用自己去跑一遍全量接口测试。如果没有完整的接口测试集至少把登录、列表、详情、下单这几条核心链路手工跑一遍。数据库迁移说到底是为了业务不中断如果业务行为出现了任何一个“和以前不一样”都要先检查是不是迁移产生的副作用。5.3 迁移窗口期设计旧库变只读新库接管流量这一步非常关键。我的习惯是“五步切换法”停机前把 SQL Server 设置成只读模式禁止业务写入只允许读。增量同步如果迁移总时长超过 30 分钟业务方可能等不了。这时候可以先做一次全量迁移然后记录迁移开始时间点在正式切换前再做一次增量同步从源库导出上次迁移时间点之后新增/变更的数据再用脚本并入 MySQL。业务切换把应用服务器的数据库连接串从 SQL Server 换成 MySQL。只读校验切换后先开放一部分只读功能验证 MySQL 能正常扛住读流量再逐步开放写流量。旧库保留期建议至少保留 SQL Server 实例 1~2 周不删除任何数据方便随时回滚。有人会问能不能不停机做迁移理论上可以用 CDC变更数据捕获或者第三方同步工具做准实时同步但 SQL Server 到 MySQL 的跨库同步方案远没有 MySQL 主从那么成熟而且中间环节越多越容易出幺蛾子。对于大多数业务我更推荐“短暂停机 增量同步 快速切换”的方案把切换窗口控制在 10~15 分钟以内对业务影响很小。6. 迁移前后的 SQL 审核与性能对照收尾工作决定长期体验6.1 迁移后的 SQL 执行计划检查清单数据迁完了、业务跑起来了不代表事情结束了。SQL Server 的优化器选出来的执行计划和 MySQL 的优化器完全不是一回事。原来在 SQL Server 上跑得很好的复杂 JOIN换到 MySQL 上可能因为统计信息没更新、索引没建对变成全表扫描慢查询直接拖垮数据库。上线后第一周我建议每天看一遍SLOW QUERY LOG把耗时超过 1 秒的 SQL 捞出来逐个分析-- 打开慢查询日志临时设置 SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1;然后逐个检查这些 SQL 的EXPLAIN结果重点关注type是不是ALL全表扫描如果是检查是否缺少合适索引rows是不是远大于预期如果是手动执行ANALYZE TABLE更新统计信息Extra里有没有Using filesort、Using temporary这两种情况在 MySQL 中特别费资源能通过加索引规避就加索引有一类非常典型的问题SQL Server 里IN和EXISTS的优化策略比 MySQL 激进很多。原来WHERE col IN (SELECT ...)在 SQL Server 上能走半连接优化同一个写法到了 MySQL 5.7 上如果子查询表比较大优化器可能会选择先扫描子查询结果再做 Nested Loop性能差好几倍。这种 SQL 需要手动改成JOIN形式并且用小表驱动大表的方式调整 JOIN 顺序。6.2 时间线回顾与后续运维习惯调整迁移完成两周后我会再做一次复盘源库里那些复杂的索引视图、触发器、作业调度SQL Server Agent Job有没有全部在 MySQL 侧找到替代方案备份策略是不是已经从“SQL Server 备份到网络共享”切换成“mysqldump / mysqlbackup / xtrabackup”监控告警里有没有加 MySQL 专属的指标连接数、锁等待、InnoDB buffer pool 命中率团队里的开发是不是还在用 SQL Server 的写法写新代码要不要做一次 SQL 规范培训这些看起来都是管理层面的事情但不做迁移的长期效果就会打折扣。数据库迁移不只是一次技术操作更是团队技术栈的一次切换。习惯没有切换过来后面还会有各种隐性的雷。最后分享两个实操小技巧第一导出 CSV 时可以在 SQL Server 侧直接用bcp的-e参数把错误行输出到单独文件。如果数据里有特殊字符导致导入失败错误文件能帮助你精准定位是哪一行哪一列的问题不用在几千万行里大海捞针。第二MySQL 8.0 有一个STRAIGHT_JOIN提示符可以在优化器选错 JOIN 顺序时手动纠正。迁移初期性能不稳时遇到极端场景可以用它应急但不要长期依赖——它属于最后手段根本解法还是把 SQL 改得更符合 MySQL 优化器的胃口。迁移这种事情真正做过一遍之后你会发现技术难点其实不像背文档那么可怕最怕的是遗漏那些“你以为不用改结果改了三天”的细节。希望这份指南能帮你少走点弯路。
返回列表