具有 SQL Server 2016、Shard 的多租户系统是否应该通过每个租户的单独数据库进行租户隔离?

D.S*_*.S. 13 database-design sql-server scalability multi-tenant sharding

鉴于用例:

  • 租户数据不应串扰,一个租户不需要另一个租户的数据。
  • 每个租户都可能拥有大量的历史数据。
  • SQL Server 托管在 AWS EC2 实例中。
  • 每个租户在地理上都很遥远。
  • 有意向使用第三方可视化工具,如 PowerBI Embedded
  • 数据量预计会随着时间的推移而增长
  • 系统的成本受到限制。
  • 解决方案必须是可维护的,无需 24/7 全天候生产 DBA
  • 该解决方案应该能够水平扩展。
  • 租户总数少于50人

推荐的架构是什么,此用例是否有任何参考实现?我相信很多人可能已经在企业软件开发中遇到过这个问题。

我认为这与在多租户数据库架构中处理越来越多的租户不同。该问题中提到的用例涉及更多租户,这与拥有很少 (50) 个大租户有很大不同。提到的架构可能是这里的一个解决方案,这也是我想了解的更多信息。

Bre*_*zar 17

分片的问题在于应用程序必须知道要查询哪个分片。通常,这是通过对客户端之类的东西进行分片来完成的。我将改编我的一篇旧博客文章作为我的答案。

当您为大量客户端构建应用程序时,有两种常见的数据库设计方法:

  • 选项 A:将所有客户端放在同一个数据库中
  • 选项 2:为每个客户端构建一个数据库

将所有客户端放在同一个数据库中

很简单:只需在架构顶部添加一个 Client 表,添加一个 ClientUsers 表以确保人们只能看到他们自己的数据,然后我们就可以了。

这种方法的好处:

更轻松的架构管理。当开发人员部署新版本的应用程序时,他们只需在一个数据库中进行架构更改。不用担心不同的客户不同步或使用错误的版本。

更容易的性能调优。我们可以在一个地方检查索引使用情况和统计​​数据,轻松实施改进,并立即查看所有客户的效果。对于成百上千的数据库,即使是最小的更改也很难协调。我们可以检查我们的过程缓存内容,并确定哪些查询或存储过程在我们的整个应用程序中是最密集的,而如果我们为每个客户端使用单独的数据库,我们可能会在不同的执行计划中聚合查询使用更困难。

更容易构建外部 API。如果我们需要授予外部人员访问我们整个数据库的权限以构建产品,如果所有数据都在一个数据库中,我们可以更容易地做到这一点。如果 API 必须处理来自多个服务器上的多个数据库的数据分组,则会增加开发和测试时间。(另一方面,“多服务器”的事情开始暗示一个数据库到规则所有场景的限制:一个数据库通常意味着我们所有的负载只影响一个数据库服务器。)在你的情况下,借助 PowerBI,将所有人集中在一个数据库中将使管理连接变得更加容易。

更轻松的高可用性和灾难恢复。如果我们只需要担心一个数据库,那么管理数据库镜像、日志传送、复制和集群真的非常简单。我们可以快速构建大量基础设施。

将每个客户端放在自己的数据库或分片中

您仍然需要一个客户端列表,但现在它变成了一个目录 - 对于每个客户端,您还跟踪它所在的分片。在启动时,您的应用程序查询此表,并将其缓存在 RAM 中。当它需要客户端的数据时,它会直接连接到该分片(数据库和服务器)。

这种方法的好处:

更轻松的单客户端还原。客户是不可靠的肉包。(除了我的——它们是可靠的肉包。)他们有各种各样的“糟糕”时刻,他们想要将所有数据恢复到某个时间点,如果他们的数据与同一表中的其他客户数据。在单客户端数据库场景中恢复非常简单:只需恢复客户端的数据库即可。没有其他人受到影响。

更轻松的数据导出。客户喜欢接触他们的数据。他们想要知道他们可以随时获取数据的安全性,避免可怕的供应商锁定情况,并且他们想要做自己的报告。将每个客户的数据隔离到他们自己的数据库中,我们可以简单地给他们一份他们自己的数据库备份的副本。我们不必构建数据导出 API。

