cod*_*dey 26 database-design sql-server
在公共服务器中为每个租户的应用程序实例使用单独的数据库处理数量适中的客户(租户)相对简单,并且通常是正确的方法。目前我正在研究一个应用程序的架构,其中每个租户都有自己的数据库实例。
但是,问题是该应用程序将拥有大量租户(5,000-10,000)和大量用户,单个租户可能有 2,000 个用户。我们需要支持每周由几个租户扩展系统。
此外,所有租户及其用户都将看到一个通用的登录过程(即每个租户不能拥有自己的 URL)。为此,我需要一个集中的登录过程和一种将数据库动态添加到系统并注册用户的方法。
如何稳健地自动化注册和数据库创建过程?
在系统上创建和注册租户数据库的过程是否可能导致性能或锁定问题。如果您认为这可能是一个问题,有人可以建议减轻它的方法吗?
如何以用户凭据与特定租户数据库相关联的方式管理中央身份验证,但用户可以通过公共页面登录(即全部通过相同的登录 URL,但他们的主应用程序将位于某些特定租户的数据库上) )。租户必须能够维护自己的登录名和权限,但中央登录系统必须了解这些。谁能建议一种方法来做到这一点?
如果我需要通过添加多个数据库服务器来“向外扩展”,谁能建议我在跨服务器管理用户身份(模拟等)时可能需要处理哪些问题以及缓解这些问题的某种方法?
Aar*_*and 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;
Run Code Online (Sandbox Code Playgroud)
在我们的例子中,当我们添加一个新租户时,我们将动态构建数据库,但当管理员用户在 UI 中单击“确定”时不会……我们有一个后台作业,每 5 分钟从队列中拉出新数据库,将模型设置为 single_user ,然后依次创建每个新数据库。我们这样做是为了 (a) 防止管理员用户等待数据库创建和 (b) 避免两个管理员用户同时尝试创建数据库或以其他方式被拒绝锁定模型的能力(创建新数据库时需要) )。
数据库用的名称方案创建Tenant000000xx,其中xx代表Tenants.TenantID。这使得维护工作很容易,而不是有各种命名数据库BurgerKing,McDonalds,KFC等不是因为我们是在快餐,只是使用作为一个例子。
我们没有像评论建议的那样预先分配数千个数据库的原因是我们的管理员用户通常对租户的大小有一些想法,他们是否具有高优先级等。所以他们在 UI 中有基本的选择将决定他们的初始大小和自动增长设置,他们的数据/日志文件将转到哪个磁盘子系统,他们的恢复设置,备份计划,甚至关于将数据库部署到哪个实例以最好地平衡使用的智能(虽然我们的管理员可以覆盖这一点)。创建数据库后,租户表将更新为所选实例,为租户创建管理员用户,我们的管理员通过电子邮件发送凭据以传递给新租户。
如果您使用单一入口点,则不允许多个租户拥有具有相同用户名的用户是不可行的。我们选择使用电子邮件地址 - 如果所有用户都为公司工作并使用他们的公司电子邮件地址 - 应该没问题。虽然我们的解决方案最终因为两个原因变得更加复杂:
因此,我们最终得到了一个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;
Run Code Online (Sandbox Code Playgroud)
现在应用程序可以连接到用户的数据库(每个用户都有一个默认租户),或者用户可以从他们可以访问的任何租户中进行选择。然后,应用程序将简单地检索新的连接字符串,并重定向到该租户的主页。
如果你进入你建议的这个 10MM 用户区,你肯定需要更好地平衡。您可能希望联合应用程序,以便它们具有连接到不同控制数据库的不同入口点。如果您为每个租户提供一个子域(例如 TenantName.YourApplicationDomain.com),那么您可以使用 DNS/路由在后台执行此操作,而无需在需要进一步扩展时中断它们。
这还有很多——比如@Darin,我只是在这里触及皮毛。如果您需要非免费咨询,请告诉我。:-)
Dar*_*ait 10
你有一个非常有趣的项目。我从来没有直接看到有人尝试实现这么大的东西,至少在 SQL Server 上是这样。我越看你的帖子,我想出的问题就越多......
最坏的情况是基础架构方面(这实际上是最好的情况,业务方面),您需要 10K 数据库乘以 2k 用户。那是 20,000,000 名用户。尝试管理 20 M SQL Server 登录是不会成功的。海事组织。只是它们的绝对数量,处理将它们从服务器移动到服务器,注意 ID 冲突和不匹配的 ID,另外我不确定 SQL Server 将如何处理 sys.server_principals 中的 20 M 行。此外,您的 Web 应用程序可能希望作为单个或非常少的用户进行连接。除非它们的 DSN 字符串相同,否则 IIS 不能汇集连接。DSN 字符串的属性之一是用户名。不同的用户意味着没有池化。
您将需要推出自己的用户凭据方案。它必须能够确定用户属于哪个租户,然后您的 Web 代码将需要选择正确的数据库。用户元数据很重要,它需要存储在某个地方,需要集群或镜像,需要快速并且需要得到很好的保护(从安全角度来看。IOW,加密它。)。假设 SQL 在这里甚至是一个好主意,我会让这个数据库远离服务器租户的实例。从安全角度和负载角度来看,这都有帮助,但我猜想,一旦用户得到验证并且 Web 应用程序被引导到另一个实例上的正确数据库,就不会再查询与此相关的用户元数据用户。
快速问题:是否应该允许属于两个不同租户的两个不同用户具有相同的用户名?
另一个简单的问题:如果我告诉你我在 FuBar, Inc. 工作,你怎么知道的?FuBar 是要给你一个用户列表,然后你再给他们一个用户名列表,还是他们要自行配置?
你将需要去多实例。即使这些用户中的一小部分决定立即点击应用程序,单个实例也会消失。它没有足够的工作线程来一次运行所有这些请求。如果只有 1000 个用户同时访问您的实例,它可能会耗尽工作线程并且请求将开始堆积并等待。我见过这种情况;最直接的症状是新连接将无法登录到实例,因为没有可用的工作线程来为它们提供服务。如果这是非常短暂的行为,您的应用程序可能会存活下来。如果没有,或者您的应用程序很挑剔,用户将收到错误消息。
即使您没有很多租户要启动,您也应该开始考虑未来和自动化,因为当您看到您的服务器陷入困境并且有 10 个新租户上线时,为时已晚,您的服务(和您的客户,以及您即将成为前客户)将受苦,直到您写出解决问题的方法。
您将需要一种方法来移动数据库,从过载的服务器到负载较轻的(或新的)服务器。您是否可以获得停机时间窗口取决于您的 SLA。
您提供的是特定的应用程序,例如 SalesForce,还是这些数据库只是租户想要放入的任何内容的容器?
数据库有多大?如果它们不是很大,您可以从提供模板的备份文件中恢复。(这与模型数据库所做的没有太大不同,但自从我使用 SQL 6.5 以来,我还没有看到任何人真正以好的方式使用模型。)一旦模板恢复到新的数据库名称,您就可以然后根据需要为特定租户自定义新数据库。显然,您无法在拥有租户之前进行自定义。如果数据库很大,除了在任何新租户需要空间之前提前进行恢复之外,您可以遵循相同的基本过程。您可能会保留几个这样的数据库,每个实例可能一个。如果你有太多的东西,这将迫使你购买比你需要的更多的硬件和/或存储,
如果这是您自己的应用程序,您将如何处理架构更新?如果您使用访问 Web 应用程序的单个 URL,您将如何使数据库版本与代码版本保持一致?
您如何检测和销毁不再使用的数据库?你会等到你的应收账款小组说有人已经三个月没有支付账单了吗?
If tenants are managing permissions, that implies that they have some understanding of the inner workings of the app, or that your app has a very simple role structure. Using something like Blogger as a rough example, users can (read posts), (read posts and make comments), (... and create posts), (... and edit other's posts), (... and can reset other users passwords), or (... and whatever). Having a role for each of those different sets of rights and assigning a user to one role or another shouldn't be too hard, but you don't want your app running 'GRANT' statements. Watch out for roles that have a hierarchy and depend on inheritance, it can get confusing. If you are promoting or demoting a user, I'd say pull them out of all of the associated roles and then add them back to the one role that they need. Oh, and since you probably can't use SQL's native credential scheme, you are going to have roll your own code here.
我想我只是在这里触及了皮毛,这篇文章已经太长了。你真正需要的是一本书,或者至少是一个做过这件事的人的白皮书。如果这些人认为这是一种竞争优势,他们中的大多数人就不会说话。
| 归档时间: |
|
| 查看次数: |
8162 次 |
| 最近记录: |