目录
-
让我们做一个应用程序 - 广告分析
-
扩展关系数据模型
-
准备表和摄取数据
-
自己试试
-
集成应用程序
-
在租户之间共享数据
-
Schema 的在线更改
-
当租户的数据不同时
-
扩展硬件资源
-
与大租户打交道
-
接下来
如果您正在构建软件即服务 ( SaaS
) 应用程序,您可能已经在数据模型中内置了租赁的概念。通常,大多数信息与租户/客户/帐户相关,并且数据库表捕获这种自然关系。
对于 SaaS
应用程序,每个租户的数据可以一起存储在单个数据库实例中,并与其他租户保持隔离和不可见。这在三个方面是有效的。首先,应用程序改进适用于所有客户端。其次,租户之间共享数据库可以有效地使用硬件。最后,为所有租户管理单个数据库比为每个租户管理不同的数据库服务器要简单得多。
但是,传统上,单个关系数据库实例难以扩展到大型多租户应用程序所需的数据量。当数据超过单个数据库节点的容量时,开发人员被迫放弃关系模型的优势。
Citus
允许用户编写多租户应用程序,就好像他们连接到单个 PostgreSQL
数据库一样,而实际上该数据库是一个水平可扩展的机器集群。客户端代码需要最少的修改,并且可以继续使用完整的 SQL 功能。
本指南采用了一个示例多租户应用程序,并描述了如何使用 Citus
对其进行建模以实现可扩展性。在此过程中,我们研究了多租户应用程序的典型挑战,例如将租户与嘈杂的邻居隔离、扩展硬件以容纳更多数据以及存储不同租户的数据。 PostgreSQL
和 Citus
提供了应对这些挑战所需的所有工具,所以让我们开始构建吧。
让我们做一个应用程序 - 广告分析
我们将为跟踪在线广告效果并在顶部提供分析仪表板的应用程序构建后端。它非常适合多租户应用程序,因为用户对数据的请求一次只涉及一家公司(他们自己的)。 Github
上提供了完整示例应用程序的代码。
citus-example-ad-analytics
-
https://github.com/citusdata/citus-example-ad-analytics
让我们从考虑这个应用程序的简化 schema
开始。该应用程序必须跟踪多家公司,每家公司都运行广告活动。广告系列有许多广告,每个广告都有其点击次数和展示次数的关联记录。
这是示例 schema
。稍后我们将进行一些小的更改,这使我们能够在分布式环境中有效地分发和隔离数据。
CREATE TABLE companies (
id bigserial PRIMARY KEY,
name text NOT NULL,
image_url text,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
CREATE TABLE campaigns (
id bigserial PRIMARY KEY,
company_id bigint REFERENCES companies (id),
name text NOT NULL,
cost_model text NOT NULL,
state text NOT NULL,
monthly_budget bigint,
blacklisted_site_urls text[],
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
CREATE TABLE ads (
id bigserial PRIMARY KEY,
campaign_id bigint REFERENCES campaigns (id),
name text NOT NULL,
image_url text,
target_url text,
impressions_count bigint DEFAULT 0,
clicks_count bigint DEFAULT 0,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
CREATE TABLE clicks (
id bigserial PRIMARY KEY,
ad_id bigint REFERENCES ads (id),
clicked_at timestamp without time zone NOT NULL,
site_url text NOT NULL,
cost_per_click_usd numeric(20,10),
user_ip inet NOT NULL,
user_data jsonb NOT NULL
);
CREATE TABLE impressions (
id bigserial PRIMARY KEY,
ad_id bigint REFERENCES ads (id),
seen_at timestamp without time zone NOT NULL,
site_url text NOT NULL,
cost_per_impression_usd numeric(20,10),
user_ip inet NOT NULL,
user_data jsonb NOT NULL
);
我们可以对 schema
进行一些修改,这将在像 Citus
这样的分布式环境中提高性能。要了解如何,我们必须熟悉 Citus
如何分发数据和执行查询。
扩展关系数据模型
关系数据模型非常适合应用程序。它保护数据完整性,允许灵活查询,并适应不断变化的数据。传统上唯一的问题是关系数据库不被认为能够扩展到大型 SaaS
应用程序所需的工作负载。开发人员必须忍受 NoSQL
数据库 — 或后端服务的集合 — 才能达到这个规模。
使用 Citus
,您可以保留数据模型并使其可扩展。 Citus
对应用程序来说似乎是一个 PostgreSQL
数据库,但它在内部将查询路由到可并行处理请求的可调整数量的物理服务器(节点)。
多租户应用程序有一个很好的特性,我们可以利用它:查询通常总是一次请求一个租户的信息,而不是多个租户的信息。例如,当销售人员在 CRM
中搜索潜在客户信息时,搜索结果是特定于他的雇主的;其他企业的线索和注释不包括在内。
由于应用程序查询仅限于单个租户,例如商店或公司,因此快速进行多租户应用程序查询的一种方法是将给定租户的所有数据存储在同一节点上。这最大限度地减少了节点之间的网络开销,并允许 Citus 有效地支持所有应用程序的 连接(joins)
、 键约束(key constraints)
和 事务(transactions)
。有了这个,您可以跨多个节点进行扩展,而无需完全重新编写或重新构建您的应用程序。
我们在 Citus
中通过确保 schema
中的每个表都有一个列来清楚地标记哪个租户拥有哪些行来做到这一点。在广告分析应用程序中,租户是公司,因此我们必须确保所有表都有一个 company_id
列。
当为同一公司标记行时,我们可以告诉 Citus
使用此列来读取和写入同一节点的行。在 Citus
的术语中, company_id
将是分布列,您可以在分布式数据建模中了解更多信息。
-
分布式数据建模
-
https://docs.citusdata.com/en/v10.2/sharding/data_modeling.html#distributed-data-modeling
-
准备表和摄取数据
在上一节中,我们确定了多租户应用程序的正确分布列: 公司 ID(company_id)
。即使在单机数据库中,通过添加 公司 ID
对表进行非规范化也是很有用的&#x