在多租户数据库体系结构中处理越来越多的租户


26

对于每个租户的应用程序实例,在具有单独数据库的公共服务器中处理少量客户(租户)相对简单,通常是正确的方法。目前,我正在研究每个租户都有自己的数据库实例的应用程序的体系结构。

但是,问题在于此应用程序将具有大量的租户(5,000-10,000),并且用户数量很多,单个租户可能为2,000。我们将需要支持每周由几个租户扩展该系统。

此外,将为所有租户及其用户提供一个通用的登录过程(即每个租户不能拥有自己的URL)。为此,我需要一个集中的登录过程以及一种将数据库动态添加到系统并注册用户的方法。

  • 如何可靠地自动化注册和数据库创建过程?

  • 是在系统上创建和注册租户数据库的过程可能会导致性能或锁定问题吗?如果您认为这可能是一个问题,那么有人可以建议减轻它的方法吗?

  • 如何以一种将用户凭据与特定租户的数据库关联但用户可以通过公共页面登录(即全部通过相同的登录URL,但他们的家庭应用程序将位于某个特定租户的数据库中)的方式管理集中身份验证)。租户必须能够维护自己的登录名和权限,但是中央登录系统必须知道这些。有人可以建议一种方法吗?

  • 如果我需要通过添加多个数据库服务器来“横向扩展”,那么有人可以建议我管理跨服务器的用户身份(模拟等)时可能要处理的问题以及减轻这些问题的某种方法吗?


1
我不必处理这样的情况,但是我的直觉是通过用尽可能多的租户数据库预配置服务器来处理租户部署,然后将预建的租户数据库分配为新租户注册。这样,您至少在部署租户数据库时不必担心资源争用。
乔尔·布朗

1
您确定您会得到接近5,000-10,000名租户的地方吗?而且您所有的租户都将在2,000个用户范围内?在我的系统中,我认为单个租户的应用程序用户最大数量约为100。而在只有20个左右的用户中,该用户始终处于活动状态。我能问一下什么行业/应用程序吗?
亚伦·伯特兰

@AaronBertrand它是一个学习管理系统,其中的服务将部分免费和部分付费。
coddey 2012年

Answers:


25

在低端(500个租户/ 10000个用户),这就是我的方法。首先,您有一个“控制”数据库,该数据库是全局的,集中的数据库,其中包含有关租户和用户的所有信息(我真的不认为您希望以SQL身份验证登录名来管理这些信息)。因此,想象一下一个名为“ Control”的数据库,其中包含以下表格:

CREATE TABLE dbo.Instances
(
  InstanceID INT PRIMARY KEY,
  Connection VARCHAR(255)
  --, ...
);

INSERT dbo.Instances SELECT 1, 'PROD1\Instance1';
INSERT dbo.Instances SELECT 1, 'PROD2\Instance1';
-- ...

CREATE TABLE dbo.Tenants
(
  TenantID INT PRIMARY KEY,
  Name NVARCHAR(255) NOT NULL UNIQUE,
  InstanceID INT -- Foreign key tells which instance this tenant's DB is on
  --, ...
);

INSERT dbo.Tenants SELECT 1, 'MyTenant', 1;
-- ...

CREATE TABLE dbo.Users
(
  UserID INT PRIMARY KEY,
  Username VARCHAR(320) NOT NULL UNIQUE,
  PasswordHash VARBINARY(64), -- because you never store plain text, right?
  TenantID INT -- foreign key
  --, ...
);

INSERT dbo.Users SELECT 1, 'foo@bar.com', 0x43..., 1;

在我们的例子中,当我们添加一个新的租户时,我们将动态地构建数据库,但是当管理员用户在UI中单击“确定”时就不会动态地构建数据库。 ,然后依次创建每个新数据库。我们这样做是为了(a)防止管理员用户等待数据库创建,以及(b)避免两个管理员用户试图同时创建数据库或以其他方式被拒绝锁定模型的能力(在创建新数据库时需要) )。

使用名称方案(Tenant000000xx在其中xx表示)创建数据库Tenants.TenantID。这使得维护工作很容易,而不是有各种命名数据库BurgerKingMcDonaldsKFC等不是因为我们是在快餐,只是使用作为一个例子。

