多个数据库 vs 逻辑分区数据的单个数据库

42

我正在考虑一个数据库设计问题,非常感谢任何帮助。

我们正在设计一个应用程序,它有20个表(在新功能开发期间可能最多增长到30个)

技术栈

MVC4,.NET 4.X, Entity Framework 5, SQL Server 2012, ASP.NET成员身份验证框架

用户数量

我们计划为大约1000个客户提供服务,每个客户平均有20个用户。

问题

我们是否应该设计数据库和应用程序的方式,使得表被逻辑分区,即所有客户都使用相同的表,并使用分区guid来分隔数据。

还是

选择多个数据库,在新功能发布和错误修复时可能会更加困难。但可能允许扩展?

注意:其中一个表具有存储文件的二进制列(每个记录最大为5MB)

除此之外,我们需要考虑Membership框架表,我们将其扩展到另一个自定义表并将用户逻辑映射到分区guid。


2
为什么应该重新开放这个问题?多租户并非一刀切。是的,ORM、数据库功能可以缓解每个选择的某些痛点,但答案仍取决于每个项目的要求。 - Panagiotis Kanavos
2个回答

103

你会希望使用独立的数据库:

  • 如果你想要授予客户或超级用户对数据库本身的权限。
  • 如果你想要还原一个客户的数据库而不影响其他客户的数据。
  • 如果有监管机构关注你的数据和数据泄露问题,而你迟迟发现只有通过使用独立的数据库才能满足这些规定。(更新:在本回答撰写的4年多之后,GDPR开始生效)
  • 如果你想要轻松地将客户数据移动到多个数据库服务器或以其他方式进行扩展,或者将更大/更重要的客户移动到世界上的另一个地方。
  • 如果你想要轻松地存档和停用旧客户数据。
  • 如果你的客户关心其数据是否被隔离,并发现你没有这样做。
  • 如果你的数据被传票,而提取一个客户的数据很困难,或者传票过于宽泛,你需要提供整个数据库而不是仅为一个客户提供数据。
  • 当你忘记保持警惕,只有一个查询可以滑过,这个查询没有包括AND CustomerID = @CustomerID。提示:使用脚本化许可工具、模式或者用包含WHERE CustomerID = SomeUserReturningFunction()的视图来包装所有表,或者这些方法的某种组合。
  • 当你在应用程序级别上授权出错,客户数据暴露给了错误的客户。
  • 当你想要为不同的客户提供不同级别的备份和恢复保护。
  • 一旦你意识到建立一个基础架构来创建、提供、配置、部署、以及启动/关闭新数据库是值得投资的,因为它迫使你变得熟练起来。
  • 当你没有考虑到某类人需要访问多个客户的数据时,你需要在Customer之上加一个抽象层,因为现在WHERE CustomerID = @CustomerID已经不够用了。
  • 当黑客针对你的网站或系统时,如果他们得到管理员凭证并轻松获得一个数据库中所有客户的数据,你就会后悔。
  • 当你的数据库备份需要运行5小时然后失败时。
  • 当你不得不获取企业版DBMS以便进行压缩备份,使得复制备份文件到网络的时间少于另外5个小时时。
  • 当你每天必须将整个数据库恢复到测试服务器上,这需要5个小时,并运行需要2个小时才能完成的验证脚本时。
  • 当只有少数客户需要复制,但你不得不将其应用于所有客户而不是仅限于少数客户时。
  • 当你想接待政府客户并发现他们要求你使用单独的服务器和数据库,但你的生态系统是建立在单一服务器和数据库基础之上,改变它实在太难或需要太长时间。
  • 你会因为使用单独的数据库而感到高兴:

    • 当面向一个客户的试点扩展完全失败时,其他999个客户将完全不受影响。而且你可以从备份中恢复来修复问题。
    • 当其中一个数据库备份失败时,你只需花25分钟修复一个而不是重新开始整个10小时过程。

    你会希望使用单一数据库:

    • 当你发现一个影响所有1000个客户的错误时,而将修复部署到1000个不同的数据库很困难时。
    • 当你在数据库层面上授权错误时,会导致客户数据暴露给错误的客户。
    • 当你没有考虑到某类人可能需要访问所有数据库的子集(例如两个客户合并)时。
    • 当你没有想到如何合并两个不同的数据数据库时,这很困难。
    • 当你合并了两个不同的数据库,并意识到其中一个是错误的时,你没有计划从这种情况中恢复。
  • 当你尝试在单个服务器上扩展超过32767个客户端/数据库时,发现这是SQL Server 2012中的最大限制。
  • 当你意识到管理1000多个数据库比你想象的更可怕。
  • 当你意识到你不能仅通过在表中添加一些数据来为新客户提供服务,而必须运行一堆可怕和复杂的脚本来创建、填充和设置权限。
  • 当你不得不每天运行1000个数据库备份,并确保它们都成功,将它们复制到网络上,将它们全部还原到测试数据库,并对每一个进行验证脚本,以一种能够被保证看到并且易于快速操作的方式报告任何失败,然后其中150个备份在各个地方都失败了,必须逐个修复。
  • 当你发现你必须为1000个数据库设置复制。
  • 我列出更多的原因并不意味着它更好。

    一些读者可能会从MSDN: 多租户数据架构 中受益。或者SaaS租户应用程序设计模式。或者为云开发多租户应用程序,第三版


    让人们可以访问多组数据听起来像是数据仓库实现的工作... - jcolebrand
    1
    @jcolebrand 听起来很棒... 除非需要读写访问权限。在这种情况下,会出现更困难的挑战! - ErikE
    一方面,EF Core 2.2 中的包含数据库、全局过滤器和行级权限等功能可以缓解每种情况下的某些痛点。另一方面,隐私法律和 GDPR 意味着即使客户没有使用共享数据库,您也必须格外小心。 - Panagiotis Kanavos
    @PanagiotisKanavos 当我提到法规影响如何存储数据时,GDPR还不存在!我有点预见性... - ErikE

    8
    如果您将架构称为“多租户”,Microsoft有一篇值得阅读的好文章,可以在此处找到。它展示了“隔离”(多个数据库)和“共享”(单个数据库)之间的一些比较。通常,在租户(客户端)数量很大时,共享获胜,但当每个租户的大小很大时,建议采用隔离方法。
    然而,这些考虑只能由经验丰富的开发人员计算。
    如果您成功使用了隔离(多个数据库)架构,即使它们仍在同一实例上运行,您也不会在性能方面获得直接的好处。如果您使用共享(单个数据库)架构,请考虑使用int而不是guid,或者如果仍需要使用guid,则使用sequential guid。

    使用int而不是guid真的很重要吗?为什么微软在其SharePoint和其他产品中到处都使用guid呢? - ilans
    @ilanS 好的,你可以在这里阅读:http://www.sqlskills.com/blogs/kimberly/guids-as-primary-keys-andor-the-clustering-key/。通常情况下,有人使用两个字段,一个 int/bigint 作为主键,一个 guid 作为非聚集索引。对于行选择,他们使用 guid。对于连接等操作,他们使用 PK。这是出于安全考虑。 - Fendy

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