如何创建具有共享表结构的多租户数据库?

144

我们的软件目前运行在MySQL上。所有租户的数据都存储在同一个模式中。由于我们使用Ruby on Rails,我们可以轻松确定哪些数据属于哪个租户。然而,当然有一些公司担心他们的数据可能会被泄露,因此我们正在评估其他解决方案。

到目前为止,我看到了三种选择:

  • 多数据库(每个租户都有自己的数据库 - 几乎相当于每个客户一个服务器)
  • 多模式(MySQL不支持,每个租户在共享数据库中拥有自己的模式)
  • 共享模式(我们目前的方法,可能需要在每列上添加附加标识记录)

多模式是我最喜欢的(考虑到成本)。但是创建新帐户并进行迁移似乎非常痛苦,因为我必须遍历所有模式并更改它们的表/列/定义。

Q: 多模式似乎被设计为为每个租户具有略微不同的表 - 我不想要这样的表结构。是否有任何关系型数据库管理系统可以让我使用多模式多租户解决方案,其中表结构在所有租户之间共享?

P.S. 我所说的多是超级多(10,000+ 租户)。


1
多租户似乎被设计为每个租户有略微不同的表格。那又怎样呢?多租户和所有相同的表格有什么问题吗?你是说你不想在所有模式中重新创建相同的表结构吗?还是说你不能在所有模式中创建相同的结构? - S.Lott
1
+1 对于好/有趣的问题 - AdaTheDev
2
@S.Lott 我预计每天有100+个注册用户的10.000+租户。在单个表定义中拥有数百万条记录(定义=共享,数据=隔离)比在成千上万个表定义中拥有数千条记录让我感觉更好。由于没有多少人以这种方式进行操作,因此我对多模式并不太自信。 - Marcel Jackwerth
1
我同意丹尼尔的观点,基于那些数字,多数据库被排除在外。我已经更新了我的答案以反映这一点,但仍然保留它作为历史记录。共享方法确实似乎是最合理的方法。 - AdaTheDev
2
来自dynjo的回答中提到:“Ryan Bigg关于这个主题的好文章”。 - Félix Adriyel Gagnon-Grenier
显示剩余3条评论
4个回答

116
然而,当然有一些公司担心他们的数据可能会被泄露,因此我们正在评估其他解决方案。 这很不幸,因为客户有时会误解只有物理隔离才能提供足够的安全性。 有一篇有趣的MSDN文章,标题为多租户数据架构,你可能想要查看。这是作者如何解决对共享方法的误解的方式: “一个常见的误解认为只有物理隔离才能提供适当水平的安全性。事实上,使用共享方法存储的数据也可以提供强大的数据安全性,但需要使用更复杂的设计模式。” 至于技术和业务考虑因素,该文章对某种方法何时比另一种方法更合适进行了简要分析:
你期望服务的租户数量、性质和需求会以不同的方式影响你的数据架构决策。以下一些问题可能会让你倾向于更独立的方法,而其他问题可能会让你倾向于更共享的方法。
- 你期望针对多少潜在租户?你可能无法准确估计潜在使用情况,但可以考虑数量级:你是为数百个租户构建应用程序吗?成千上万?数万?更多?你期望的租户基数越大,越有可能需要考虑更共享的方法。
- 你期望每个租户的数据占用多少存储空间?如果你期望一些或所有租户存储大量数据,则采用单独的数据库方法可能是最好的选择。(实际上,数据存储要求可能会迫使你采用单独的数据库模型。如果是这样,从一开始就采用这种方式设计应用程序会比后来转向单独的数据库方法容易得多。)
- 你期望每个租户支持多少并发用户?用户数量越多,更独立的方法就越适合满足最终用户的需求。
- 你期望提供任何按租户增值服务,例如按租户备份和恢复功能吗?这些服务通过更独立的方法更容易提供。

更新: 关于预期租户数量的最新信息。

对于大多数(如果不是全部)情况,预期租户数量(10k)应该不包括多数据库方法。我认为你不会喜欢维护10000个数据库实例,并且每天还要创建数百个新实例的想法。

仅从这个参数来看,似乎共享数据库、单一模式方法是最合适的。事实上,每个租户存储的数据只有约50Mb,而且没有每个租户的附加组件,使得这种方法更加适用。

上面提到的MSDN文章提到了三种安全模式,以解决共享数据库方法的安全考虑:

当你对应用程序的数据安全措施有信心时,就可以向客户提供强有力的数据安全保证的服务级别协议。在你的SLA中,除了这些保证,你还可以描述你将采取的措施来确保数据不会被破坏。

更新2: 微软的工程师们似乎移动/创建了一篇关于这个主题的新文章,原始链接已经消失,这是新的链接:多租户SaaS数据库租户模式(感谢Shai Kerer)


