【问题标题】:Avoiding collisions on primary keys from separate MySQL databases避免来自不同 MySQL 数据库的主键冲突
【发布时间】:2012-11-24 12:17:16
【问题描述】:

我有几台服务器运行自己的特定MySQL 数据库实例,遗憾的是无法在复制/集群中设置。每个服务器将数据插入到几个与用户相关的表中,这些表之间有外键约束(例如useruser_vote)。流程如下:

  1. 所有服务器都以相同的数据开始
  2. 每台服务器独立于其他服务器增长自己的数据集
  3. 定期手动将来自所有服务器的数据合并在一起并应用回每个服务器(因此该过程会从步骤 1 开始重复)。

这之所以成为可能,是因为除了其主键之外,user 表还包含一个唯一的 email 字段,该字段允许识别每个数据库中已经存在的用户,并在更改主键时合并新用户和外键以避免冲突并保持正确的外键约束。它可以工作,但需要付出很大的努力,因为必须更改主键和外键以避免冲突,因此我的问题是:

有没有办法让每个服务器使用不与其他服务器冲突的主键来促进合并?

我最初想使用复合主键(例如server_idid),但我使用的是Doctrine which doesn't support primary keys composed of multiple foreign keys,所以我的外键约束会出现问题。

我考虑过使用VARCHAR 作为id 并使用部分字符串作为前缀(SERVER1-1、SERVER1-2、SERVER2-1、SERVER2-2...),但我在想它会使数据库变慢,因为我必须对 id 进行一些操作(例如,在插入时,我必须解析现有的 id 并提取最高值,增加它,将它与服务器 id 连接......)。

PS:另一种选择是通过从从属设备读取和写入到主控设备来实现复制,但由于无法解决主控设备上的复制滞后和单点故障等问题,该选项被丢弃现在。

【问题讨论】:

    标签: php mysql doctrine doctrine-orm


    【解决方案1】:

    您可以确保每个服务器使用不同的自动增量增量和不同的起始偏移量:

    Change the step auto_increment fields increment by

    (假设您正在使用自动增量)

    我只在两台服务器上使用过这个,所以我的设置是一个带有偶数 ID,一个带有奇数。

    当它们重新合并在一起时,只要您确保所有表都遵循上述想法,就不会发生冲突。

    为了实现 4 台服务器

    你会说,设置以下偏移量:

    • 服务器 1 = 1
    • 服务器 2 = 2
    • 服务器 3 = 3
    • 服务器 4 = 4

    您可以这样设置增量(我使用 10 为额外的服务器留出空间):

    • 服务器 1 = 10
    • 服务器 2 = 10
    • 服务器 3 = 10
    • 服务器 4 = 10

    然后在合并之后,在复制回每个服务器之前,您只需要更新每个表的 autoinc 值以再次获得正确的偏移量。假设每个服务器创建了 100 行,autoincs 将是:

    • 服务器 1 = 1001
    • 服务器 2 = 1002
    • 服务器 3 = 1003
    • 服务器 4 = 1004

    这是因为有四台服务器而变得棘手的地方。假设某些表可能没有从特定服务器插入任何行。因此,您最终可能会得到一些表的最后一个 autoinc id 不是来自服务器 4,而是来自服务器 2。这使得计算任何特定表的下一个 autoinc 应该是什么变得非常棘手。

    因此,最好在每个表中还包含一列,用于记录插入任何行时的服务器编号。

    id | field1 | field2 | ... | server
    

    这样,您可以通过在任何表上选择以下选项轻松找出特定服务器的最后一个 autoinc 值:

    SELECT MAX(id) FROM `table` WHERE `server`=4 LIMIT 0,1
    

    使用此值,您可以为每台服务器上的每个表重置下一个 autoinc 值,然后再将合并的数据集推送到相关服务器。

    UPDATE information_schema.tables SET Auto_increment = (
      SELECT MAX(id) FROM `table` WHERE `server`=s LIMIT 0,1
    )+n WHERE table_name='table' AND table_schema = DATABASE();
    

    其中s 是服务器编号,n 设置为偏移量,因此在我的示例中为10

    【讨论】:

    • 我可以清楚地看到它与 2 台服务器一起工作,但我在想我应该如何使用 4 台服务器:如果我使用 1 作为所有服务器的步骤,但使用不同的 @987654329每台服务器上的 @ 值足够分开以给我增长空间(例如,100000000 用于服务器 1,200000000 用于服务器 2..),这行得通吗?
    • 我在想,尤其是在合并数据之后,服务器 1、2 和 3 不会从来自服务器 4 的最高 id 递增吗?
    • @user359650,通过按质数自动递增,例如 7 或 13,应该有助于每个服务器从下一个序列(如 1、2、3、4)开始,然后它们对应的下一个数字将是每台服务器 8、9、10、11 个
    • 不需要素数:step 必须等于服务器的数量。从1,2,3,4 开始并使用4 作为一个步骤:下一个序列是5,6,7,8 - 9,10,11,12 - 13,14,15,16 等...
    • @user359650 我已经用希望有用的信息更新了我的答案。
    【解决方案2】:

    前缀 ID 可以解决问题。至于数据库速度较慢 - 取决于那里提供的流量有多大。您还可以将“prefixed id”分成两列,“prefix”和“id”,它们可以是任何类型。在请求中需要一些逻辑来处理它,但可能值得评估

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      • 1970-01-01
      • 2020-04-07
      • 1970-01-01
      相关资源
      最近更新 更多