【问题标题】:Synchronizing table data across databases跨数据库同步表数据
【发布时间】:2013-02-19 18:54:31
【问题描述】:

我有一张表,它在字段上记录其行插入/更新时间戳。

我想将此表中的数据与另一个数据库服务器上的另一个表同步。两个数据库服务器未连接,同步是一种方式(主/从)。不适合使用表触发器

我的工作流程:

  • 我使用全局 last_sync_date 参数和查询表 Master 更改/插入的记录
  • 将结果行输出到 xml
  • 使用更新和插入解析 xml 和更新表 Slave

在处理Master表的删除记录时,问题的复杂性增加了。为了捕获已删除的记录,我想我必须为先前插入的记录维护一个日志表并使用 sql“NOT IN”。这在处理大型数据集时会成为性能问题。

处理这种情况的替代工作流程是什么?

【问题讨论】:

  • 你使用的是什么数据库? (主从?)
  • 目前使用 MsSql 作为 master 和 mysql 作为 slave,所以我不认为特定于数据库的解决方案会有所帮助
  • 也可以看看这个稍微相关的链接en.wikipedia.org/wiki/CAP_theorem
  • 问题的措辞不符合其内容,我怀疑。如果服务器未连接,那么您唯一的选择是将文件导出到可移动磁盘并以这种方式传输数据,否则它们会以某种方式连接。 Connected 包括网络,因此您可以使用触发器等选项。您不应该使用它们的唯一原因是 DBA 拒绝它,我从未遇到过 DBA 会拒绝使用触发器,如果​​它被证明是有益的。如果您合法地这样做,则可以克服防火墙限制等。
  • @JustAguy 我为你感到同情,因为我提供了消息队列的解决方案,因为他标记了问题spring-batch。恕我直言,他应该使用真正的复制或消息队列。他还担心一个甚至还不是问题的性能问题(即:NOT IN 问题)。

标签: java sql spring-batch


【解决方案1】:

【讨论】:

  • 很遗憾,它们不适合我的用例,谢谢。
【解决方案2】:

非常有趣的问题。

在可能的情况下,我有足够的 RAM 来加载主表和从表中的所有 id 以区分它们。

如果主表中的 id 是连续的,您可以尝试在主表中维护一组完整填充的范围(使用所有 id 的范围,没有空格,例如 100,101,102,103)。

要查找已删除的 id 而不将它们全部加载到内存中,您可以执行 SQL 查询来计算每个已填充区域的 id >= full_region.start and id <= full_region.end 记录数。如果查询结果为== (full_region.end - full_region.end) + 1,则表示区域内的所有记录删除。否则 - 将区域分成两部分并对它们进行相同的检查(在很多情况下,只有一侧包含已删除的记录)。

经过一定长度的范围(我认为大约为 5000)后,加载所有存在的 id 并使用 Set 检查是否存在会更快。

还有一种意义是将所有 id 加载到内存中以用于一批小(10-20 条记录)区域。

【讨论】:

  • 感谢您大开眼界的解决方案;特别是使用范围。不幸的是,主数据库和从数据库没有连接,所以我无法从从表中加载所有 id。但我会考虑内存解决方案。
【解决方案3】:

为什么不添加一个 TIMESTAMP 列来指示上次更新/插入/删除时间?然后添加一个已删除的列 - 即。将该行标记为已删除,而不是立即实际删除它。导出删除操作后将其删除。

如果您无法更改现有应用中的架构用法:

你根本不能使用触发器吗?第二个(“隐藏”)表如何填充每个插入/更新/删除并且构成下一个要生成的 xml 导出文件的内容?这是一个常见的概念:历史(或“日志”)表:它将有自己的进度 id 列,可用作导出标记。

【讨论】:

  • 感谢您的评论。我已经有一个时间戳列并在选择中使用它。不幸的是,我不能删除列,因为这会导致修改真正使用主表的应用程序。我希望这个同步作业独立于域。
【解决方案4】:

我同意另一条评论 - 这需要使用触发器。我认为另一个表应该保存您的 sql 语句的历史记录。见this answer关于使用2008扩展事件... 然后,你可以得到整个sql,并将结果查询存储在历史表中。如果要将其存储为 mysql 查询或 mssql 查询,由您决定。

