当前位置:首页 > 知识

如何把SQLserver数据每天插入到mysql

跨数据库同步这件事,何把最稳的天插路子往往不是写代码,而是何把用 SQL Server 自带的“导出向导”配合 MySQL 的批量加载。听起来有点土,天插但我在三个生产环境里都这么干,何把两年没出过数据错位。天插

先说清楚一个前提:SQL Server 和 MySQL 之间没有原生的何把“复制”通道。SQL Server 的天插复制功能只认 SQL Server 订阅端,MySQL 那边得靠中间层。何把中间层可以是天插 SSIS、Python 脚本,何把也可以是天插每天定时跑的一个 .bat 文件。我选的何把是最后一种,原因很简单——出问题的天插时候,运维能看懂。何把

具体流程分两步走。第一步,从 SQL Server 把当天新增的数据抽出来,存成 CSV。第二步,把 CSV 灌进 MySQL。听起来像把大象装冰箱,但细节全在“怎么抽”和“怎么灌”上。

抽数据的时候,别用 SELECT *。我吃过亏,源表加了个字段,CSV 列数变了,MySQL 那边直接报错。正确做法是显式列出需要的列,并且用 WHERE 条件框定增量范围。如果表里有 UpdateTime 字段,就用它;如果没有,就用自增主键。我见过一个表,主键是 GUID,没有时间戳,最后只能靠“昨天最大 ID”这种笨办法,但也能跑。

SQL Server 导出 CSV 有个坑:默认编码是 UTF-16,MySQL 的 LOAD DATA 不认。得在导出时选“平面文件目标”,然后手动把代码页改成 65001(UTF-8)。这个选项藏得深,在“高级”选项卡的“始终检查”下面。我第一次找的时候,翻了十分钟。

灌数据到 MySQL,LOAD DATA LOCAL INFILE 比 INSERT 快一个数量级。十万行数据,INSERT 要跑一分半,LOAD DATA 三秒完事。但 LOCAL 关键字在 MySQL 8.0 默认是关的,得在客户端配置里加 local_infile=1。服务端也要开 local_infile,否则报错 ERROR 3948。

下面是一个能直接用的命令示例,我把它写进了一个 .bat 文件,每天凌晨两点跑:

bcp "SELECT id, name, amount, UpdateTime FROM SalesDB.dbo.Orders WHERE UpdateTime >= DATEADD(day, -1, GETDATE())" queryout "C:\temp\orders.csv" -c -t"," -S localhost -U sa -P yourpassword -f "C:\temp\orders.fmt" mysql -u root -pYourPass --local-infile=1 -e "LOAD DATA LOCAL INFILE 'C:/temp/orders.csv' INTO TABLE orders FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' (id, name, amount, UpdateTime);" 

bcp 后面的 -f 指定格式文件,这个文件里要写明每列的数据类型和分隔符。格式文件不用手写,用 bcp 的 format 参数生成一次就行。生成之后,把里面的 SQLCHAR 改成 SQLVARCHAR,不然中文会乱码。

有个细节容易被忽略:MySQL 的 LOAD DATA 默认把 \n 当行结束符,但 Windows 的 CSV 是 \r\n。如果 CSV 里某列的值本身包含换行符,整个导入会错位。解决办法是在 bcp 导出时加 -r "\n",强制行结束符为 \n。这个参数在 bcp 的文档里写得很隐蔽,我是在一次数据错位后才发现的。

增量同步的另一个麻烦是“重复插入”。如果昨天的任务失败了,今天重跑,LOAD DATA 会把昨天的数据再插一遍。我的做法是在 MySQL 侧建一个临时表,先 LOAD DATA 到临时表,再用 INSERT ... SELECT ... WHERE NOT EXISTS 合并到正式表。多一步,但能保证幂等。

临时表的建表语句和正式表一样,只是名字加 _tmp。合并完 TRUNCATE 临时表,下次继续用。这个模式我用了很久,没出过重复数据。

最后说一个真实案例。有个客户的订单表每天新增约 8 万行,用上面的方法,从 SQL Server 导出到 MySQL 导入,全程 12 秒。他们的运维原来写了个 Python 脚本,用 pymssql 和 pymysql 逐行读、逐行插,跑一次要 40 分钟。换成 bcp + LOAD DATA 之后,他跟我说:“这玩意儿怎么这么快?”

快的原因很简单:bcp 是原生批量导出,LOAD DATA 是原生批量导入,中间没有逐行解析的开销。Python 脚本慢,是因为它在客户端一行一行地拼 SQL,网络往返次数太多了。

如果你现在还在用 INSERT INTO ... SELECT 跨库同步,可以试试这个方案。不需要装额外软件,SQL Server 和 MySQL 自带的命令行工具就够。唯一的要求是:两台机器之间能互相访问文件路径。如果是云数据库,得先把 CSV 传到中转服务器,再从中转服务器灌到 MySQL。多一跳,但逻辑不变。

分享到: