一个应用程序多个实例和不同的DB

时间:2013-12-30 14:00:22

标签: sql asp.net-mvc database-connection multi-tenant

我正在构建案例管理应用程序,其中一个要求是每个客户端都使用自己的URL获取自己的数据库。但是,升级时维护应用程序的多个实例已成为一场噩梦。我正在使用IIS 7 ASP.NET MVC。我想有一个应用程序,让应用程序知道根据用户身份验证从哪个数据库获取数据。有可行的替代方案吗?

7 个答案:

答案 0 :(得分:11)

是的,如果可能的话,最好有一个应用程序实例,否则会变得复杂。如果不同客户端的功能不同,则使用功能标志为每个客户端/用户打开/关闭功能。请参阅Martin Fowler的以下文章。功能标志可以是每个用户或客户端。 Facebook和其他几个主要网站都有效地使用功能标记。

http://martinfowler.com/bliki/FeatureToggle.html

关于数据库,我认为您可以拥有公共数据库,其中包含所有基本信息,包括所有客户端,然后特定于每个客户的其他数据库客户的数据。

当任何用户在 Global.asax Session_Start 方法中点击客户端特定网址时,添加逻辑以获取相应的连接字符串并将其存储在会话中{{1这时您可以在需要来自客户端数据库的数据时使用它。

我建议您将所有连接字符串存储在Common数据库的表中(使用标识每个客户端的键),以便在您希望加载新客户端时添加新的连接字符串行。每当您执行新版本时,我建议一起更新所有客户端数据库,而不是仅更新一个客户端数据库,否则一段时间后它将变得无法管理。

答案 1 :(得分:9)

你的问题实际上只是一个冰山一角的问题。多租户是一个复杂的主题。与租户数据库建立每个客户端连接只是一个方面。还有其他方面需要考虑:

  • 租户平衡:您如何确保比平均增长速度快得多的租户获得足够的资源不会压倒在同一存储上并置的其他租户(例如同一个实例)。你如何移动已经成长的租户?你如何汇总低活动的小租户?
  • 资源隔离:租户可以消耗大量资源(例如,它可以在数据库中运行查询,占用所有CPU并使所有其他查询匮乏),您如何确保其他租户的公平性是并置?
  • 共享数据。复制共享数据的更改(所有租户中常见的数据,例如查找表)可能有问题。
  • 架构更改。这是最棘手的话题之一。数据库架构和代码通常是同步更改的(代码需要某个架构),并且对所有租户部署架构更改可能有问题。

我建议你阅读Multi-Tenant Data Architecture白皮书。这提出了三种基本方法:

此外,我会添加SQL Azure Federations选项(在撰写白皮书时尚未提供)。本文从数据库/存储的角度讨论了这些方法的优缺点,考虑如下:

  • 存储费用
  • 安全
  • 可用性
  • 可伸缩性
  • 架构更改的简易性(应用升级)
  • 扩展

从客户端来看,我不知道任何有助于多租户案例的MVC扩展,这与Rails中的act_as_tenant gem一致。

  在升级时,

开始成为维护应用程序的多个实例的噩梦。

这实际上是多租户架构中最大的问题之一。即使你已经消除了在DB中实际升级的摩擦,它仍然是一个难以解决的问题。除非您能够承担所有租户的停机时间并使整个系统脱机,升级所有租户数据库,部署了解新架构的新代码,然后将所有租户联机,因为应用程序代码在线完成(ASP / MVC代码)必须能够同时理解这两个版本(旧版和新版)。不是不可能解决,但很难,必须仔细编码。

话虽如此,如果“消除实际升级的摩擦”,这是一个重要的部分。我没有采用什么程序来部署升级。至关重要的是,升级是自动化和脚本化的,无需任何手动干预。有时会使用基于差异的工具,例如来自Red-Gate的SQL Compare甚至是Visual Studio的vsdbcmd.exe。我最喜欢的方法是在应用程序中使用升级脚本和元数据版本控制。有关详细信息,请参阅Version Control and your Database。作为参考,这种方法基本上是Rails Migrations方法。

答案 2 :(得分:2)

您需要为每个用户(可能在用户表中?)存储连接字符串,并在创建新的数据库连接时查找此值。这意味着您不能使用web.config中的连接字符串,但我假设您知道如何使用自定义连接字符串创建新的数据库连接。

答案 3 :(得分:2)

如果我理解你,你需要为每个客户提供authentication。如果客户获得成功授权,他应该被重定向到他的网站,这意味着connection string should be changed programmatically

或者,您可以在web.config文件中定义连接字符串的数量。然后你可以使用它取决于客户授权:How to: Read Connection Strings from the Web.config File

以下是web.config设置示例:

<configuration>
    <connectionStrings> 
        <add name="Client1"
            connectionString="..." 
            providerName="System.Data.SqlClient" />
        <add name="Client2"
            connectionString="..." 
            providerName="System.Data.SqlClient" />
    </connectionStrings>
</configuration>

干杯!

答案 4 :(得分:2)

我在此看到的一个问题是,您现在将应用程序与其数据库分开。

在通常情况下,一起更新。您现在可能最终得到一个更新的应用程序和一个或多个未更新的数据库。

我认为Adarsh Shah的答案非常好,但它忽略了这一点。

我的问题是:为什么你不能合并这些数据库?

有解决方案可以实现这一目标。例如:

假设您有一个表名称Case。创建一个视图,例如Cases_V(视图中的V)。在登录时,设置会话变量CompanyId(我认为这在SQL Server中是可能的,至少在我使用的Oracle中)。

在视图中,过滤此会话变量上的表。

Cases_V的SQL看起来像这样(在oracle语法中):

select *
from   Cases
where  CompanyId = sys_context('USERENV', 'CLIENT_INFO'), 'CompanyId')

在您的应用程序中,您将使用以下视图:

select *
from   Cases_V

这可以防止用户访问其他“安装”中的信息并最大限度地减少维护。

答案 5 :(得分:1)

AdarshShah 语句非常正确,您应该在单独的MetaDatabase中维护不同数据库的连接字符串,以简化识别特定客户端的相应连接字符串的过程,特别是在多租户环境中您可能需要为客户端部署/分配专用数据库或模式,在XML文件中管理和维护连接字符串(如SQL.Config或Web.Config)可能会成为一场噩梦。

我是一个组织的一员,我们专注于构建多租户框架,可供ISV使用,开发人员可以更快地构建SaaS应用程序,而不会在分析和理解一些常见的非功能性要求时失去理智。框架的主要组成部分之一是数据分片器,它完全相同。

请查看http://www.techcello.com/products/saas-features

答案 6 :(得分:1)

我将介绍如何解决这个问题。

首先,必须在应用程序配置中包含多个连接字符串。 如果您的应用程序位于ASP.NET MVC中,我建议您使用Entity Framework进行数据库管理。 您可以在DbContext和ObjectContext类上构建包装器(我的意思是您使用包装器继承所提到的类),并使用Factory Method根据登录用户选择上下文。

我假设您知道存储用户的数据库。因此,从Global.asax开始,您将首先激活该dbContext,然后根据用户配置文件选择适当的上下文。