【问题标题】:Writing into multiple MySQL databases async异步写入多个 MySQL 数据库
【发布时间】:2012-12-26 07:12:22
【问题描述】:

我使用的是 AWS RDS,因此区域之间的数据库复制是不可能的。

我的应用程序用 PHP 编写并部署在所有地区,我正在寻找一种快速可靠的方法来实现这一目标。

我要建立 MySQL 连接:

SET @@auto_increment_increment= NUM​​BER_OF_WRITEABLE_DATABASES;

SET @@auto_increment_offset = REGION_ID ;

所以 AI pk 将在所有地区都是独一无二的。

我目前的计划是保留一个包含字段 => id、查询、状态、user_id 的查询日志表。它将所有插入、更新、删除查询记录到同一页面加载的查询字段中。

状态代码:

状态 0 => 未执行

状态 1 => 已在所有区域成功执行

状态 2 => 失败

状态 3 => 失败,受影响的行不匹配

示例行:

id=>1 查询=> 插入配置文件值(1,{USER_ID},'用户名','电子邮件')##SEPERATOR##AFFECTED_COUNT 更新用户设置 last_modified='2012-12...' 其中 id={USER_ID}##SEPERATOR##AFFECTED_COUNT 状态=0 user_id=>{USER_ID}

并且会有一个守护进程读取 status != 1 的记录,并将在所有没有提交的区域上处理它们,一旦全部运行没有错误,它将提交或回滚以防出错。

这就是我的想法和将要使用的。

我的问题是该场景是否有更体面/经过测试的方法,或者我的方法有任何问题。

提前致谢