我们没有按注释建议预先分配数千个数据库的原因是,我们的管理员用户通常对租户的规模,是否具有高优先级等有一些了解。因此,他们在UI中有一些基本选择,将决定其初始大小和自动增长设置,其数据/日志文件将进入哪个磁盘子系统,它们的恢复设置,取决于其的备份计划,甚至还可以明智地将数据库部署到哪个实例,以最佳地平衡使用率(尽管我们的管理员可以覆盖此设置)。创建数据库后,将使用所选实例更新租户表,为租户创建一个admin用户,并通过电子邮件向我们的管理员发送凭据以将其传递给新租户。

如果您使用的是单个入口点,则不允许多个租户拥有具有相同用户名的用户是不可行的。我们选择使用电子邮件地址,如果所有用户都在公司工作并使用他们的公司电子邮件地址,则可以使用该地址。尽管我们的解决方案最终变得更加复杂,原因有两个:

  1. 我们有为多个客户服务的顾问,并且需要访问多个
  2. 我们有一些房客,他们实际上实际上是由多个房客组成的

因此,我们最终得到了一个TenantUsers表,该表允许一个用户与多个租户关联。

最初,当用户登录时,应用程序将仅知道控制数据库的连接字符串。登录成功后,它便可以根据找到的信息建立连接字符串。例如

SELECT i.Connection
  FROM dbo.Instances AS i
  INNER JOIN dbo.Tenants AS t
  ON i.InstanceID = t.InstanceID
  INNER JOIN dbo.TenantUsers AS u
  ON i.TenantID = u.TenantID
  WHERE u.UserID = @UserID;

现在,该应用程序可以连接到用户的数据库(每个用户都有一个默认租户),或者用户可以从他们可以访问的任何租户中进行选择。然后,该应用程序将简单地检索新的连接字符串,并重定向到该租户的主页。

如果您建议使用这个10毫米的用户区域,则肯定需要更好地平衡它。您可能希望联合应用程序,以便它们具有连接到不同控制数据库的不同入口点。如果为每个租户提供一个子域(例如TenantName.YourApplicationDomain.com),则可以在幕后使用DNS /路由进行此操作,而无需在需要进一步扩展时中断它们。

除此之外,还有很多-像@Darin一样,我只是在这里挠表面。让我知道您是否需要免费咨询。:-)


感谢您分享您的经验。的确给我带来了启发。但是您已经写了《非自由》。:(
coddey

1
我的意思是,我只有太多时间分配给免费建议。:-)
亚伦·伯特兰

+1-与我以前使用的方法几乎完全相同。〜同样数量的租户,也表现很好。
AdaTheDev 2012年

如何处理主数据库和租户数据库之间的关系?(不使用触发器等)
Jitendra Pancholi 2014年

@jitendra没有太多选择-租户数据库中实际上需要多少数据需要与master数据库中的数据相关?我也不确定我是否理解对触发器的普遍恐惧-编写正确的触发器就没什么可担心的...
Aaron Bertrand

10

您有一个非常有趣的项目。我从来没有直接看到有人尝试至少在SQL Server上实现如此大的功能。我阅读您的文章越多,我提出的问题就越多...

从基础架构角度来看,最坏的情况(实际上从业务角度来说是最好的情况),您需要10K数据库乘以2k用户。那是20,000,000个用户。您将无法成功管理20 M SQL Server登录名。海事组织。只是它们的绝对数量,处理它们在服务器之间的移动,注意ID冲突和ID不匹配,再加上我不确定SQL Server在sys.server_principals中具有2000万行时的行为。此外,您的Web应用可能会希望以单个或很少数量的用户身份进行连接。除非它们的DSN字符串相同,否则IIS无法合并连接。DSN字符串的属性之一是用户名。不同的用户意味着没有池。