更轻松的多服务器可扩展性。当我们的应用程序需要比我们从单个服务器获得更多的能力时,我们可以在多个服务器之间划分数据库。我们还可以在地理上分散负载,将亚洲或欧洲的服务器放置在离客户更近的位置。

更轻松的每客户端性能调整。如果某些客户端使用不同的功能或报告,我们可以为这些客户端构建一组专门的索引或索引视图,而不会增加每个人的数据大小。诚然,这里存在一些风险——通过允许客户端之间的模式差异,我们只是使我们的代码部署风险更大,我们的性能管理更加困难。

更轻松的安全管理。只要我们正确锁定每个数据库一个用户的安全性,我们就不必担心客户端 X 访问客户端 Y 的数据。但是,如果我们只是为每个人使用单一登录,那么我们还没有真正解决这个问题。

更容易的维护窗口。 在客户分散在全球各地的全球环境中,如果我们可以分组或区域进行维护,则更容易让客户离线进行维护。

哪一个适合你?

没有一个正确的选择:您必须了解自己公司的优势和劣势。让我们以我的两个客户为例。

A 公司擅长硬件性能调优。他们真的非常擅长从硬件中榨取最后一点性能,而且他们不介意在 12-18 个月的周期内更换他们的 SQL Server 硬件。(他们每 4-6 个月更新一次 Web 服务器!)他们的致命弱点是极端的合规性和安全性要求。他们有令人难以置信的审计需求,对他们来说,在单个服务器、单个数据库上实施防弹控制比在数十台服务器上的数千个数据库中管理这些需求更容易。他们选择了一个数据库、一台服务器和许多客户端。

公司 2 擅长开发实践。跨数千个数据库管理架构更改和代码部署对他们来说不是问题。他们在世界各地都有客户,他们全天候为这些客户处理信用卡交易。他们需要能够在地理上分散负载,而且他们不想每 12-18 个月更换一次世界各地的服务器。他们为每个客户选择了一个数据库,并且随着他们开始在亚洲和欧洲为他们的离岸客户部署 SQL Servers,它得到了回报。


Mat*_*tum 5

一种使多租户模型变得更加容易的做法是在租户的每个表上包含一列,即使它破坏了规范化*。您可以将其称为 TenantID。这样,针对数据库运行的每个查询都可以根据每个表上的 TenantID 进行筛选,并且您可以使用数据库分区来隔离每个租户的数据,并通过对齐分区来加快查询速度。通过这种方式将所有租户存储在一个数据库中要容易得多。

* 它并不总是会破坏标准化,但它可以。例如,如果您有一个Person和一个PersonAddress表。该Person表将作为TenantID, PersonID主键。该PersonAddress表将TenantID, PersonID, AddressTypeID按照我的建议作为主键。

通常PersonID就足够了,因为您可以将其连接回Person表中以查找Tenant. 我建议您继续TenantID使用后续的每张桌子,即使更薄的钥匙也可以使用。

据我了解,将任何可以从其他数据派生的信息转移到表中都被视为破坏标准化。但也许使用细键只是一种最佳实践。

  • 但即使您选择将 TenantID 放入子表中(您不必这样做),更宽的键并不意味着规范化被“破坏”。就像选择 GUID 而不是 IDENTITY(更宽的密钥)不会破坏规范化一样,选择更宽的自然密钥而不使用代理也不会破坏规范化。 (4认同)

Mic*_*een 5

我在其他答案中还没有看到的进一步考虑。

具有允许在单个数据库中容纳多个租户的设计将在以后提供灵活性。如果稍后加载/扩展/安全/地理位置需求建议租户应该有一个单独的数据库,可以通过在新实例上恢复当前数据库来创建它。其他租户的数据仍然受到现有机制的保护。在时间允许的情况下,可以从新旧数据库中逐步删除现在过时的数据。

反过来就不对了。整合许多单租户数据库需要更多的工作。