【讨论】:

    【解决方案5】:

    看看Oracle GoldenGate:

    Oracle GoldenGate 是一个全面的软件包,用于启用 在异构数据环境中复制数据。产品 set 支持高可用性解决方案、实时数据集成、 事务性变更数据捕获、数据复制、转换、 运营和分析企业之间的验证 系统。

    SymmetricDS:

    SymmetricDS 是多主数据库的开源软件 复制、过滤同步或转换 异构环境中的网络。它支持多个 具有单向或双向异步数据的订阅者 复制。

    Daffodil Replicator:

    Daffodil Replicator 是一个 Java 数据同步工具,数据 各种数据库服务器之间的迁移和数据备份。

    【讨论】:

      【解决方案6】:

      为需要同步的表创建一个历史表(基本上是该表的副本,可能还有一些额外的字段),并在每次在活动表中插入/更新/删除某些内容时插入整行。

      编写一个 Spring 批处理作业,根据历史表的额外字段将数据同步到 Slave 机器

      希望这会有所帮助..

      【讨论】:

        【解决方案7】:

        在当前工作流程中允许删除的潜在选项:

        如果触发器限制仅限于跨数据库引用的触发器,则当前工作流程中的一个可能解决方案是在主数据库中创建一个帮助表,以仅存储已删除行的唯一标识符(或其他唯一键将使您能够最有效地删除已删除的行)。

        这些 ID 需要在删除时由您的主表上的触发器插入。

        使用与插入/更新相同的机制,在插入和更新之后创建一个任务。正如您在当前工作流程中所述,您可以将帮助表导出到 xml。

        此任务将简单地从从表中删除行,然后在任务完成后从您的助手表中删除所有数据。记录任务中的任何错误,以便您可以解决此问题,因为没有审计跟踪。

        【讨论】:

          【解决方案8】:

          这是我的看法。你真的需要处理这个吗?我假设奴隶是为了报告的目的。所以我要问的问题是它应该是最新的吗?如果数据是一天前的可以吗?您是否计划每晚刷新一次?

          如果是这样,请忘记此在线同步过程,下载完整表格;将其发送到 mysql 并批量加载。处理时间可能比您想象的要快得多。

          【讨论】:

            【解决方案9】:

            听起来您需要一个事务性消息队列。

            它的工作原理很简单。当您更新主数据库时,您可以向消息代理(无论更新是什么)发送一条消息,该消息可以发送到任意数量的队列。每个从属数据库都可以有自己的队列,并且由于队列的保留顺序,该进程最终应该正确同步(具有讽刺意味的是,这是大多数 RDBMS 在内部进行复制的方式)。

            将消息队列视为一种 SCM 更改列表或补丁列表数据库。 这在很大程度上应该是相同(或大致相同)的 SQL 语句发送到 master最终复制到其他数据库。不要担心丢失消息,因为大多数消息队列都支持持久性和事务。

            我建议您查看 和/或,尤其是因为您使用 标记了此问题。

            根据您的 cmets:

            顺便说一句,您担心NOT IN 是一个性能问题并不是一个很好的问题,因为有很多变通方法,但考虑到您不想做特定于数据库的事情(如触发器和复制),我仍然感觉到一条消息队列是您的最佳选择。

            编辑 - 非 MQ 路由

            因为我让你很难问这个问题,所以我会继续努力提供帮助。 除了消息队列之外,您还可以像我们之前尝试的那样执行某种 XML 文件。您在模式中需要的关键功能是主数据库上的 CREATE TIMESTAMP 列,以便您可以在系统启动并运行时进行批处理(否则您将不得不停止系统)。现在,如果您走这条路线,您将希望SELECT * WHERE CREATE_TIME < ? 小于当前时间。基本上,您只能在快照中获取行。

            现在在您的其他数据库上删除您将在 ID 表上通过 inner joining 删除行但使用 !=(即您可以使用 JOINS 而不是慢速 NOT IN)。幸运的是,您只需要删除所有 ids 而不需要其他列。其他列您可以使用基于更新时间戳列的增量(用于更新,并创建又名插入)。

            【讨论】:

            • 您能否进一步解释一下,以便我搜索该主题?一个简单的工作流程或内容“信息”将帮助我理解您的建议。谢谢
            • 尝试查看 Spring Integration 并玩转它,然后提出一些问题。我想一旦你玩过它,你就会有一个想法。
            • 但是如果其他人从表中删除行怎么办?
            • 如果他从 master 中删除行,他很好。他只是将“DELETE”作为消息发送,它最终会进入另一个数据库。正如我在回答中所说,他甚至可以发送原始 SQL。订单保留在消息队列中。这个想法正是其他数据库通过自定义队列进行复制的方式。话虽这么说,如果他从从属服务器删除行而不从主服务器删除,他就有麻烦了。
            • 如果他不想使用触发器并且不想更新访问数据库的应用程序,那么谁会生成这些事件/消息?以及如何将它们与单个事务绑定,以免出现不一致?
            【解决方案10】:

            如果您的数据库有事务转储日志,只需发送该日志即可。

            这在 MySQL 中是可能的,在 PostgreSQL 中应该是可能的。

            【讨论】:

              猜你喜欢
              • 2011-02-02
              • 2015-09-25
              • 2013-03-16
              • 2016-11-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多