您将需要推出自己的用户凭证方案。它必须能够找出用户属于哪个租户,然后您的Web代码将需要选择适当的数据库。用户元数据至关重要,需要将其存储在某个地方,需要对其进行群集或镜像,需要快速并且需要受到很好的保护(从安全角度来看。IOW,加密。)。假设在这里SQL是一个好主意,我将使该数据库远离服务器租户的实例。从安全的角度和负载的角度来看,这都是有帮助的,尽管我猜想一旦验证了用户并将Web应用程序引导到另一个实例上的正确数据库,就不会再查询与此相关的用户元数据了。用户。

快速提问:应该允许属于两个不同租户的两个不同用户使用相同的用户名吗?

另一个快速的问题:如果我告诉你我在FuBar,Inc.工作,您怎么知道的?FuBar是要给您提供用户列表,然后又给他们返回用户名列表,还是他们要进行自我配置?

您将需要进行多实例。如果这些用户中只有一小部分决定立即运行该应用程序,则单个实例将崩溃。它没有足够的辅助线程来一次运行所有这些请求。如果只有1000个用户同时命中您的实例,则它可能会用完工作线程,并且请求将开始堆积并等待。我已经看到了这种情况;最直接的症状是新连接将无法登录到实例,因为没有可用的辅助线程来为其服务。如果这是短暂的行为,则您的应用程序可能会幸免。如果不是这样,或者您的应用程序很麻烦,用户将收到错误消息。

即使您没有太多的租户开始,您也应该开始考虑未来和自动化,因为当您看到服务器陷入困境并且有10个新的租户要上线时,这太迟了,您的服务(以及您的客户以及您即将成为的客户)将遭受苦难,直到您写出解决问题的方法。

您将需要一种从超载服务器到轻负载(或新负载)服务器移动数据库的方法。您能否获得停机时间窗口取决于您的SLA。

您是在提供特定的应用程序(例如SalesForce),还是这些数据库仅仅是租户想要放入的容器?

数据库有多大?如果它们不是很大,则可以从提供模板的备份文件中还原。(这与模型数据库的功能没有太大不同,但是自从我使用SQL 6.5以来,我还没有看到任何人真正地以良好的方式使用模型。)将模板还原为新的数据库名称后,您可以然后根据需要为特定租户自定义新数据库。显然,在拥有租户之前,您无法进行自定义。如果数据库很大,则可以遵循相同的基本过程,除了在任何新租户需要空间之前提前进行还原。您可能会保留几个这样的数据库,每个实例可能一个。如果周围有太多东西,这将迫使您购买比所需数量更多的硬件和/或存储,

如果这是您自己的应用程序,那么您将如何处理模式的更新?如果您使用的是访问Web应用程序的单个URL,那么如何使数据库的版本与代码的版本保持一致?

您如何检测和销毁不再使用的数据库?您是否要等到您的应收帐款小组说有人三个月没有付款了?

如果租户正在管理权限,则意味着他们对应用程序的内部运作有所了解,或者您的应用程序具有非常简单的角色结构。以Blogger为例,用户可以(阅读帖子),(阅读帖子并发表评论),(...创建帖子),(...并编辑其他帖子),(...并可以重置其他用户密码),或(...等)。为每组不同的权限分配一个角色,并为用户分配一个或多个角色并不难,但是您不希望您的应用运行“ GRANT”语句。当心具有层次结构并且依赖于继承的角色,这会引起混乱。如果要提拔或降级用户,我想说的是将他们从所有关联的角色中删除,然后将其重新添加到他们所需的一个角色中。哦,

我认为我只是在这里刮了一下,这篇文章已经太久了。您真正需要的是一本书,或者至少是做这本书的人的白皮书。如果他们认为这是一种竞争优势,那么大多数人不会谈论。


感谢您的评论。确实这个项目很有趣。由于单词限制,我会保持注释非常精确。这是一个学习管理系统,每个租户将拥有大约120-150张桌子。无论租户如何,用户都不会拥有相同的用户名。为了进一步降低复杂性,将使用示例tenant1.abc.com的DNS CNAME映射。现在的沸点是-以正确的方式设计它,以便它可以解决您分享的所有建议,我很担心。获得白皮书值得称赞,但可能并不容易,如果可以的话,请寻求更多投入。!!!!
coddey
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.