如何同步两个 MySQL 表(按需或通过 cron)

Ani*_*udh 7 mysql mysql-replication percona

我见过Cron 同步 mysql 表。但我不能使用复制。我曾经使用过percona-toolkit,它工作得很好。我可以按需运行同步命令,也可以通过 cron 运行它。它会比较两个表上的校验和并执行插入、更新、删除等操作。但是,Perl(DBD::mysql) 和 MySQL 在我要运行的新服务器中存在一些不兼容问题,并且我不能使用 pt-table-同步。是否有类似的解决方案使用 Perl/DBD 以外的其他东西?

编辑:(更多细节,清晰)

  1. 源表和目标表都是活动表并且一直在使用。因此,表不存在的解决方案(例如,完成了 DROP TABLE)是不可接受的。
  2. 不能对服务器本身使用复制或任何此类修改。解决方案必须在客户端工作。
  3. 在这个特定的场景中,两个表都在同一个服务器中,但是不同的数据库(例如 db1.tbl db2.tbl)。然而,一个不依赖于这个事实的解决方案肯定会是一个奖励
  4. 网络延迟不太可能成为问题。在这种情况下,脚本在同一数据中心的服务器上运行。
  5. 无法使用 Perl(Perl 和 MySQL 之间不兼容 - 64 位与 32 位)

the*_*bit 12

用于mysqldump --opt <database> <tablename>创建表的转储并将其提供给新服务器。由于您显然可以通过 TCP/IP 访问远程数据库,因此您只需使用

mysqldump --opt --user=<youruser> --password=<yourpassword> -host <yourhost> \
<yourDB> <yourtable> | mysql -u <newserveruser> -p<password>
Run Code Online (Sandbox Code Playgroud)

连接到远程数据库,转储它并将输出提供给您的新服务器。

如果您没有对远程数据库的直接 TCP/IP 访问,您仍然可以通过在设置公钥身份验证后通过 SSH 隧道传输数据来完成几乎相同的操作:

ssh -C -l <remoteuser> <remoteserver> \
'mysqldump --opt --user=<youruser> --password=<yourpassword> <yourDB> <yourtable>' \
| mysql -u <newserveruser> -p<password>
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请参阅SSH文档mysqldump手册页

如果您需要更高的带宽效率,请考虑使用 来创建转储mysqldump,将其存储在源服务器上并rsync用于在导入之前复制/更新目标服务器上的副本。由于rsync将在源文件和目标文件上创建滚动校验和,它可能不需要在后续运行中传输大部分转储内容。

有一个 mysqldump 补丁,它旨在在插入行时使用临时表,然后将表重命名为原始表名以减少锁定时间,但我认为它是实验性的,因为它有未解决的问题并且从未进入主分支. 有关补丁代码和详细信息,请参阅此讨论

如果您出于某种原因根本无法将表删除到目标上,则可以将转储的数据插入到新表中(一种快速而肮脏但有些不安全的方法会在进一步管道到之前将mysqldump输出sed -e 's/mytable/newtable/g'通过管道传输到mysql),然后运行更新/ DELETE / INSERT 循环,带有几个这样的 JOIN(未经测试,请进行完整性检查):

/* set write lock on the table so it cannot be read while updating */
LOCK TABLES mytable WRITE;

/* update all rows which are present in mytable and newtable */
UPDATE mytable AS M LEFT JOIN newtable AS N ON M.primarykey = N.primarykey 
SET M.column1=N.column1, M.column2=N.column2 [...]
WHERE N.primarykey Is Not NULL;

/* delete all rows from mytable which are no longer present in newtable */
DELETE M FROM mytable AS M LEFT JOIN newtable AS N on M.primarykey = N.primarykey 
WHERE N.primarykey Is NULL;

/* insert new rows from newtable */
INSERT INTO mytable (primarykey, column1, column2, [...]) 
SELECT (N.primarykey, N.column1, N.column2, [...]) FROM mytable AS M 
RIGHT JOIN newtable AS N ON M.primarykey=N.primarykey WHERE M.primarykey Is NULL

/* release lock */
UNLOCK TABLES;
Run Code Online (Sandbox Code Playgroud)

注意:当然,当您插入/更新其数据时,您的数据库数据会不一致,但只要您不使用事务(不适用于 MyISAM 表),无论您做什么,都会出现这种情况 - 删除和重新创建表会产生临时的不一致,就像执行更新/删除/插入循环一样。这是由于 MyISAM 的非原子无事务设计的本质。