2012-04-14 74 views
1

我在工作空间中的数据库和工作区B. 两个数据库网上有这个数据库,总是从两个工作区更新的副本。 当其他工作区进行任何更改时,这两个工作区都可能需要更新其数据库。SQL问题与国外键

一切工作很好,但我的问题是这样的:比如有2台StockOrders,在Orders有一列这是stock_id

如果工作区A使用自动递增的“stock_id”= 23创建新的“库存X”,而工作区B使用自动递增的“stock_id”= 23创建新的“库存Y”,则工作区B将添加工作区A的“股票X“,工作区B将添加工作区B的”股票Y“,但每个数据库中都有不同的ID。

当工作空间A在stock_id = 23上作出“股票X”订单时,如果此查询发送到中央数据库,然后发送到工作空间B,它将插入订单,但stock_id = 23将提到“股票Y”。

我真的很感激一些帮助,这得益于:)

回答

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 ; 

的主要问题是,所存储的函数需要是单一语句,以便它完成并因此立即提交(否则,您将锁定它,这将导致您的事务在订单进入时互相堆叠在一起。如果y你没有很高的吞吐量,这不是一个问题。

我没有写这个功能 - 我从优从这里复制它:http://www.bigdbahead.com/?p=185这里我要提到你,了解更多详情。 (如果这个用户在这里找到我,我会很乐意让他写一个答案,并在这里给他适当的信用)。

现在,对于每一个数据库,您用不同数量的初始化值,以避免冲突。所以,对于orders表,在位置A,你会用初始化这个:

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> 

-

I H还没有对这一解决方案进行道路测试 - 将其作为我在关于序列的回答中所建议的概要。您应该在上面的博客条目链接中进行跟踪,该链接提供了一些更多细节,特别是关于如何在事务控制下进行的工作,请参阅评论(我从注释中获取函数的形式,而不是原始函数) ,当然,在负载下进行测试。

+0

即使我确定我不会遇到碰撞,至少不是在光年之前。然而,分配不同的自动增量根不会解决我的问题。 例如:结构:库存(s_id,s_name)订单(o_id,s_id,金额) 工作空间A插入一个新项目s1,它被分配一个自动增量s1.s_id = 1,然后它与o生成一个订单O. s_id = 1,当它将这两个查询发送到工作区2时,它插入库存s1,并为其分配一个自动增量s1.s_id = 100000,然后执行第二个查询“insert into Orders(s_id)values(1)” 。我认为没有合乎逻辑的解决方案 – Haz 2012-04-14 17:46:26

+0

我的歉意 - 请参阅修订后的答案。我认为在db2条款(我目前的客户),而不是mysql条款。一般来说,是的,这个问题有合乎逻辑的解决方案 - 在Wi-Fi和普及互联网连接之前的日子里,有分布式数据库并且必须稍后合并他们的数据的情况很常见。因此,有很多方案可以避免合并时的冲突。 – 2012-04-14 19:25:44

+0

是的,我很感兴趣。我会感激一个小例子,因为我有点迷路。我会很感激你对我下面想出的想法 – Haz 2012-04-15 09:32:34

2

如果工作区A和B各自有ID 23个不同的项目,那么你唯一的选择是创建时,记录每个条目次要钥匙更新到中央数据库。然后,您可以在更新这些密钥时将该密钥重新复制到工作区数据库中,这样就可以使用真正唯一的标识符访问它们的项目。

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

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

+0

您能否给我一个简单的二级密钥解决方案示例。 我会使用中央数据库,但工作区通过互联网连接,而不是局域网,他们不能承受时间滞后。至于我的数据结构,它可能就像外键例子那样简单地实现。库存(stock_id,名称等)订单(o_id,stock_id,金额等)。你会建议什么?再次感谢 – Haz 2012-04-14 17:31:32

+0

我重新思考了这个问题,这里是我可以给你的。我原来的解决方案是有两个id,id_local和id_global。在创建记录时,Id_local在本地数据库中自动分配。当记录被复制时,Id_global在全局数据库中自动分配。如果每次更新记录都更新回工作区数据库,那么这个id_global最终也会出现在这些数据库中,并且可以用来唯一标识每条记录。其他解决方案在下一评论。 – 2012-04-14 22:03:25

+0

我的另一个解决方案是在id_workspace上有一个工作区id列和一个多列键,并且是自动分配的项目id。这样,工作区1和2可以自动分配相同的值,而不会有重叠的id/pks,因为它们也可以通过工作区id列进行唯一标识。如果有可能,我真的建议你实施一个集中的数据库解决方案,因为某些事情迟早会破坏,给你留下一大堆不愉快的顾客。也就是说,如果你绝对不能这样做,那么这个想法可能会最终为你工作。 – 2012-04-14 22:08:21

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 

数据库:(EMPTY)

工作区A发送Q1 & Q2到中央,并且是在等待中央确认B已经执行的那些查询,以便它可以执行他们自己

工作区B:

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

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

数据库:(1, 'C'),(2, 'd')

工作区乙发送Q3 & Q4到中央后,通知q1和q2的,

工作区乙执行Q1 & 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')

工作区甲被通知B已经执行其q1 & q2,但它被告知必须执行q3 & Q4之前它可以执行Q1 & Q2

工作区答:

  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')

+0

我对上述的主要评论是,你会直接烘焙你的架构到你的代码,这是不是很扩展/灵活。上述两种解决方案都可以扩展到多个分布式数据库。如果其中一个数据库(包括所有合并的中央数据库)出现故障,这两者都不会受到负面影响。无论你走哪条路,我都鼓励你用这些术语来思考 - 它能工作1瓦,2分贝还是10分贝。当一个或多个数据库关闭时会发生什么。 – 2012-04-19 04:29:36