将 PostgreSQL 用作多租户服务的数据库的策略
这是近两年来我第二次有幸参与构建高弹性多租户SaaS基础设施的团队。多租户的定义是一种架构,其中单个软件实例(可能包含多个服务/微服务)服务于多个租户/实体,这些租户/实体可以是服务的消费者,也可以是企业用户。
构建这样一个基础设施,既要保证产品快速上市,又要能够扩展以支持业务增长,这带来了很多挑战(不仅仅是与软件工程相关的挑战),今天我想重点介绍我们考虑过(并最终实施过)的各种策略,特别是围绕我们服务基础设施的 SQL 数据库部分。
我不会提倡 SQL 数据库比其他数据库类型更有优势的使用场景,并且认为无论如何都应该讨论您的服务可能需要哪种数据库。
多租户服务数据库的要求
大多数情况下,在构建多租户基础设施时,我的思路都围绕着如何构建一个能够(理想情况下)扩展到数千个租户的系统,同时还要在运营、安全和隐私方面提供足够的隔离。在我看来,最重要的考虑因素之一是,在发生人为错误或安全漏洞时,能够控制影响范围。这意味着,从安全角度来看,我始终会尝试将数据库访问凭据限制在与会话相关的特定租户。
上述方法的明显缺点是,它可能会影响数据库连接池的管理能力,以及在同一数据库连接上为多个租户复用会话的能力。
方案 A - 每个租户一个数据库
在这种方案中,我们为每个租户维护一个独立的数据库。它允许定义访问凭证,从而在不同租户的数据之间建立起非常严格的界限。
无论是处理独立的数据库集群(成本高昂且会使基础设施部署复杂化),还是处理单个集群内的逻辑数据库(管理起来更可行),这种方法都非常适合需要严格数据隔离的环境。
如果采用这种方式,可以获得的一个潜在好处是能够为不同的租户维护不同的架构版本,从而实现逐步升级。
这种方法的缺点主要体现在运维成本上。例如,想象一下,每次模式更新都需要在每个数据库上运行,而不是只运行一次。此外,还要考虑在服务(及其组成微服务)需要并行处理多个租户的大量会话的环境中,这种方法会对数据库连接池造成怎样的影响。
另一个挑战是维护跨租户数据的复杂性。这需要创建一个单独的数据库,然后会使数据访问层变得复杂,因为该层需要将来自单个租户数据库的数据与跨租户数据库中的数据进行交叉引用。
方案 B - 每个租户一个 PostgreSQL 模式
PostgreSQL 特有的机制是,每个命名数据库都包含另一层用于存放数据库对象的结构,称为模式(schema)。根据文档,定义模式的最初目的是为了组织数据并允许并行操作而不会发生冲突。
PostgreSQL 允许不同的数据库角色拥有不同的模式,从而为每个租户的数据访问控制奠定了基础。
此外,还可以创建不同模式之间的交叉引用,以及为不同的租户维护不同的版本。
操作复杂性方面的缺点依然存在。对数据库结构进行更新需要修改所有模式中的表。
根据多租户服务的性质,有一些报告指出这种方法的可扩展性存在问题,表明拥有大量数据库模式的 PostgreSQL 数据库集群可能会导致性能问题。
方案 C1 - 包含租户标识列的共享表
这种方法常见于对租户数据隔离要求不高的环境中。所有租户的数据都存储在同一个数据库和同一个模式中,但每个包含租户数据的表都有一个特殊列,用于指示该行数据属于哪个租户。
这种方法的主要缺点在于,它要求在服务/微服务的数据访问层实现中,对多租户问题给予高度重视。经验不足的开发人员很容易犯错,例如未在查询中添加“WHERE tenant_id=<...>”,从而影响多个租户的数据。同样,如果有人恶意接管您的微服务并获取数据库访问凭证,情况也类似(甚至更令人担忧)。在这种情况下,影响范围非常广,此类攻击的影响可能是灾难性的。
从积极的方面来看,建立这样的数据库基础设施非常容易,而且可以将其扩展到大量的服务租户。
虽然这是一个很受欢迎的选择,但它的缺点(主要是服务器端完全没有强制执行访问隔离)促使一些软件架构师考虑更高级的方案。
方案 C2 - 共享表,包含用于租户标识的列和行级安全策略
SQL 数据库中的行级安全性 (RLS)是一种机制,它允许对表中符合特定表达式的行进行(高粒度的)访问管理。
为了利用此功能,必须为相关表启用行级安全性(“ALTER TABLE ... ENABLE ROW LEVEL SECURITY”),并且应定义指定相关权限的策略对象(“CREATE POLICY ON”)。
特别是当专用字段(例如上面示例中的“tenant_id”)包含分隔条件时,策略的using_expression和check_expression部分应包含布尔 SQL 表达式。
虽然可以将权限分配给不同的角色,从而实现非常严格的访问控制,但总体要求可以稍作放宽,例如使用具有唯一值的会话变量来实现上下文分离。在为角色分配策略时,值得注意的是,被定义为表所有者的角色将不受该策略的影响,因此将所有权与访问控制分离是明智之举。
概括
以上所有方案均可用于(实际上也已应用于)多租户软件服务。选择合适的方案取决于多种需求。以下是我们讨论中通常考虑的一些方案:
- 您的系统中将有多少租户?
- 我们预计多久更新一次数据库模式?
- 我们预计多久会删除一次租户(及其所有数据)?
- 我们的服务是否需要符合 SOC 2 隐私章节的要求?
- 我们的服务是否需要符合 ISO 27001 标准?
- 我们是否需要实施隐私保护措施(受 GDPR / 加州隐私法驱动)?
在本系列的后续文章中,我们将提供有关实现的更多技术细节。
欢迎您就此主题发表您的想法和见解。
文章来源:https://dev.to/lbelkind/strategies-for-using-postgresql-as-a-database-for-multi-tenant-services-4abd


