• 在 Kubernetes 上快速测试 Citus 分布式 PostgreSQL 集群(分布式表,共置,引用表,列存储)


    image

    准备工作

    这里假设,你已经在 k8s 上部署好了基于 Citus 扩展的分布式 PostgreSQL 集群。

    查看 Citus 集群(kubectl get po -n citus),1Coordinator(协调器) 节点 + 3Worker(工作器) 节点。

    NAME                  READY   STATUS    RESTARTS   AGE
    citus-coordinator-0   2/2     Running   0          3h55m
    citus-worker-0        2/2     Running   0          22m
    citus-worker-1        2/2     Running   0          21m
    citus-worker-2        2/2     Running   0          21m
    

    进入 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');
    

    插入设备元数据

    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存储过程分布式事务

    创建引用表

    当您需要不包含分布列的快速 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) 的对比。压缩了几十倍,效果非常的惊人,大大节省了存储空间。

    您可以单独使用列存储,也可以在分布式表中使用,以结合压缩和分布式查询引擎的优势。

    使用列式存储时,您应该只使用 COPYINSERT..SELECT 批量加载数据以实现良好的压缩。柱状表目前不支持更新、删除和外键。 但是,您可以使用分区表,其中较新的分区使用基于行的存储,而较旧的分区使用列存储进行压缩。

    更多

  • 相关阅读:
    人工智能的前世今生与未来
    想开发DAYU200,我教你
    【云原生.大数据】镜像仓库Harbor对接MinIO对象存储
    图片编辑软件怎样加文字内容?图片添加文字方法大分享
    探索 GO 项目依赖包管理与Go Module常规操作
    Linux:syslog()的使用和示例
    lxparse:解析列表页链接和详情页内容
    【PyTorch】如何取得预训练模型的标签label列表(以 Alexnet 在 ImageNet 上的预训练模型为例)
    正规黄金代理的三大要素
    计算电磁学(三)有限差分方法
  • 原文地址:https://www.cnblogs.com/hacker-linner/p/16049278.html