将数千个数据库合并为一个数据库的想法

时间:2009-03-10 21:00:56

标签: sql sql-server

我们有一个SQL服务器,每个客户端都有一个数据库,我们有数百个客户端。想象一下:database001,database002,database003,...,database999。我们希望将所有这些数据库合并到一个数据库中。

我们的想法是添加一个siteId列,001,002,003,...,999。

我们正在探索尽可能顺利完成转换的方案。我们很乐意听到您的任何想法。它被证明是一个非常具有挑战性的问题。

我听说过一种可以创建匹配然后过滤的视图的技术。

任何想法的人?

8 个答案:

答案 0 :(得分:7)

为每个客户端数据库创建客户端数据库ID。您将使用此id来保持数据在逻辑上分开。这是“站点ID”概念,但您可以使用派生密钥(标识字段)而不是手动创建这些数字。创建一个具有数据库名称和标识的表,以及您需要的任何其他元数据。

下一步是创建一个SSIS包,该包获取相关数据库的ID,并将其添加到必须将其数据逻辑分离的表中。然后,您可以在每个数据库上运行相同的包,并查找相关数据库的ID。

如果您拥有唯一且唯一且已导入数据的ID,则必须更改您的应用以适应新架构(实际上之前,或者您几乎搞砸了)。

如果要逐步执行此操作,可以在不同的“数据库”中创建视图或函数,以便旧客户端仍然可以访问客户端的数据,即使它已被移动。如果部署一些停机时间,则可能不需要此步骤。

我建议的方法相当灵活,可以一次应用于一个客户端,具体取决于您的客户端应用程序部署方法。

答案 1 :(得分:4)

为什么要这样做?
您可以阅读有关Multi-Tenant Data Architecture的内容,并聆听有关此设计的SO #19 (around 40-50 min)

答案 2 :(得分:3)

“site-id”解决方案已经完成了。

另一种可能无法解决的可能性(但仍然很吸引人)是单个数据库中的多个模式。您可以将公用表拉入“通用”模式,并将客户特定的内容保留在客户特定的模式中。但是,在某些数据库产品中,每个模式 - 实际上 - 是一个单独的数据库。在其他产品(例如Oracle,DB2)中,您可以轻松编写在多个模式中工作的查询。

另请注意 - 作为优化 - 您可能不需要将siteId列添加到每个表中。

有时你会有“包含”关系。它是一个主要细节FK,通常使用级联删除定义,以便在没有父级的情况下不能存在细节。在这种情况下,孩子们不需要siteId,因为他们没有独立的存在。

答案 3 :(得分:3)

您的第一步是确定这些数据库是否具有相同的结构。即使你认为他们这样做,你也需要比较它们以确保它们能够做到。可能会有一些定制或错过一两个升级周期。

现在,根据客户端数量和每个客户端的记录数量,您的表格可能会变得很大。您确定这不会产生性能问题吗?无论如何,您可能需要重新审视索引。您可能需要一组功能更强大的服务器,并且可能还需要客户端分享以提高性能。

接下来,是的,每个表都需要某种类型的站点ID。此外,根据您的设计,您可能拥有现在不再唯一的主键。您可能需要重新定义所有主键以包含siteid。添加时始终为此字段编制索引。

现在需要重写所有查询,存储过程,视图和udfs,以确保siteid是其中的一部分。特别注意任何动态SQL。否则,您可能会向客户B显示客户A的信息。客户不喜欢这样。我们将客户从一个单独的数据库带到主应用程序一次(当他们决定他们不想再为单独的服务器付费时)。开发人员错过了一个必须添加client_id的地方。不幸的是,这会向每个客户发送有关此客户专有信息的电子邮件,并且更糟糕的是,这是一个在半夜运行的夜间过程,因此直到第二天才知道。 (开发人员非常幸运,不会被解雇。)当你这样做并进行测试,测试,测试和测试时,要非常小心。确保在幕后测试所有自动化内容以及UI内容。

答案 4 :(得分:3)

我在去年年底在佛罗伦萨解释的是,如果你必须保持数据库名称和数据库的逻辑层与应用程序相同。在这种情况下,您将执行以下操作:

  • 将合并表中的所有数据折叠到一个主统一数据库(以下称为合并数据库)中。
  • 这些表必须具有SiteID等标识符。
  • 使用现有名称创建新数据库。
  • 使用旧表名创建视图,这些视图使用行级安全性来查询统一数据库中的表,但使用SiteID进行过滤。
  • 设置跨数据库所有权链接的数据库,以便服务帐户不会“意外”查询统一数据库中的基表。访问必须通过视图或存储过程以及将强制执行行级安全性的其他构造进行。现在,如果它是所有站点的相同服务帐户,则可以避免跨数据库所有权链接并为统一数据库中的对象分配权限。
  • 重写存储过程以处理更改(因为它们现在引用视图,并且它们不知道命中基表并包含SiteID)或在视图上使用RatherOf触发器来拦截更新请求并放置相应的站点特定信息到基表中。

答案 5 :(得分:1)

如果数据很大,您可以查看使用分区视图。这将简化您的访问代码,因为您需要维护的只是视图;但是,如果数据不大,只需添加一列来标识客户。

答案 6 :(得分:1)

根据数据和您的安全要求,交叉污染的威胁可能是一个显示阻止。

假设您已经考虑过这一点,并认为它“足够安全”。您可能需要/想要创建VIEWS或强加一些其他访问控制以防止客户看到彼此的数据。

IIRC一个名为“Trusted Oracle”的产品能够根据这样的密钥对数据进行分区(大约是Oracle 7或8发布的时间)。这个想法是任何给定的查询都会自动附加“和sourceKey = @userSecurityKey”(或某些此类)。该功能可能已被纳入流行商业产品的更高版本。

答案 7 :(得分:0)

为了扩展Gregory的答案,你还可以创建一个父ssis来调用在foreach循环容器中实际移动的包。

父包查询配置表并将其放入对象变量中。然后,foreach循环使用此记录集将变量传递给包,例如数据库名称和包可能需要的任何其他详细信息。

您可以列出所有客户端数据库,并在准备好移动它们时标记一个标记。这样您就不会在32,767数据库上运行ssis包。我迷上了ssis的foreach循环。