跨数据库同步靠纯sql实现,核心是insert into...select,需同时具备源库select和目标库insert权限,注意锁表、字段映射及数据一致性。

跨数据库同步不等于主从复制,它发生在同一 MySQL 实例内,靠纯 SQL 就能完成,但必须注意权限、锁和字段映射这三关。
INSERT INTO ... SELECT 是最直接的跨库同步方式
只要源库表和目标库表在同一个 MySQL 实例中,INSERT INTO target_db.t2 SELECT * FROM source_db.t1 就能执行。它本质是一条原子 SQL,不是“导出再导入”。
- 执行用户必须同时对
source_db和target_db有SELECT和INSERT权限 - 源表会被加读锁(InnoDB 下是 consistent read,但大表仍可能阻塞长事务)
- 目标表若含自增主键或唯一约束,需提前确认数据不冲突;否则加
SET FOREIGN_KEY_CHECKS=0; SET UNIQUE_CHECKS=0;临时关闭检查 - 不支持跨 MySQL 实例——如果报错
ERROR 1146 (42S02): Table 'xxx' doesn't exist,先确认是不是连错了实例
字段名/类型不一致时必须显式映射
不能依赖 SELECT * 直接插入结构不同的目标表,否则会报列数不匹配或类型转换失败。
- 字段名不同:用
AS别名对齐,例如user_id AS id, name AS user_name - 类型不兼容:用
CAST()或函数转换,比如CAST(create_time AS DATETIME)、DATE(created_at) - 新增固定值:直接写常量,如
'sync_20260701' AS sync_tag - 忽略某列:不在
INSERT INTO (...)和SELECT中出现即可,但目标列要有默认值或允许 NULL
定时同步脚本里最容易漏掉的三件事
用 crontab 调 mysql -e 看似简单,但线上出问题基本都栽在这几个点上。
- 密码明文暴露:
mysql -u root -p123456 -e "..."会被ps aux看见,必须用--defaults-extra-file配置文件,且权限设为600 - 误判成功:
mysql命令退出码为 0 不代表数据真写进去了,必须跟一句mysql -Nse "SELECT ROW_COUNT();"检查影响行数 - WHERE 条件失效:比如
WHERE updated_at > NOW() - INTERVAL 1 DAY在跨时区部署时可能漏数据,优先用 UTC 时间字段或带时区的TIMESTAMP
大表同步时为什么不能只靠 INSERT SELECT?
单次执行耗时过长,容易触发 max_execution_time 限制,还可能撑爆 buffer pool 或引发主库慢查询告警。
- 分批处理:用主键 or 自增 ID 分段,例如
WHERE id BETWEEN 10000 AND 20000,配合循环脚本 - 避开高峰:避免在业务高峰期执行,尤其不要在凌晨备份窗口前后跑大同步
- 替代方案:超 100 万行建议改用
mysqldump --where="..." | mysql,它天然支持--single-transaction且不锁表(仅限 InnoDB) - 注意
mysqldump默认不带--skip-definer,遇到视图或事件同步失败时,先加这个参数再重试
跨库同步真正的复杂点从来不在语法,而在于你是否清楚那条 SQL 执行时锁了什么、读了什么、写了什么,以及失败后有没有可靠的方式发现并重试——这些细节不会报错,但会在某个低峰期悄悄导致数据不一致。











