在|在 Kubernetes 上快速测试 Citus 分布式 PostgreSQL 集群(分布式表,共置,引用表,列存储)
文章图片
准备工作
这里假设,你已经在 k8s
上部署好了基于 Citus
扩展的分布式 PostgreSQL
集群。
查看 Citus
集群(kubectl get po -n citus
),1 个 Coordinator(协调器)
节点 +
3 个 Worker(工作器)
节点。
NAMEREADYSTATUSRESTARTSAGE
citus-coordinator-02/2Running03h55m
citus-worker-02/2Running022m
citus-worker-12/2Running021m
citus-worker-22/2Running021m
进入
coordinator
节点(kubectl -n citus exec -it citus-coordinator-0 -- bash
),查看活动的 worker
节点(psql 'host=citus-coordinator user=postgres' -c "SELECT * FROM citus_get_active_worker_nodes();
"
)。node_name| node_port
-----------------------------------------------------+-----------
citus-worker-1.citus-worker.citus.svc.cluster.local |6432
citus-worker-2.citus-worker.citus.svc.cluster.local |6432
citus-worker-0.citus-worker.citus.svc.cluster.local |6432
(3 rows)
一旦拥有
Citus
集群,就可以开始创建分布式表
、引用表
和使用列存储
。创建分布式表
create_distributed_table
将在本地或工作节点之间透明地切分您的表。进入命令行工具:
psql 'host=citus-coordinator user=postgres'
建表
CREATE TABLE events (
device_id bigint,
event_id bigserial,
event_time timestamptz default now(),
data jsonb not null,
PRIMARY KEY (device_id, event_id)
);
-- 将事件表分布在本地或工作节点上的分片上
SELECT create_distributed_table('events', 'device_id');
执行此操作后,对特定设备
ID
的查询将有效地路由到单个工作节点,而跨设备 ID
的查询将在集群中并行化。插入一些事件
INSERT INTO events (device_id, data)
SELECT s % 100, ('{"measurement":'||random()||'}')::jsonb FROM generate_series(1,1000000) s;
-- INSERT 0 1000000
获取设备 1 的最后 3 个事件,路由到单个节点
命令行开启计时:
postgres=# \timing
SELECT * FROM events WHERE device_id = 1 ORDER BY event_time DESC, event_id DESC LIMIT 3;
device_id | event_id |event_time|data
-----------+----------+-------------------------------+-------------------------------------
1 |999901 | 2022-03-24 02:30:50.205478+00 | {"measurement": 0.8822990134507691}
1 |999801 | 2022-03-24 02:30:50.205478+00 | {"measurement": 0.5239176115816448}
1 |999701 | 2022-03-24 02:30:50.205478+00 | {"measurement": 0.9900647926398349}
(3 rows)Time: 4.779 ms
解释跨分片并行化的查询的计划,以下显示了查询其中一个分片的计划以及如何完成跨分片的聚合
执行
sql
语句:EXPLAIN (VERBOSE ON) SELECT count(*) FROM events;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Aggregate(cost=250.00..250.02 rows=1 width=8)
Output: COALESCE((pg_catalog.sum(remote_scan.count))::bigint, '0'::bigint)
->Custom Scan (Citus Adaptive)(cost=0.00..0.00 rows=100000 width=8)
Output: remote_scan.count
Task Count: 32
Tasks Shown: One of 32
->Task
Query: SELECT count(*) AS count FROM public.events_102008 events WHERE true
Node: host=citus-worker-0.citus-worker.citus.svc.cluster.local port=6432 dbname=postgres
->Aggregate(cost=725.00..725.01 rows=1 width=8)
Output: count(*)
->Seq Scan on public.events_102008 events(cost=0.00..650.00 rows=30000 width=0)
Output: device_id, event_id, event_time, data
(13 rows)Time: 5.427 ms
使用共置(Co-location)创建分布式表 具有相同分布列的分布式表可以位于同一位置,以实现分布式表之间的高性能分布式
连接(join)
和外键。 默认情况下,分布式表将根据分布列的类型位于同一位置,但您可以使用 create_distributed_table
中的 colocate_with
参数显式定义同一位置。建表
CREATE TABLE devices (
device_id bigint primary key,
device_name text,
device_type_id int
);
CREATE INDEX ON devices (device_type_id);
-- 将设备表与事件表放在一起
SELECT create_distributed_table('devices', 'device_id', colocate_with := 'events');
【在|在 Kubernetes 上快速测试 Citus 分布式 PostgreSQL 集群(分布式表,共置,引用表,列存储)】插入设备元数据
INSERT INTO devices (device_id, device_name, device_type_id)
SELECT s, 'device-'||s, 55 FROM generate_series(0, 99) s;
可选:确保应用程序只能插入已知设备的事件
ALTER TABLE events ADD CONSTRAINT device_id_fk
FOREIGN KEY (device_id) REFERENCES devices (device_id);
获得跨分片并行的所有类型
55
设备的平均测量值SELECT avg((data->>'measurement')::double precision)
FROM events JOIN devices USING (device_id)
WHERE device_type_id = 55;
avg
--------------------
0.4997412230952178
(1 row)Time: 122.548 ms
Co-location
还可以帮助您扩展 INSERT..SELECT、存储过程和分布式事务。- INSERT..SELECT
- https://docs.citusdata.com/en/stable/articles/aggregation.html
- 存储过程
- https://www.citusdata.com/blog/2020/11/21/making-postgres-stored-procedures-9x-faster-in-citus/
- 分布式事务
- https://www.citusdata.com/blog/2017/06/02/scaling-complex-sql-transactions/
join
或外键时,您可以使用 create_reference_table
在集群中的所有节点之间复制表。建表
CREATE TABLE device_types (
device_type_id int primary key,
device_type_name text not null unique
);
跨所有节点复制表以在任何列上启用外键和 join
SELECT create_reference_table('device_types');
插入设备类型
INSERT INTO device_types (device_type_id, device_type_name) VALUES (55, 'laptop');
可选:确保应用程序只能插入已知类型的设备
ALTER TABLE devices ADD CONSTRAINT device_type_fk
FOREIGN KEY (device_type_id) REFERENCES device_types (device_type_id);
获取类型名称以笔记本电脑开头的设备的最后 3 个事件,跨分片并行
SELECT device_id, event_time, data->>'measurement' AS value, device_name, device_type_name
FROM events JOIN devices USING (device_id) JOIN device_types USING (device_type_id)
WHERE device_type_name LIKE 'laptop%' ORDER BY event_time DESC LIMIT 3;
device_id |event_time|value| device_name | device_type_name
-----------+-------------------------------+---------------------+-------------+------------------
31 | 2022-03-24 02:30:50.205478+00 | 0.9994211581289107| device-31| laptop
31 | 2022-03-24 02:30:50.205478+00 | 0.13771543211483106 | device-31| laptop
88 | 2022-03-24 02:30:50.205478+00 | 0.5585740912470349| device-88| laptop
(3 rows)Time: 96.537 ms
引用表使您能够扩展复杂的数据模型并充分利用关系数据库的功能。
使用列式存储创建表 要在
PostgreSQL
数据库中使用列式存储,您只需将 USING columnar 添加到 CREATE TABLE
语句中,您的数据将使用列式访问方法自动压缩。建表
CREATE TABLE events_columnar (
device_id bigint,
event_id bigserial,
event_time timestamptz default now(),
data jsonb not null
)
USING columnar;
插入一些数据
INSERT INTO events_columnar (device_id, data)
SELECT d, '{"hello":"columnar"}' FROM generate_series(1,10000000) d;
创建一个基于行的表进行比较
CREATE TABLE events_row AS SELECT * FROM events_columnar;
查看表大小
postgres=# \d+
List of relations
Schema |Name|Type|Owner| Persistence | Access method |Size| Description
--------+------------------------------+----------+----------+-------------+---------------+------------+-------------
public | citus_tables| view| postgres | permanent|| 0 bytes|
public | device_types| table| postgres | permanent| heap| 8192 bytes |
public | devices| table| postgres | permanent| heap| 8192 bytes |
public | events| table| postgres | permanent| heap| 8192 bytes |
public | events_columnar| table| postgres | permanent| columnar| 25 MB|
public | events_columnar_event_id_seq | sequence | postgres | permanent|| 8192 bytes |
public | events_event_id_seq| sequence | postgres | permanent|| 8192 bytes |
public | events_row| table| postgres | permanent| heap| 806 MB|
(8 rows)
注意
events_row(806 MB)
与 events_columnar(25 MB)
的对比。压缩了几十倍,效果非常的惊人,大大节省了存储空间。您可以单独使用列存储,也可以在分布式表中使用,以结合压缩和分布式查询引擎的优势。
使用列式存储时,您应该只使用
COPY
或 INSERT..SELECT
批量加载数据以实现良好的压缩。柱状表目前不支持更新、删除和外键。 但是,您可以使用分区表,其中较新的分区使用基于行的存储,而较旧的分区使用列存储进行压缩。更多
- Citus 集群 - 官方快速入门教程
- Citus 集群 - 官方安装指南
- Citus 简介 - 将 Postgres 转换为分布式数据库
- Citus 架构及概念
- Citus 集群官方示例 - 多租户应用程序实战
- Citus 集群官方教程 - 迁移现有应用程序
- Citus 集群)官方示例 - 实时仪表盘
- Citus 集群)官方示例 - 时间序列数据
- 多租户数据库项目实战(Python/Django+Postgres+Citus)
- Citus 集群 - 分布式表中的分布列选择最佳实践
- 扩展分析处理服务(Smartly.io) - 使用 Citus 对 PostgreSQL 数据库进行分片
推荐阅读
- 如何向上管理
- 历史上的今天|【历史上的今天】1 月 4 日(Linux 2.4 内核发布;创世区块诞生;苹果开放 Macintosh 系统授权)
- 历史上的今天|【历史上的今天】3 月 24 日(苹果推出 Mac OS X;微软前任 CEO 出生;Spring 1.0 正式发布)
- vue.js|在vue3中使用vuex 4.x
- SpringBoot如何通过配置文件(yml|SpringBoot如何通过配置文件(yml,properties)限制文件上传大小
- SpringBoot文件上传大小设置方式(yml中配置)
- 一文让你明白Redis持久化
- 数据库|在(快来pick你最喜爱的团队!)
- 蓝桥真题|【蓝桥真题六】三十块的蓝桥省赛模拟真题,做的大一都直呼上当(文末PDF原题)
- vue实现在线预览office文件的示例代码