如何设计一个多租户的MySQL数据库。

40

假设我需要设计一个数据库,用于存储多家公司的数据。现在出于安全和管理目的,我需要确保不同公司的数据得到适当隔离,但我也不想为10家公司的数据在10个不同服务器上启动10个mysql进程进行托管。有什么最好的方法可以使用mysql数据库实现这一点。

7个回答

51

有几种处理多租户数据库的方法。一般来说,它们通常被分为三类。

  • 每个租户一个数据库。
  • 共享数据库,每个租户一个模式。
  • 共享数据库,共享模式。租户标识符(租户键)将每行数据与正确的租户关联。

MSDN有一篇很好的文章介绍了每种设计的优缺点实现的示例


Microsoft显然已经删除了我所提到的页面,但是它们在archive.org上。链接已更改为指向那里。

作为参考,这是第二篇文章的原始链接


1
光谱从“共享无”(每个租户一个数据库)到“共享一切”(每个表中都有租户键)运行。分片接近光谱的“共享一切”端。我希望它能使单个租户的灾难恢复更加简单。如果您可以想象在单个服务器上运行“共享一切”系统,则为单个租户恢复数据意味着仅恢复每个共享表中的某些行。这不仅是非常简单的。 - Mike Sherrill 'Cat Recall'
MySQL支持单个数据库多个模式吗? - Sagar
2
@Sagar:不支持标准SQL意义上的“create schema”。MySQL中的“create schema”只是“create database”的同义词。文档 - Mike Sherrill 'Cat Recall'
有人能给我提供租户数据库的正确示例吗? - Raj Mohan
@MikeSherrill'CatRecall',能否给我一个实时的例子,我找不到。 - Raj Mohan
显示剩余6条评论

9
在MySQL中,我倾向于为所有租户使用单个数据库。我通过为每个租户使用一个单独的数据库用户来限制数据访问权限,该用户仅能访问仅显示属于该租户行的视图。
可以通过以下方式实现:
  1. 在每个表中添加tenant_id列
  2. 使用触发器在插入时将tenant_id填充为当前数据库用户名
  3. 为每个表创建一个视图,其中tenant_id = current_database_username
  4. 在应用程序中仅使用视图
  5. 使用特定于租户的用户名连接到数据库
我已经在博客文章中对此进行了全面记录:https://opensource.io/it/mysql-multi-tenant/

2
您的文章似乎不再存在了:错误404。您是否有更新的链接?谢谢 - Metafaniel
1
这是一篇非常详细的博客,讲解了在MySQL中使用相同方法的多租户策略。 - Harsh Agarwal

5

简单的方法是:对于每个共享的表,添加一列称为SEGMENT_ID。 为每个客户分配适当的SEGMENT_ID。 然后基于SEGMENT_ID为每个客户创建视图,这些视图将使数据与每个客户分开。 使用此方法,信息可以共享,同时简化操作和开发(存储过程也可以共享)。


12
SEGMENT_ID是一个糟糕的名称。 - Willem D'Haeseleer
20
TENANT_ID 是一个完美的名称。 - Nabeel

3
假设您在单个MySQL实例上运行一个MySQL数据库,有几种方法可以区分属于谁的内容。对我来说,最明显的选择是创建一个复合主键,例如:
CREATE TABLE some_table (
id int unsigned not null auto_increment,
companyId int unsigned not null,
..
..
..,
primary key(id, company_id)
) engine = innodb;

然后,通过更改主键中的companyId部分来区分公司。这样,您可以在同一个表/数据库中拥有所有公司的数据,并且在应用程序级别上,您可以控制哪个公司与哪个companyId相关联,并确定显示某些公司的哪些数据。

如果这不是您要寻找的内容-很抱歉我误解了您的问题。


我的想法是在一定程度上隔离数据,为每个公司数据库设置唯一的登录ID。这样,假设公司A发生了安全漏洞,公司B的数据仍然应该是安全的。 - stocked
1
当时我不理解你的回答。当我理解了它,我意识到我甚至使用了相同的模式。我不认为这是无意义的,你可能需要与我沟通以获得清晰度,而不是将我所说的标记为无意义。我认为这很粗鲁。 - I.Tyger

2
您考虑过为每个公司创建不同的schema吗?
尽管如此,您应该尝试更加明确地定义您想要实现的目标。
例如,如果您想确保硬件故障不会影响多个公司的数据,那么您需要创建不同的实例并在不同的节点上运行它们。
如果您想确保来自A公司的人无法看到属于B公司的数据,您可以像Matthew PK的答案一样在应用程序级别上进行设置。
但是,如果您希望能够防止某个人破坏安全性并对数据库运行任意SQL,则需要更强大的解决方案。
如果您想能够独立备份数据,以便您可以安全地在周一备份公司C,在周日备份公司A,并能够仅恢复公司C,则单纯的应用程序解决方案将无济于事。

我想做你在最后一点提到的事情。独立进行备份,将数据隔离到一定程度,以便如果一个公司正在接收大量流量,则将它们移动到完全不同的实例上,放在另一台机器上。 - stocked
那么看一下模式,我对mySql没有太多经验,所以可能会漏掉一些实现特定的细节,但我认为这是你情况下最好的方法。 - p.marino

1

给定一个特定的数据库用户,您可以将用户成员身份授予组,指示他们被允许访问数据的公司。

我假设您将拥有一个Companies表,因此只需在CompaniesMySQLUsers或类似表之间创建一对多关系。

然后,在所有查询的条件中,只需根据UserID匹配CompanyID即可。


0

在我的文件Generate_multiTanentMysql.php中,我使用PHP脚本执行所有步骤。

https://github.com/ziedtuihri/SaaS_Application

一个解决方案设计模式:

  • 为每个租户创建一个数据库用户

  • 将每个表重命名为不同且唯一的名称(例如,使用前缀'someprefix_')

  • 在每个表中添加一个名为'id_tenant'的文本列,用于存储行所属的租户名称

  • 为每个表创建一个触发器,在插入新行之前自动将当前数据库用户名存储到id_tenant列中

  • 为每个表创建一个视图,使用原始表名和除id_tenant外的所有列。该视图只返回(id_tenant = current_database_username)的行

  • 仅向每个租户的数据库用户授予视图权限(而非表权限)

    然后,应用程序中唯一需要更改的部分是数据库连接逻辑。当有人连接到SaaS时,应用程序需要:

  • 以该特定租户的用户名连接到数据库


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