全部產品
Search
文件中心

PolarDB:建立與管理分布表和複製表

更新時間:Sep 12, 2025

隨著業務發展,單張表的效能和容量會成為瓶頸。傳統解決方案是垂直擴充(Scale Up),即提升單個伺服器的硬體設定,但這很快會遇到性價比和物理極限。水平擴充(Scale Out)通過將資料分散到多個伺服器,提供了近乎無限的擴充能力。PolarDB PostgreSQL分布式版叢集支援水平擴充,它引入了兩種特殊的表類型來管理資料分布:分布表(用於儲存海量資料)和複製表(用於儲存需要被頻繁關聯的小資料)。本文將指導您如何建立和管理這兩種表。

建立分布表

建立分布表是實現資料水平分割的核心步驟,適用於儲存海量資料的業務表,如使用者表、訂單詳情表等。其過程分為兩步:首先建立一張普通表,然後通過create_distributed_table函數將其轉換為分布表。

1. 選擇分布列

分布列(Distribution Column)是決定資料如何分布到不同資料節點(DN)的關鍵。系統會根據指定列的雜湊值來路由資料行。

  • 選擇原則:通常選擇表的主鍵或唯一標識作為分布列,這樣可以確保資料均勻分布。

  • 重要限制:如果表上存在主鍵或唯一約束,分布列必須是構成這些約束的列之一。

樣本:將普通表t轉換為分布表,並以id列作為分布列。

  1. 建立一張普通表t

    CREATE TABLE t (id int primary key, data text);
  2. 轉換為分布表,並以id列作為分布列。

    SELECT create_distributed_table('t', 'id');

    返回結果如下:

     create_distributed_table 
    --------------------------
     
    (1 row)

2.(可選)指定分區數量

分區(Shard)是分布表的實體儲存體單元。預設情況下,每張分布表會被建立為32個分區。您也可以在建立時顯式指定分區數量,或通過polar_cluster.shard_count參數進行全域設定。

樣本:建立一張有4個分區的分布表。

  1. 建立一張普通表t1

    CREATE TABLE t1 (id int primary key, data text);
  2. 顯式指定分區數量。

    通過shard_count參數

    1. 在轉換時通過shard_count參數指定。

      SELECT create_distributed_table('t1', 'id', shard_count := 4);

      返回結果如下:

       create_distributed_table 
      --------------------------
       
      (1 row)
    2. 查詢分區數量。

      SELECT * FROM pg_dist_shard WHERE logicalrelid = 't1'::regclass;

      返回結果如下:

      logicalrelid | shardid | shardstorage | shardminvalue | shardmaxvalue 
      --------------+---------+--------------+---------------+---------------
       t1           |  102072 | t            | -2147483648   | -1073741825
       t1           |  102073 | t            | -1073741824   | -1
       t1           |  102074 | t            | 0             | 1073741823
       t1           |  102075 | t            | 1073741824    | 2147483647
      (4 rows)

    通過polar_cluster.shard_count參數

    1. 通過polar_cluster.shard_count參數進行全域設定。

       SET polar_cluster.shard_count TO 4;
    2. 轉換為分布表。

      SELECT create_distributed_table('t1', 'id');

      返回結果如下:

       create_distributed_table 
      --------------------------
       
      (1 row)
    3. 查詢分區數量。

      SELECT * FROM pg_dist_shard WHERE logicalrelid = 't1'::regclass;

      返回結果如下:

      logicalrelid | shardid | shardstorage | shardminvalue | shardmaxvalue 
      --------------+---------+--------------+---------------+---------------
       t1           |  102072 | t            | -2147483648   | -1073741825
       t1           |  102073 | t            | -1073741824   | -1
       t1           |  102074 | t            | 0             | 1073741823
       t1           |  102075 | t            | 1073741824    | 2147483647
      (4 rows)

3. (可選)使用親和組最佳化JOIN效能

在業務模型中,一個實體的相關資訊可能分布在多張表內,通常需要進行關聯查詢JOIN才可以獲得完整的資訊。例如,user_info表存放所有的使用者資料,user_order存放所有的使用者訂單,兩者需要通過user_id關聯。

在分散式資料庫中,如果這兩張表的資料隨機分布在不同節點,關聯查詢就會觸發跨節點的資料轉送,開銷極大。為解決此問題,PolarDB PostgreSQL分布式版入了親和組(Colocation Group)的概念。

  • 作用:確保多張表中具有相同分布索引值的資料行(例如user_id為1001的所有相關記錄)始終物理地存放在同一個資料節點上。這樣,基於分布鍵的JOIN操作就可以在節點內部高效完成,效能等同於本地查詢。

  • 如何使用PolarDB PostgreSQL分布式版提供了兩種管理親和組的方式:隱式(預設行為)和顯式(推薦)。

    • 預設親和(隱式行為):當您建立分布表時,如果未指定colocate_with參數,系統會根據分布列類型分區數量這兩個特徵,自動將表放入一個預設的親和組。這意味著所有分布列類型和分區數都相同的表,會被預設視為親和。

      說明

      預設親和(隱式行為)即使具有相同的分布列資料類型和分區數量,兩張分布表中的資料也並不一定是互相關聯的。

    • 顯式控制(推薦方式):預設行為可能導致業務上無關聯的表被錯誤地分到同一組。為精確控製表的親和關係,建議採用顯式聲明:

      1. 建立親和組中的第一張表時,在create_distributed_table函數中設定colocate_with := 'none',這將為它建立一個全新的、獨立的親和組。

      2. 建立後續需要親和的表時,設定colocate_with := '第一張表的表名',將其準確地加入已建立的親和組。

