【问题标题】:Many tables for many users?许多用户的许多表?
【发布时间】:2010-05-17 23:03:47
【问题描述】:

我有一个 Web 应用程序,它在很多方面都可以被视为多租户环境。我的意思是应用程序的每个用户都有自己的“自定义”环境,这些用户之间绝对没有交互。

到目前为止,我已将 Web 应用程序构建为“单用户”环境。换句话说,我实际上并没有做任何事情来支持多用户,而只是处理了我想要从应用程序中获得的功能。这是我的问题...构建多用户环境的最佳方法是什么:

  1. 所有用户都指向同一个“核心”后端。换句话说,我构建了通过适当的 SQL 查询(例如 select * from table where user='123' and attribute='456')来分隔用户的逻辑。
  2. 每个用户都指向一个唯一的表空间,该表空间是在他们加入系统时单独构建的。在这种情况下,我将简单地为每个用户生成所有相关的 SQL 表,并为用户添加某种后缀。 (例如,现在查询看起来像 'select * from table_ where attribute ='456')。

简而言之,"select * from table where USER=" 和 "select * from table_USER" 是有区别的。

【问题讨论】:

  • 我理解每个用户都有自己的数据库表吗?这样做的目的是什么?
  • 采用“从 USER=.. 的表中选择 *”的方式。我还建议使用 OR/M 而不是直接编写 SQL,这应该清楚为什么这种方法更好(即 1 个用户类,多个用户对象 :))

标签: asp.net sql database-design


【解决方案1】:

动态创建表相当肮脏和混乱。此外,如果您有很多用户,那么如果您有大量表格,那将是一个完全的混乱 - 特别是如果您需要更改 n 个表格而不是单个表格中的某些内容。

--> 使用一张表并添加一些 user_id 列。使用适当的索引,这将与单独的表一样快甚至更快。

【讨论】:

    【解决方案2】:

    第一个选项更好。

    一般表格应该包含规范化的数据,你不应该重复同一个表格。

    第一个选项也更安全,因为您不需要授予程序创建或删除真实表的能力

    【讨论】:

      【解决方案3】:

      我会说你的选择取决于。你真的有三个选择:

      一个数据库来统治所有这些...(您的选择 1)

      当然,添加TenantId 列可以更轻松地添加新租户(用户),但也有一些缺点:

      1. 您必须小心确保每个查询都针对 TenantId 进行过滤。很容易不小心把TenantId忘在正确的地方,返回其他租户的数据。
      2. 所有顶级父表都必须包含 TenantId。它不仅是您的主要数据,还包括所有特定于租户的父数据。
      3. 租户不能在不同时间使用不同的架构版本。例如,假设您在应用程序的 1.1 版中进行了一些数据模式更改。如果所有租户都在同一个数据库中,那么无论您是否希望每个人都必须同时更新。此外,如果您使用的是单个数据库,您几乎不得不使用单个站点,因为您希望确保站点和架构保持同步。如果您这样做,例如,您不能向某人收取升级费用以获得新功能。这些功能必须作为插件构建,而不是针对特定版本的更新,这可能不是一件坏事,但必须从一开始就做出有意识的决定。
      4. 如果租户希望拥有其数据的副本或希望托管自己的数据,或者如果您希望将它们移动到另一个数据库服务器,则分离租户的数据可能是一件苦差事。由于所有资源都是共享的,因此您可能会遇到这样一种情况,即一个租户正在通过报告或流量消耗资源,以便您希望将它们移动到他们自己的数据库服务器(并向他们推销这种好处)。此外,我遇到过租户想要一份他们可以自己下载的数据副本的情况。如果所有数据都在一个数据库中,这可能会很麻烦。

      如果您要向企业客户销售产品,那么我不会走这条路。但是,如果您计划将成千上万的最终用户添加为不需要向他们提供数据的租户,那么使用单个数据库可能是正确的方法。

      按架构细分租户(例如 Tenant1.Table1、Tenant1.Table2...Tenant2.Table1、Tenant2.Table2...)(我相信您的选择 2)

      IMO,这是简单使用单独数据库的更难的版本。它的优点是维护一个数据库更容易一些,但除此之外,与使用单独的数据库相同的问题。

      每个数据库的细分租户

      对于企业客户,我发现最终证明这是最简单的。它消除了租户看到错误数据的可能性(除非连接字符串错误)。它允许公司托管自己的系统。如果每个租户有不同的虚拟应用程序,它允许租户使用不同的版本。它使资源分配、备份和恢复变得容易。唯一(但并非微不足道)的缺点是设置的时间成本(以及财务成本)。当您获得新客户时,添加数据库可能会很痛苦。从技术上讲,它可以自动化,但仍然很痛苦。

      因此,最终取决于您的目标客户。如果他们是标准用户,那么我会采用“一个数据库来统治他们”的方法,并确保您进行大量代码审查和自动化测试。如果他们是企业客户,尤其是大型企业客户,那么我会考虑为每个租户使用单独的数据库。

      【讨论】:

        【解决方案4】:

        为每个租户单独的表有意义的唯一方法是,如果您为每个租户都有一个单独的数据库,在这种情况下,这些表仍将具有相同的名称。

        否则,为每个实体使用一个表,并按租户 ID 过滤它们。

        【讨论】:

          【解决方案5】:

          如果您在 SQL Server 上,我建议对所有租户使用单个表,不允许对应用程序使用的任何登录名访问基表,并限制对内联表值函数的访问。这些就像参数化视图一样,意味着任何有权访问这些视图的人都无法在一次调用中检索多个租户的集合(因此不会意外加入其他人的产品目录):

          CREATE TABLE [dbo].[mt](
              [ID] [int] IDENTITY(1,1) NOT NULL,
              [TenantID] [int] NOT NULL,
              [BusinessKey] [varchar](50) NOT NULL,
           CONSTRAINT [PK_mt] PRIMARY KEY CLUSTERED 
          (
              [ID] ASC
          ),
           CONSTRAINT [IX_mt] UNIQUE NONCLUSTERED 
          (
              [TenantID] ASC,
              [BusinessKey] ASC
          ))
          
          CREATE FUNCTION f_mt ( @TenantID AS INT )
          RETURNS TABLE
          AS
          RETURN
              ( SELECT    *
                FROM      mt
                WHERE     TenantID = @TenantID
              )
          

          如果您将 TenantID 存储在连接中的某处(使用 CONTEXT_INFO()),也可以使用简单的视图来包装它:

          CREATE VIEW vw_mt
          AS
              SELECT *
              FROM f_mt(CONTEXT_INFO())
          

          这完全取决于您想对此进行多少抽象。

          【讨论】:

            猜你喜欢
            • 2020-08-16
            • 1970-01-01
            • 1970-01-01
            • 2015-12-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-27
            相关资源
            最近更新 更多