【问题讨论】:

    标签: mysql amazon-web-services load-balancing multi-master-replication


    【解决方案1】:

    我最初的想法是,如果您尝试使用 RDS 作为跨多个区域强制执行唯一记录 ID 的解决方案,那么您将走错路。我认为您可能需要重新考虑您对跨区域唯一性的实际需求或使用多个列(即自动增量加上区域标识符)强制唯一性。这可以被读取并放入一些最终一致的数据存储中以用于读取目的。

    【讨论】:

    • 我同意。如果真的需要跨地域复制,这里RDS是错误的选择。举个例子:如果 OP 需要添加另一个区域,这个策略就会有问题。
    • 我同意 Mike 的观点——如果你自己实现完整的数据库复制——你做错了。不过,您的自动增量技巧很不错...
    【解决方案2】:

    您做出了值得称赞的努力,但正如其他评论者所说,您的解决方案不可行,原因有很多。

    您并不想在会话级别使用 auto_increment_offset 和 auto_increment_increment。您想在服务器级别设置它们。如果 RDS 不允许您这样做,这就是 RDS 可能不是最佳解决方案的另一个原因。

    如果我出来建议您在多主环中部署 MySQL 服务器(EC2,而不是 RDS)的全球网络,其中数据复制 1 => 2 => 3 => 4 => 1 和每个服务器忽略带有自己服务器 ID 的传入复制消息,我的 MySQL DBA 同事会指责我失去了理智,让你陷入了难以管理的境地; 然而,我相信这将是一个比你提出的更容易的解决方案,因为至少,那么,世界各地的数据将在相当多的时间内发生变化它实际更改的顺序相同 - 这将减少来自多个位置的冲突更新的可能性。 MySQL 复制是异步的,因为服务器 1 在向客户端返回成功(表明事务已提交)之前不会等待服务器 2 上的事务提交,但不要将该事实与它的事实混淆顺序的——事务按照提交的顺序在每台服务器上复制。 (MySQL 5.6 中的新选项允许通过并行复制线程对此进行一些例外处理,但这对本次讨论并不重要)。

    由于您已经设计了一种方案来避免冲突的自动增量值,您的更大问题可能来自更新和删除。在我刚刚描述的场景中,如果服务器 2 删除了一条记录,而服务器 4 同时删除了同一条记录,那么服务器 4 将在收到来自服务器 2 的删除时停止复制传入事件,因为“受影响的行”将有所不同。您的方案同样会失败。不同之处在于,使用实际的 MySQL 复制,在冲突事件发生后什么也没有发生,所以在你解决冲突之前,至少你的数据不会因为上面讨论的顺序性质和 MySQL 复制 遇到冲突时完全停止。在主服务器环中,已停止复制的服务器继续从上游系统收集复制事件日志,但执行停止并且该服务器上的数据被冻结,除非在本地更改,直到冲突解决并重新启动复制。

    另请注意,在您的方案中,您需要在更新时为每一列保留“from”和“to”值,因为除非您知道它回滚到,否则您无法回滚任何内容。

    需要注意的是,回滚需要实时发生,而不是稍后发生。如果我在两个银行账户之间转账,并且由于某种原因需要回滚转账,我需要在使用银行网站时看到这一点——银行不能在中间回滚该交易晚上只是因为他们的一台服务器在我的银行账户中有不同的余额。

    这里有一个想法:在你的场景中,我转移“到”的帐户在所有服务器之间是一致的,但我转移“从”的帐户不是,那么我想知道......你的设置会回滚从“from”账户提款,但将存款留在“to”账户?我认为可能。

    请记住,您受到CAP theorem 的限制。没有一个系统可以是全局一致的、可用的,并且可以容忍节点之间的隔离。充其量,您可以选择任意两个。

    有了这个想法,我的问题是:为什么全局系统中的所有节点都需要同步?如果主要原因是性能,请考虑部署单个全局主服务器的可能性,其中只读副本分布在各个区域之间。使用两个数据库连接线程池编写应用程序,以便大多数 SELECT 查询转到本地只读副本,而 INSERTDELETEUPDATECALL(更新数据的存储过程)是发送到全局主服务器。那么,您最大的担心变成了只读副本上只有 eventual consistency 的事实。使用适当大小的服务器和编写良好的查询,这是非常快的(受制于光学和电信号全球传播的物理定律),但它不是瞬时的。你必须做的是对于最近对数据库进行更改的会话,它们的读取可能需要命中全局主控——如果你下订单,你需要立即查看订单,所以主控可能是最好的地方,马上。稍后,查看本地副本将起作用。由于跨区域问题,您仍然超出 RDS 的范围......但 EC2 上的 MySQL 非常适合。

    只读副本对主服务器施加的负载非常小,但即使是这种负载,也可以通过将单个只读副本连接到主服务器,然后将下游只读副本连接到该中间服务器来减轻这种负载。

    在主服务器和副本服务器上设置slave_compressed_protocol = 1 将使机器能够使用压缩连接来传输复制事件。我发现这个比例在 3:1 到 10:1 之间,具体取决于被复制数据的性质以及压缩和解压缩数据的延迟似乎微不足道。

    此外,您可以设置第二个主节点,与主主节点相邻(可能在不同的 A/Z 中),通过主主复制链接这两个服务器,将只读副本链接到第二个主节点,使用自动增量适当地递增和偏移,但在正常情况下不要写入或读取到第二个主机。你为什么要这样做?这样,您就有了第二个全局主节点,如果主主节点发生故障,您可以通过重定向您的应用程序访问它来立即投入使用。

    当然,应用程序的性质在实际需要多少全局集成方面起着重要作用。解决此问题需要您重新考虑应用程序的工作方式,以确定是否需要更改架构。

    作为一名 DBA,我不喜欢 RDS 强加给我的一些限制和灵活性约束。作为失去控制的回报,我真正得到的只是相对容易的备份和时间点恢复……我喜欢……但是,对我来说,这些并不能弥补限制。


    脚注:在第 3 段中,我说过“事务按照提交的顺序在每个服务器上复制”。但这并不一定意味着在现实世界的挂钟实际顺序中它们被提交......它实际上意味着它们被提交到每个服务器相对于该服务器提交的其他事务的顺序。 .. 因此,在服务器 #3 上的不同事务之前实际提交的服务器 #1 上的事务可能会在来自 #3 的事务之后而不是之前到达服务器 #4,具体取决于事务通过服务器 #2 传播所需的时间并在服务器 #3 上提交。然而,这在原则上仍然“足够真实”,因为如果 #1 上的事务在服务器 #3 上被认为与 #3 上发生的任何事情发生冲突,它实际上不会复制到 #4,因为 #3 将停止复制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-28
      • 1970-01-01
      • 2018-10-03
      • 2015-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多