1
哦,我昨天扫描了那篇文章,跳过了那个误解部分。需要再读一遍。 - Marcel Jackwerth
1
@Marcel:然而,除了客户对安全性的感知之外,我认为您选择哪种多租户方法应该基于我从MSDN文章中引用的这4个因素:1.预期租户数量。-2.每个租户的预期存储要求。-3.预期并发终端用户数。-4.预期每租户附加组件。 - Daniel Vassallo
1
感谢您指出那个部分。数量=10k,存储=50mb,每租户并发终端用户=2,插件=0。因此,目前采用共享方法似乎是最合理的。我想下周我会打一些电话,了解客户真正需要/期望什么。德国和数据/IT安全是一个非常棘手的问题。 - Marcel Jackwerth
1
仅供阅读此内容的用户,提到的文章已不存在,或许有人复制了一份? - gmslzr
1
@guillesalazar 我不确定是不是同一个,但我猜应该是 - https://learn.microsoft.com/zh-cn/azure/sql-database/saas-tenancy-app-design-patterns (@DanielVassallo 如果是同一个的话,或许可以考虑在你的回答中更新链接 :-) ) - Shai Kerer
在我的情况下,我选择单个DB服务器实例,但面临最大连接限制的问题。你能看一下这个问题吗?https://stackoverflow.com/questions/60880495/create-a-connection-pool-for-many-dbs-on-the-same-db-server-spring-boot - Hopesun

24

以下是Salesforce.com有关如何实现多租户的白皮书链接:

http://www.developerforce.com/media/ForcedotcomBookLibrary/Force.com_Multitenancy_WP_101508.pdf

他们有一个巨大的表,拥有500个字符串列(Value0、Value1......Value500)。日期和数字以一种可以在数据库级别转换为其本机类型的字符串格式存储。还有元数据表来定义数据模型的形状,每个租户可以是独特的。此外还有用于索引、关系、唯一值等的其他表。

为什么要这样做呢?

每个租户都可以在运行时自定义自己的数据架构,而无需进行数据库级别的更改(例如alter table)。这绝对是一种较为困难的实现方式,但非常灵活。


22

我的经验是(虽然是针对SQL Server),多数据库是一种可靠的方式,每个客户都有自己的数据库。因此,尽管我没有mySQL或Ruby On Rails的经验,但我希望我的建议能够增加一些价值。

原因如下:

  1. 数据安全/灾难恢复。每个公司的数据完全分开存储,降低数据被攻击的风险(比如代码缺陷导致访问其他客户数据时出现错误),最大程度减少一个特定数据库受损引起的潜在损失等。客户感知到的安全优势甚至更大(额外的奖励效果!)
  2. 可扩展性。本质上,您可以将您的数据进行分区以实现更高的可扩展性 -例如,可以将数据库放置在不同的磁盘上,可以在线上添加多个数据库服务器并更易于移动数据库以平衡负载。
  3. 性能优化。假设您有一个非常大的客户和一个非常小的客户。使用模式、数据量等可能会有很大的差异。如果需要,您可以更容易地为每个客户进行调整/优化。

我希望这能提供一些有用的建议!还有更多原因,但我现在脑子一片空白。如果我想到了,我会更新的:)

编辑:
自从我发布这个答案以来,现在明确了我们正在讨论10,000+租户的问题。我的经验是在数百个大型数据库上 - 我认为对于您的情况,管理10,000个单独的数据库可能有些困难,所以我现在不支持多数据库方法。特别是现在清楚了您所说的每个租户数据量都很小!

我将保留此答案,因为它可能对处于类似情况(具有较少租户)的其他人有用。


是的,抱歉我之前没有澄清。还是+1。;) - Marcel Jackwerth
在谈论数据安全时,您会说每个数据库应该放置在不同的服务器/虚拟机上吗?还是将所有数据库放在单个/集群服务器上,并使用不同的SQL用户足够安全? - Shay
@Shay - 不需要将它们放在不同的服务器上 - 想象一下你有数百个,这是你需要开始的许多服务器实例/许可证。请参见Daniel在上面的答案,那里有一些很好的链接。 - AdaTheDev
我认为即使多数据库意味着有10,000个单独的数据库,这会显著增加维护成本,但您仍然可以使用云基础架构上的自动化脚本来驯服这个野兽,使一切都变得可编程管理,几乎不需要人力投入。 - Korayem

13
作为你提到的,每个租户一个数据库是一种选择,并且确实有一些较大的权衡。它在较小规模下可以很好地工作,例如单个数字或低10个租户,但超过这个范围后,管理变得更加困难。不仅需要迁移,还需要保持数据库的正常运行。
每个架构模型不仅对于每个唯一架构有用,尽管在所有租户上运行迁移变得困难,在1000个架构以上时,Postgres可能会开始出现问题。

更具可扩展性的方法是将租户随机分布在同一数据库中的不同逻辑分片(或)中。根据您使用的编程语言,有许多库可以帮助完成这项工作。如果您使用的是Rails,有一个名为acts_as_tenant的库,它有助于确保租户查询仅返回该数据。还有一个gem apartment - 虽然它使用模式模型,但它确实有助于跨所有模式的迁移。如果您使用Django,则有很多库,但其中一个较受欢迎的是跨模式。所有这些都更有助于应用程序级别的操作。如果您正在寻找更直接的数据库级别的解决方案,则Citus专注于使Postgres的此类分片对多租户的支持更加开箱即用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接