在单个数据库上设计一个多租户(SQL)的SaaS

5
我正在开发一个SaaS产品,尝试为我的场景设计最佳的数据库结构,我认为这个场景非常标准。
我应该注意到我没有设计过这样的数据库。
我在网上进行了研究,但实际上我找不到任何有关实现的信息。虽然有很多比较不同的多租户架构。
对于多租户方法,我决定采用单一数据库 - 似乎是最适合的。
以下是基本支持的列表:
- 多个客户端,全部分离,在它们之间不共享数据。 - 每个客户端都有自己的用户群(员工)。 - 客户的员工成员可以访问系统的不同区域,并能执行某些操作。 - 每个客户都有自己的客户。
我可以理解将tenant_id放在属于那个租户的任何表格上的基本概念。我想我的问题更多地是如何与每个客户端的员工成员的不同访问级别相结合。
你会怎么做呢?是否有相关的数据库实现参考?
谢谢!
更新
在得到@dmfy的答案后,我思考了一下并想出了这个解决方案:
account
 -id
 -name

user
 -id
 -account_id
 -username
 -password

role
 -id
 -account_id
 -name

user_role
 -user_id
 -role_id

access
 -id
 -role_id
 -name

role_access
 -role_id
 -access_id

session
 -account_id
 -user_id
 -token

我会解释一下 - role表实际上是与权限/访问级别列表相关联的用户组。 access表表示单个权限。平台的某个区域,可以(或不能)执行的操作。
在成功登录后,会创建一个session表中的行。每次调用服务器时,如果已对令牌进行了验证,则会查找该用户的角色(使用user_roles上的session.user_id)并收集其访问列表(使用role_access.role_id上的role.id)。
一旦我有了访问列表,我就可以检查请求并查看用户是否被允许执行操作。
注释:
- role可以为每个租户/帐户定制(例如,一个租户可以拥有“管理”和“员工”,另一个租户可以拥有“管理”,“支持”和“销售”),因此与account关联。 - 另一方面,access是平台范围内的。该平台在所有租户中都具有相同的区域和操作集。因此,没有必要将其与特定帐户相关联。 - 对于access查找的改进可能是在登录时将访问列表存储到会话中,以消除双重连接(获取所有用户的角色,获取所有角色的访问列表)。
问题:
- 首先,您对设计的整体看法如何?您是否看到任何缺陷? - 在session中保存account_id是否真的需要/是一个好主意? - 服务器检查用户是否有权访问某个资源是否是标准方法?是否有一种方式可以将其作为查询本身的一部分来完成(例如从数据库本身获得错误)?

将多个租户存储于同一数据表格中是最糟糕的选择。你为什么要默认选择它呢? - Neil McGuigan
1
更容易维护单个数据库,扩展性更好,成本更低...你对此有什么反对意见吗? - yohairosen
有不做的原因:一些行业法规可能要求更严格的数据隔离,升级/迁移会影响所有租户,无论如何,与单个租户数据集一起工作都很困难。我在2017年做了一个关于管理最后一个问题的技术的演讲(链接:https://vimeo.com/223984368)。 - dmfay
1
我明白了。这就是为什么我不认为不同的方法有好坏之分,更多的是适合你想要实现的目标。在我的情况下,我没有任何那些规定或需求,所以单个数据库看起来最合适。 - yohairosen
1
@yohairosen,近两年来,您对数据库结构感到满意吗?您仍在使用它吗?回顾过去,您会做出任何改变吗? - aalimovs
2个回答

3

在描述解决方案之前,描述您的要求可能会得到更好的答案。

您的设计似乎描述了一个授权方案。它看起来相当可信 - 我会用自然语言总结一下:

  • 租户是一个帐户。
  • 一个帐户有多个用户。
  • 用户可以拥有多个角色。
  • 角色授予许多权限。
  • 系统维护会话列表,将请求映射到用户;这反过来允许系统检查用户是否具有执行给定操作的权限。

不知道您的要求情况下,这似乎相当合理。您可能需要从“帐户”链接到应用程序认可的某些内容作为“租户”的内容。

最大的问题是如何在应用程序中使用此数据。检查每个请求的权限 - 特别是细粒度的权限 - 可能很昂贵。

特定的解决方案在这里严重依赖于您的应用程序框架 - 许多应用程序框架都具有内置的身份验证/授权模型,使用这些内置功能通常是一个好主意。

对于如何实现此功能的想法,您可以查看适用于Ruby on Rails的授权框架CanCanCan或适用于Laravel的Authority
另外,您的系统中实际数据如何与帐户关联并不清楚 - 如果您的系统跟踪小部件,则“小部件”表是否有“account_id”列?如果是这样,您的应用程序如何跟踪谁有权访问该行?

我同意,你的列表总结得很好。拥有租户和账户的好处是什么?你所提出的(重要)问题正是我想要听取一些意见的- 我认为这可能也很昂贵- 还有其他的做法吗?那么我在问题中提出的“改进”呢?将权限列表缓存到会话中如何?...是的,所有相关的“对象”/“行”都将有一个account_id- 以你的例子为例,我将检查用户的角色是否与“widget”的“访问”相关联。你有什么想法? - yohairosen
我已经更新了答案 - 这实际上是一个关于你正在使用的应用程序框架的问题,而不是数据库设计。总的来说,我认为超级细粒度的权限可能有些过度杀伤力 - 通过对应用程序需要支持的角色进行建模,您可能会得到更清晰的设计。 - Neville Kuyt

2
听起来你把“数据库用户”和“应用程序用户”混为一谈了。在一个采用共享模式的SaaS产品中,单个用户无法直接访问数据库;相反,你的应用程序会作为一个单一用户连接到所有需要的对象,并具有适当的权限。你所担心的是用户可以访问哪些应用程序区域以及他们能够执行哪些操作。这意味着你需要将授权模型构建到你的架构中。
最简单的模型有两个访问级别:普通用户和管理员。这可以通过一个“用户”表来表示,该表具有“租户ID”以将个人登录与正确的客户端关联,并带有一个“is_admin”标志。然后,你的应用程序确保只有设置了标志的用户才能访问管理功能。如果你的授权模型更复杂,请适当设计你的架构(例如,“用户”可能与“角色”具有多对多的关系)。
还要注意,仅对于与“租户”直接相关的表格,才严格需要一个“租户ID”列;如果你有一个带有“user_id”的“配置文件”表,则可以通过“用户”追溯到租户的关系。在某些情况下,添加“租户ID”以避免长时间的连接链可能是有意义的。

感谢您指引我正确的方向。我更新了我的问题。 - yohairosen

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