樣本:將使用者表和訂單表放入同一個親和組,而動物相關的表放入另一個組。

  1. 建立使用者與訂單相關表,並放入同一個親和組。

    -- 建立一個新親和組
    CREATE TABLE user_info (user_id int, user_data text);
    SELECT create_distributed_table('user_info', 'user_id', colocate_with := 'none');
    -- 加入已建立的親和組中
    CREATE TABLE user_order (user_id int, order_id int, order_data text);
    SELECT create_distributed_table('user_order', 'user_id', colocate_with := 'user_info');
  2. 動物相關的表,並放入另一個親和組。

    -- 建立一個新親和組
    CREATE TABLE animal (animal_id int, animal_data text);
    SELECT create_distributed_table('animal', 'animal_id', colocate_with := 'none');
    -- 加入已建立的親和組中
    CREATE TABLE animal_class (animal_id int, class_id int, class_data text);
    SELECT create_distributed_table('animal_class', 'animal_id', colocate_with := 'animal');
  3. 驗證兩組分布表有著不同的親和組id,意味著各自被加入到了一個親和組中。

    SELECT table_name, colocation_id, polar_cluster_table_type, distribution_column, shard_count
    FROM polar_cluster_tables
    WHERE table_name IN (
      'user_info'::regclass, 'user_order'::regclass,
      'animal'::regclass, 'animal_class'::regclass)
    ORDER BY colocation_id;

    返回結果如下:

      table_name  | colocation_id | polar_cluster_table_type | distribution_column | shard_count 
    --------------+---------------+--------------------------+---------------------+-------------
     user_info    |             3 | distributed              | user_id             |           4
     user_order   |             3 | distributed              | user_id             |           4
     animal       |             4 | distributed              | animal_id           |           4
     animal_class |             4 | distributed              | animal_id           |           4
    (4 rows)

建立複製表

複製表(Reference Table)的資料在所有資料節點上都存有一份完整的副本。它適用於儲存資料量不大但需要與分布表頻繁關聯的公用資料或維度資料表,例如國家代碼、商品分類等。

  • 優勢:避免跨節點查詢,加速關聯操作。

  • 代價:寫入操作會同步到所有節點,開銷較大,因此不適合頻繁變更的資料。

樣本:建立一張複製表。

  1. 建立一張普通表t_reference

    CREATE TABLE t_reference (id int primary key, data text);
  2. 轉換為複製表,僅需要指定表名即可。

    SELECT create_reference_table('t_reference');

    返回結果如下:

     create_reference_table 
    ------------------------
     
    (1 row)
  3. 查詢複製表的資訊,可以看到複製表在所有節點上都有一個同名的分區:

    SELECT table_name, polar_cluster_table_type, distribution_column, shard_count
    FROM polar_cluster_tables
    WHERE table_name = 't_reference'::regclass;

    返回結果如下:

     table_name  | polar_cluster_table_type | distribution_column | shard_count 
    -------------+--------------------------+---------------------+-------------
     t_reference | reference                | <none>              |           1
    (1 row)
    SELECT table_name, shardid, nodename, nodeport
    FROM polar_cluster_shards
    WHERE table_name = 't_reference'::regclass;

    返回結果如下:

     table_name  | shardid |   nodename     | nodeport 
    -------------+---------+----------------+----------
     t_reference |  102096 | 10.xxx.xxx.xxx |     3007
     t_reference |  102096 | 10.xxx.xxx.xxx |     3020
     t_reference |  102096 | 10.xxx.xxx.xxx |     3006
     t_reference |  102096 | 10.xxx.xxx.xxx |     3003
    (4 rows)

管理分布表

將分布錶轉換回普通表

如果不再需要分布式特性,可以使用undistribute_table函數將分布表或複製表還原為單機表。資料會自動從各分區彙集到主CN節點。

樣本:將分布表t轉換為普通表。

SELECT undistribute_table('t');

返回結果如下:

NOTICE:  creating a new table for public.t
NOTICE:  moving the data of public.t
NOTICE:  dropping the old public.t
NOTICE:  renaming the new table to public.t
 undistribute_table 
--------------------
 
(1 row)

其他DDL操作

對於已建立的分布表,您可以像操作普通PostgreSQL表一樣執行其他DDL操作。這些操作會自動下推到所有物理分區上,以保持邏輯表與物理表結構的一致性。

  • 刪除表DROP TABLE table_name;

  • 建立索引CREATE INDEX index_name ON table_name (column_name);