使用外键合并两个数据库时的SQL问题

时间:2012-04-14 15:50:10

标签: php mysql sql foreign-keys merging-data

我在工作区A和工作区B有一个数据库。 在线有一个这个数据库的副本,它总是从两个工作区更新。 每当其他工作空间进行任何更改时,两个工作空间都需要更新其数据库。

一切运作良好但我的问题是这样的:例如,有两个表StockOrdersOrders中有一列是stock_id

如果工作区A创建一个新的“Stock X”,其自动递增“stock_id”= 23,工作区B创建一个新的“Stock Y”,其自动递增“stock_id”= 23,工作区B将添加工作区A的“Stock” X“和工作区B将添加工作区B的”Stock Y“,但每个数据库中的每个数据库都有不同的ID。

当工作区A在stock_id = 23上发出订单时出现问题,即“Stock X”,当此查询发送到中央数据库,然后发送到工作区B时,它会插入订单但stock_id = 23将请参阅“库存Y”。

我非常感谢这一点的帮助:)

3 个答案:

答案 0 :(得分:2)

如果工作空间A和B每个都有不同的ID 23条目,那么您唯一的选择是在将记录更新到中央数据库时为每个条目创建一个辅助密钥。然后,当您更新它们时,可以将此密钥重新复制回工作区数据库,这将允许使用真正唯一的标识符访问它们的项目。

但我强烈建议你不要使用这种方法!

正确的做法是创建工作区1和2使用的Web或桌面应用程序,它们将连接到中央数据库并用于所有数据访问。良好的数据库设计通常意味着拥有尽可能少的重复数据副本。通过在三个不同的数据库中使用多个订单,库存等副本,您将在未来设置数据损坏和/或丢失 - 这个问题随着数据库的增长而变得越来越难以修复尺寸。现在在数据库变大之前修复结构问题!

答案 1 :(得分:2)

编辑:

您的原始设计使用AUTO INCREMENT列作为主键。这样做的问题是,当您看到数据同时进入最终将合并的单独数据库时,您最终会创建重复键。

另一种方法是在每个不同的数据库中使用序列。不幸的是,序列在mysql中本身不可用(许多其他数据库,如Oracle,db2确实有它们 - 它们允许这种类型的分布式数据库插入没有冲突)。 AUTO INCREMENT列不允许您进行所需的复制。

所以,你有两个选择。

1)添加设置location_id的主键的额外部分(根据第一个答案)。

2)或者使用您自己的序列手动为您的插入生成您的ID,而不是使用AUTO INCREMENT列。

最好将序列实现为存储过程/函数,当它获取序列中的下一个值时自我提交 - 这可能最终导致未使用的值。没关系 - 如果您要等待提交序列号直到提交整个插件,那么它比争用更好。

主要的是,当您执行第一次插入时,您使用存储过程中的序列#。当您有效地将数据复制到第二个数据库时,可以在原始数据库的行中使用生成的序列#。并且序列将在每个单独的数据库中保持不同的起始点以防止冲突。

例如,在每个数据库中,您需要两个部分:

1)一个表,其中包含每个命名序列的下一个可用序列号。 (从序列中获取主键的每个表都会获得一个条目)。 2)一个函数,用于访问和更新具有下一个序列号的表。

示例实现如下:

序列表:

CREATE TABLE sequences (
  name varchar(30) NOT NULL,
  value int(10) unsigned,
  PRIMARY KEY (name)
) ENGINE=InnoDB

序列功能:

delimiter //
create function get_next_value(p_name varchar(30)) returns int
  deterministic
  sql security invoker
begin
  declare current_val integer;
    UPDATE sequences
    SET value = (@current_val:=value) + 1
    WHERE name = p_name;

  return @current_val;
end //
delimiter ;

主要问题是存储的函数需要是一个单独的语句,因此它会完成并因此立即提交(否则,您将对其进行锁定,这将导致您的事务堆叠在一起当订单进来时。如果你没有非常高的吞吐量,这不是一个问题。

我没有写这个功能 - 我可以从这里自由地复制它:http://www.bigdbahead.com/?p=185我会引用你的地方,以获取更多细节。 (如果该用户曾在我这里找到我,我很乐意让他写一个答案并在这里给他相应的信用。)

现在,对于每个数据库,使用不同的数字初始化值以避免冲突。因此,对于位置A中的orders表格,您可以使用以下内容对其进行初始化:

insert into sequences ('orders', 1);

在位置B,您可以使用以下内容对其进行初始化:

insert into sequences ('orders', 1000000);

然后在两个数据库中,在插入orders时,你会这样做:

insert into orders (order_id, . . .)
select mysql.get_next_value('user_id'), . . . <hardcoded-values>

-

我还没有通过公路测试这个解决方案 - 把它作为我在关于序列的答案中建议的概述。您应该对上面的博客条目链接进行跟进,其中提供了一些更多详细信息,特别是关于如何在事务控制下进行此项工作,请参阅注释(我在评论中采用了函数的形式,而不是原始的功能),当然,在负载下测试它。

答案 2 :(得分:0)

我想到了这个答案,但我仍然感到困惑,如果它比上面的其他解决方案更好。 工作空间B中的所有查询都在本地数据库上执行,并且还发送到中央数据库,然后发送到工作空间A,但是工作空间A上的查询不是在本地数据库上执行,而是发送到中央数据库然后中央数据库将它们发送到工作空间B,当工作空间B执行这些查询时,它通知中心,然后通知工作空间A,然后它可以执行存储的查询。因此,允许工作空间B正常执行其查询,但工作空间A仅在它知道工作空间B何时执行了那些查询时才允许执行其查询,因为在工作空间B执行A的查询之前,它发送自己的新查询查询,然后执行A的查询,因此在A通知B执行其查询后,它检查B是否发送了自己的任何新查询并执行它们然后执行自己的查询。这样,两个工作空间中所有自动增量的id都是相同的。例如:

工作区A:

         q1= insert into stock (name) values ('A')    not executed

         q2= insert into stock (name) values ('B')    not executed

A:(EMPTY)数据库

工作区A发送q1&amp; q2到central并且正在等待中心确认B已经执行了这些查询,因此它可以自己执行

工作区B:

         q3= insert into stock (name) values ('C')    executed id=1

         q4= insert into stock (name) values ('D')    executed id=2

B的数据库:(1,'C'),(2,'D')

发送q3&amp;发送后的

工作区B. q4到central,它被通知q1和q2,

工作区B执行q1&amp; q2

          q1= insert into stock (name) values ('A')   executed id=3

          q2= insert into stock (name) values ('B')   executed id=4

数据库B:(1,'C'),(2,'D'),(3,'A'),(4,'B')

工作区A被通知B已执行其q1&amp; q2,但它被告知必须执行q3&amp; q4才能执行q1&amp; Q2

工作区A:

          q3= insert into stock (name) values ('C')    executed id=1

          q4= insert into stock (name) values ('D')    executed id=2

          q1= insert into stock (name) values ('A')    executed id=3

          q2= insert into stock (name) values ('B')    executed id=4

B的数据库:(1,'C'),(2,'D'),(3,'A'),(4,'B')