本文面向准备使用分布式版集群的业务,按集群规划、账号和数据库准备、表分布式化、验证和扩缩容的顺序完成上线准备。该流程可避免因手动创建普通数据库或连接到不适用的地址而导致分布式能力未生效、性能下降或业务不可用。
业务场景
业务购买分布式版集群后,需要将连接、数据分片、表结构和事务设计串联起来使用。本教程以文档业务为例:documents 表保存文档信息,document_revisions 表保存文档修订记录,两张表按 doc_id 分片并加入同一亲和组;document_status 是读多写少的状态字典,创建为复制表。
方案架构
计算节点(CN)接收业务请求,决定 SQL 的执行方式,将任务分发给数据节点(DN),并汇总结果。CN 还维护表结构和分片位置等信息,并参与复制表的读写。DN 存储分片数据并执行查询。规划时应分别评估两类节点的资源需求:
CN 的主要压力来自处理连接、制定执行计划和汇总结果,受连接数、并发请求和 SQL 复杂度影响。
DN 的主要压力来自执行查询、存储数据和读写磁盘,受数据量以及请求是否集中访问少量数据影响。数据分散到多个 DN 并不代表每条 SQL 都能获得相同的性能收益,分片键、关联关系和事务范围仍会影响实际路由与执行开销。
实施步骤
规划实例与连接地址
选择 CN 和 DN 数量
初次评估可以从 1 个 CN、4 个 DN 的组合开始压测,再根据实际负载调整。CN 与 DN 的数量比例可在 1:2 到 1:8 之间评估,具体取决于业务特征;该比例仅用于规划参考,不是固定配比或产品限制。
选择连接地址
购买完成后,集群详情页会提供主地址和默认分布式代理地址。按业务用途和下表中的适用前提选择连接入口。
地址 | 连接范围 | 推荐用途 |
主地址 | 主 CN | 建表、分布式化及增删改查。 |
默认分布式代理地址 | 集群内的 CN,随 CN 增减调整 | 建表和分布式化;通过多个 CN 分担已分布式化表的读写请求。 |
自定义地址 | 自行选择的 CN 集合 | 按业务划分连接入口,控制请求在哪些 CN 上负载均衡。 |
自定义地址是否自动包含新增 CN,取决于创建或配置地址时选择的选项。自定义地址可以隔离 CN 层的请求负载,但业务表仍可能共享 DN 资源,应结合数据分布评估整体隔离效果。
普通本地表不能通过已挂载或会自动挂载从 CN 的地址读写,如果业务仍需读写未分布式化的普通本地表时,不能使用已挂载从 CN ,或扩容时会自动挂载新增从 CN 的分布式代理地址或自定义地址。使用这类地址前,业务读写涉及的所有表必须已分布式化,包括以下类型:
这些表在所有 CN 上均可见。未分布式化的普通本地表仅在其所在 CN 上可见。请求被分配到从 CN 时,会因找不到表而读写失败。
重要默认分布式代理地址即使当前只指向主 CN,也不能用于依赖普通本地表的业务读写。尚未扩容时,这些表的访问可能看起来正常。扩容新增 CN 后,默认分布式代理地址会自动挂载新增从 CN,落到从 CN 的请求会因找不到这些表而失败。
业务仍需读写普通本地表时,应连接该表所在的 CN。表位于主 CN 时使用主地址。完成相关表的分布式化后,再使用分布式代理地址或自定义地址。
准备账号、数据库与网络
在 PolarDB 控制台完成账号、数据库和网络准备。
创建账号:创建一个高权限账号用于对象管理,再按业务需要创建普通账号。业务应用使用普通账号,并通过
GRANT获得所需权限。创建数据库:一个分布式集群只能有一个分布式数据库,应将其所有者(owner)设置为前面创建的高权限账号。后续建表、授权和分布式化操作均连接该数据库。
配置白名单:将业务机器的实际访问地址加入集群白名单,并确认业务机器到数据库连接地址的网络可达。
请通过 PolarDB 控制台的数据库管理功能创建数据库,以获得完整的分布式功能。避免使用控制台的 SQL 窗口直接执行 CREATE DATABASE,这种方式创建的只是普通的集中式PolarDB PostgreSQL版数据库。
下文以数据库 appdb 和普通业务账号 app_user 为例,假定两者已在控制台创建。初始化示例由高权限账号通过主地址或分布式代理地址连接分布式数据库 appdb 执行。两种地址均可用于建表、授权和分布式化。业务读写涉及的所有表均已分布式化后,应用可通过分布式代理地址承接上线流量。
按业务访问模式设计分片
分布式化之前,先梳理高频查询、修改和事务涉及的数据,再决定使用哪一列作为分片键(即分布列)将数据分到不同分片,并确定表类型:
业务表创建为分布表。
字典表、配置表等数据量小、读多写少、需要与多张业务表关联的公共表,可创建为复制表,各 CN 和 DN 保存完整副本,DN 可以直接使用本节点的数据完成关联查询。复制表写入需要同步多个节点,不能仅因为表小就跳过压测,直接用其承载高频写入。
使分片键匹配高频查询和修改
高频 SQL 应尽量带有分片键的等值条件,例如 WHERE doc_id = $1,使数据库能根据 doc_id 将请求路由到单个分片。
UPDATE和DELETE应在最外层的WHERE写明目标表的分片键条件,不能只在子查询里增加分片键条件。应用传入的参数类型应与分片键类型匹配,尽量避免在分片键列上套函数或做类型转换。否则数据库可能无法将请求路由到单个分片,需要访问更多分片。
分片键应取值丰富、非空、负载相对均匀,且业务上不修改。不宜使用只有少量取值的状态、类别等字段。
同时检查数据量和请求热点。即使
doc_id的取值很多,大部分请求集中在同一个文档时,增加 DN 也不能将这个分片键值的负载拆到多个 DN。
让经常关联的大表保持亲和
经常关联的大表应优先选择业务含义相同、数据类型相同的分片键,使用相同分片数,并放入同一个亲和组。亲和组会让不同表中分片键值相同的数据放在同一个 DN 上。例如,文档和修订记录按 doc_id 分片并保持亲和后,同一文档的数据可在同一个 DN 上完成关联查询,减少节点间的数据传输。JOIN 条件应写明分片键的等值关系,例如 a.doc_id = b.doc_id。
检查主键、唯一约束和外键
分布表的主键和唯一约束必须包含分片键。例如,按 doc_id 分片的明细表可使用 PRIMARY KEY (doc_id, id)。
将 UNIQUE (id) 改为 UNIQUE (doc_id, id) 后,数据库只保证同一个 doc_id 内的 id 不重复,不同 doc_id 下可以出现相同的 id。业务要求整张表的 id 全局不重复时,还需要确保生成的 id 满足这一要求。
分布表之间建立外键时,通常要求两张表处于同一亲和组,且外键两侧包含对应的分片键。迁移集中式PolarDB PostgreSQL版数据库中的表结构时,应逐项检查外键,以及更新或删除一张表中的记录时自动修改关联记录的规则,尤其要避免通过这类规则修改分片键。
将高频事务尽量限定在一个分片键值内
一次事务只修改同一亲和组各表中一个 doc_id 的数据时,可以减少参与事务的 DN 数量。一次事务需要访问多个分片时,应单独压测其处理能力和响应时间,并验证并发读写、事务提交和回滚的结果是否符合业务要求。分布表上的 DML 执行限制和写法,请参见分布表DML操作说明。
处理序列与触发器
自增列使用 64 位序列
序列与自增列的类型需要在建表和分布式化之前确定,触发器则需要单独评估。
自增列请使用
BIGSERIAL,不要使用GENERATED ALWAYS AS IDENTITY或GENERATED BY DEFAULT AS IDENTITY定义的标识列(Identity Column)。如果已有表使用了 Identity 列,建议在分布式化前改为
BIGSERIAL方式的序列自增列,否则后续修改分布式表结构的操作可能失败。显式创建序列时,也应保证序列及接收其值的列使用适合分布式场景的 64 位类型。
分布式版集群为不同节点分配不同的序列值范围,使多个节点能生成互不冲突的 64 位序列值。因此,业务不应假定序列值连续,也不能根据不同节点生成的序列值大小判断记录生成时间的先后。
16 位和 32 位序列,例如 SMALLSERIAL、SERIAL 对应的序列,只能在主 CN 生成值。应用通过分布式代理地址写入时,请求落到其他 CN 并需要生成这类序列值,可能会报错。上线前应检查所有默认值和显式 nextval() 调用。序列的限制和用法,请参见分布式Sequence的使用及限制。
使用 BIGSERIAL 可以让多个节点生成互不冲突的自增值,但不代表 INSERT ... SELECT 一定能直接在 DN 上完成。对于包含 nextval() 或序列默认值的语句,还应在目标版本上使用 EXPLAIN 查看执行计划,核对是否需要将查询结果传回 CN 处理。
触发器
分布式业务一般不推荐使用触发器。迁移已有业务时,应检查触发器能否正常执行、在哪个节点执行、是否需要访问其他表,以及多个请求同时执行时结果是否正确。
建表、授权与分布式化
以下示例按“业务场景”中定义的文档业务模型实施:先创建表结构,再为业务账号授权,最后转换为分布表和复制表。本示例覆盖新建空表再分布式化的流程,已有数据表的迁移不在本文展开。
创建普通 PostgreSQL 表
先建立表结构。自增列使用 BIGSERIAL,修订记录的主键包含分片键 doc_id。
CREATE TABLE documents (
doc_id BIGSERIAL PRIMARY KEY,
title TEXT NOT NULL,
status_id INTEGER NOT NULL
);
CREATE TABLE document_revisions (
doc_id BIGINT NOT NULL,
id BIGSERIAL NOT NULL,
content TEXT NOT NULL,
PRIMARY KEY (doc_id, id)
);
CREATE TABLE document_status (
status_id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);本例不创建外键。业务需要数据库强制维护引用关系时,应按前述约束要求设计并验证后再添加。
为业务账号授权
按业务所需权限授权。以下示例允许 app_user 读写文档与修订记录,只读状态字典,并使用自增列对应的序列。
GRANT CONNECT ON DATABASE appdb TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE
ON TABLE documents, document_revisions TO app_user;
GRANT SELECT ON TABLE document_status TO app_user;
GRANT USAGE ON SEQUENCE documents_doc_id_seq,
document_revisions_id_seq TO app_user;表权限与序列权限相互独立,仅授予 INSERT 不代表可以调用序列生成默认值。上述授权只针对列出的对象;后续新增表或序列时,也需要同步检查业务账号权限。
转换为分布表和复制表
以下示例先为文档表建立独立亲和组,再将修订记录表加入该组,便于与其他业务的表分开管理。最后将状态字典转换为复制表。
SELECT create_distributed_table(
'documents', 'doc_id', colocate_with := 'none'
);
SELECT create_distributed_table(
'document_revisions', 'doc_id', colocate_with := 'documents'
);
SELECT create_reference_table('document_status');创建分布表时,未指定 colocate_with 的情况下,分片键数据类型相同、分片数(shard_count)相同的表默认会加入同一个亲和组;对于文本分片键,排序规则也需要一致。未单独指定分片数时,使用 polar_cluster.shard_count 的值,默认值为 32,可从默认值开始评估;需要按业务划分亲和组时,可使用 colocate_with := 'none' 创建独立亲和组,再使用 colocate_with := '表名' 将相关表加入该组。表类型、亲和组和 DDL 操作的详细说明,请参见创建与管理分布式表。
转换后,使用以下查询核对表类型、分片键、分片数和亲和组。
SELECT table_name, polar_cluster_table_type, distribution_column,
shard_count, colocation_id
FROM polar_cluster_tables
WHERE table_name IN (
'documents'::regclass,
'document_revisions'::regclass,
'document_status'::regclass
)
ORDER BY table_name::text;确认 documents 和 document_revisions 的表类型为 distributed、分片键为 doc_id、分片数相同且 colocation_id 相同。确认 document_status 的表类型为 reference。
编写带分片条件的 SQL
以下查询通过 WHERE d.doc_id = 1001 指定文档,并通过 r.doc_id = d.doc_id 关联同一文档的修订记录,使查询能在存放该文档的 DN 上完成。
SELECT d.doc_id, d.title, r.id, r.content, s.name AS status_name
FROM documents AS d
JOIN document_revisions AS r ON r.doc_id = d.doc_id
JOIN document_status AS s ON s.status_id = d.status_id
WHERE d.doc_id = 1001;更新和删除也应直接写明目标表的分片键条件。以下语句仅用于说明条件写法,执行前应替换为业务需要操作的记录。
UPDATE document_revisions
SET content = '修订后的内容'
WHERE doc_id = 1001 AND id = 2001;
DELETE FROM document_revisions
WHERE doc_id = 1001 AND id = 2001;验证业务并上线
完成表结构设计和分布式化后,使用普通业务账号连接分布式代理地址测试完整业务流程,而不是只使用高权限账号完成测试。
检查项 | 验证内容 |
连接和权限 | 白名单、连接地址、数据库名称正确;表与序列权限满足需求。 |
多 CN 写入 | 在多个 CN 承接请求时验证增删改查,特别是依赖序列默认值的插入。 |
单分片路由 | 使用 |
关联与批量写入 | 检查参与 |
约束与事务 | 验证唯一约束、外键、事务回滚,以及访问多个分片时的读写结果是否符合业务要求。 |
热点与容量 | 模拟生产的数据量、请求集中访问少量数据的情况及并发量,观察 CN、DN 的资源使用和响应时间。 |
例如,可先使用不会实际执行更新的 EXPLAIN,检查以下更新是否只访问 doc_id = 1001 所在的分片。
EXPLAIN (VERBOSE)
UPDATE document_revisions
SET content = '修订后的内容'
WHERE doc_id = 1001 AND id = 2001;需要实测时,再在测试环境使用 EXPLAIN ANALYZE;该命令会实际执行语句。查询优化方法,请参见优化分布表查询。业务测试和压测满足预期后,可通过分布式代理地址承接正式流量。
调整规格和扩缩容
调整规格:CN 和 DN 可以分别升配或降配。调整前应按方案架构中所述的两类节点压力来源评估实际负载,再决定调整哪类节点。降配前应确认目标规格能够承受业务峰值,并留有必要的资源余量。
增加或删除节点:
增加 CN:默认分布式代理地址会包含新增 CN;自定义地址是否自动包含新增 CN,取决于配置时的选项。扩容后应按地址配置核对实际包含的节点集合。
增加 DN:新增节点本身不会自动迁移已有分片,需要执行分片平衡(Rebalance),才能让新 DN 承接已有数据和相应负载。
删除 DN:缩容时,管控流程会自动执行 Rebalance,完成待删除节点上的分片迁移。
删除 CN:评估剩余 CN 能否承接连接和查询负载。
分片平衡的具体操作方法、任务状态与进度的查看方式不在本文展开。多租户业务还可结合多租户管理和租户隔离,规划不同租户的数据存放在哪些节点上。
后续步骤
阅读创建与管理分布式表,了解分布表、复制表、亲和组和 DDL 操作。
阅读分布表DML操作说明,了解分布表上的 DML 执行限制和写法。
阅读优化分布表查询,检查路由、关联和执行计划。
阅读分布式Sequence的使用及限制,规划序列与自增列。