全部產品
Search
文件中心

PolarDB:分布表DML操作說明

更新時間:Sep 12, 2025

PolarDB PostgreSQL分布式版叢集中,您可以使用標準的PostgreSQL DML命令(如INSERTUPDATEDELETE)來操作資料。雖然文法保持一致,但其執行行為和效能表現與資料在節點間的分布方式密切相關。為確保資料操作的正確性和高效性,請首先瞭解以下核心原則。

DML操作的核心原則與限制

  • 插入時必須指定分布列:執行INSERT語句時,必須包含分布列。系統需要根據分布列計算雜湊值,以確定資料應插入到哪個物理分區。若缺少分布列,操作將失敗。

    樣本:假設id是分布列,若插入時未包含分布列id,則會報錯。

    INSERT INTO t (data) VALUES ('TEST');

    返回結果如下:

    ERROR:  cannot perform an INSERT without a partition column value
  • 不支援修改分布列的值:一旦資料行被寫入,其所在的位置就由分布列的值確定。因此,在UPDATEINSERT ... ON CONFLICT DO UPDATE語句中,不允許修改分布列的值,因為這會涉及到資料在物理節點間的遷移,此類操作不被支援。

    樣本:假設id是分布列,先插入id=1的值後,嘗試修改分布列的值。

    -- 插入未經處理資料
    INSERT INTO t (id, data) VALUES (1, 'TEST');
    -- 嘗試修改分布列的值
    UPDATE t SET id = 10 WHERE id = 1;

    返回結果如下:

    ERROR:  modifying the partition value of rows is not allowed
  • 跨節點事務的一致性說明:PolarDB PostgreSQL分布式版使用兩階段交易認可(2PC)來保證分散式交易的原子性。但在預設的讀已提交(Read Committed)隔離等級下,暫不支援跨節點的快照隔離(Snapshot Isolation,SI)。

    這意味著,一個涉及多個資料節點(DN)的事務,其在各節點上的提交時間點可能存在微小延遲。在此短暫的間隙中,並發的SELECT查詢可能會觀察到不一致的中間狀態(例如,在一個節點上看到新資料,在另一個節點上仍是舊資料)。如果您的業務對跨節點資料一致性有嚴格要求,需要在應用程式層進行特殊處理,如使用鎖或規避讀取最新資料。

常用DML操作

INSERT:插入資料

支援單行、多行插入以及ON CONFLICT子句來處理資料衝突。

  • 單行插入。

    -- 隱式列文法,按照表中列的順序插入資料。
    INSERT INTO t VALUES (1, 'TEST');
    -- 顯示列文法,明確指定要為哪些列插入資料。
    INSERT INTO t (id, data) VALUES (1, 'TEST');
  • 批量插入。

    INSERT INTO t VALUES (1, 'TEST'), (2, 'TEST'), (3, 'TEST');
  • INSERT...SELECT:將資料從源表匯入目標表。

    1. 建立源表與目標表。

      -- 源表
      CREATE TABLE source (id int, data text);
      SELECT create_distributed_table('source', 'id');
      -- 目標表
      CREATE TABLE target (id int, data text);
      SELECT create_distributed_table('target', 'id');
    2. 匯入資料。

      -- 當源表與目標表結構一致時
      INSERT INTO target SELECT * FROM source;
      -- 手動指定目標列集與資料列集
      INSERT INTO target(id, data) SELECT id, data FROM source;
  • ON CONFLICT:處理資料衝突

    • 問題:如果插入表存在主鍵約束(PRIMARY KEY)、唯一性限制式(UNIQUE)或者排他性約束(EXCLUDE)時,插入資料時可能會出現衝突。可能出現報錯如下:

      ERROR:  duplicate key value violates unique constraint "xxx"
    • 解決方式:

      • ON CONFLICT DO NOTHING:存在衝突時,忽略新資料。

        INSERT INTO target VALUES (1, 'NEW_DATA') ON CONFLICT(id) DO NOTHING;
      • ON CONFLICT DO UPDATE:存在衝突時,更新指定列。同時,PolarDB PostgreSQL分布式版支援使用EXCLUDED來引用被衝突排除的新值。

        -- 當存在衝突時,更新指定列為固定值,例如'ERROR'。
        INSERT INTO target VALUES (1, 'NEW_DATA') ON CONFLICT(id) DO UPDATE SET data='ERROR';
        -- 使用EXCLUDED來引用被衝突排除的新值
        INSERT INTO target VALUES (1, 'NEW_DATA') ON CONFLICT(id) DO UPDATE SET data = EXCLUDED.data;

UPDATE/DELETE:更新與刪除資料

文法與標準PostgreSQL完全相同。建議使用WHERE子句高效地定位到目標資料。

  • 更新資料:

    UPDATE t SET data = 'CHANGED' WHERE id = 1;
  • 刪除資料:

    DELETE FROM t WHERE id = 1;

COPY:批量載入資料

使用COPY命令可以從檔案高效地載入大量資料。請注意psql用戶端中的COPY\COPY的區別:

  • COPY ... FROM 'file_path':從資料庫(CN或DN節點)的檔案系統讀取檔案。

    COPY target FROM '/path/to/data.csv' WITH CSV HEADER;
  • \COPY ... FROM 'file_path':從執行psql命令的用戶端機器讀取檔案,並將其串流到資料庫。

    \COPY target FROM '/path/on/client/data.csv' WITH CSV HEADER;

DML效能最佳化

複雜的DML(尤其是INSERT...SELECT和多表UPDATE/DELETE)有多種執行模式,效能差異巨大。您可以通過EXPLAIN命令查看執行計畫,判斷其執行模式。

DML執行模式

執行模式

效能評級

觸發條件

核心思想

下推並存執行

★★★★★

  • 僅涉及一張分布表。

  • 涉及多張分布表時,需滿足以下所有條件:

    • 由多張分布表或複製表組成。

    • 分布表同屬於一個親和組

    • UPDATE/DELETE命令涉及的分布表均使用分布列進行關聯。

計算完全下推到各資料節點並存執行,無節點間資料轉送。

重新分區執行

★★

INSERT...SELECT中,源表和目標表的分布鍵不匹配。

將源表資料在節點間重新分發,以匹配目標表的分布規則。

上拉並重新導向執行

DML語句中包含ORDER BYLIMITOFFSETGROUP BY(如果分組鍵不包含分布列)等子句。

將中間結果拉取到協調節點處理,再下發到目標節點,網路開銷大。

下推並存執行

  • DML操作僅涉及一張分布表。

    -- 簡單插入語句
    INSERT INTO t VALUES (1, 'TEST'), (2, 'TEST');
    -- 簡單更新語句
    UPDATE t SET data = 'CHANGE' WHERE id = 1;
    -- 簡單刪除語句
    DELETE FROM t WHERE id = 1;
  • DML操作多張分布表。

    樣本

    1. 假設存在source表與target表,表結構如下所示,兩表處於同一個親和組,分布列均為id列:

      -- 源表
      CREATE TABLE source (id int, data text);
      SELECT create_distributed_table('source', 'id');
      -- 目標表
      CREATE TABLE target (id int, data text);
      SELECT create_distributed_table('target', 'id');
    2. 查看查詢親和組資訊。

      SELECT logicalrelid, colocationid FROM pg_dist_partition;

      返回結果如下:

       logicalrelid | colocationid
      --------------+--------------
       target       |            1
       source       |            1
    3. 當執行如下DML操作時,可以在單一節點內部執行,因此可以下推並存執行:

      -- 插入語句樣本
      INSERT INTO target(id, data) SELECT id, data FROM source;
      -- 更新語句樣本
      UPDATE target SET data = source.data FROM source WHERE target.id = source.id;
      -- 刪除語句樣本
      DELETE FROM target USING source WHERE target.id = source.id;
    4. INSERT...SELECT命令為例,典型可下推的命令將會產生若干獨立、可下推執行的子任務:

      EXPLAIN INSERT INTO target(id, data) SELECT id, data FROM source;

      返回結果如下:

                                                 QUERY PLAN                                            
      --------------------------------------------------------------------------------------------------
       Custom Scan (PolarCluster Adaptive)  (cost=0.00..0.00 rows=0 width=0)
         Task Count: 4 --任務被分發到4個分區上執行
         Tasks Shown: One of 4 -- 每個分區都在本地執行INSERT...SELECT
         ->  Task
               Node: host=10.xxx.xxx.xxx port=3006 dbname=testdb
               ->  Insert on target_102105 polar_cluster_table_alias  (cost=0.00..22.70 rows=0 width=0)
                     ->  Seq Scan on source_102101 source  (cost=0.00..22.70 rows=1264 width=36)
                           Filter: (id IS NOT NULL)
      (8 rows)

重新分區執行

INSERT...SELECT的源表和目標表分布規則不一致時,即不能在單一節點內執行DML操作時,則可能觸發資料重分區。當觸發重分區後,源節點會將需要的資料打包並轉寄到匹配的目的節點上,用於後續命令的執行。

樣本

  1. 假設存在source2表與target2表,表結構如下所示:

    -- 源表
    CREATE TABLE source2 (id int, t_id int, data text);
    SELECT create_distributed_table('source2', 'id');
    -- 目標表
    CREATE TABLE target2 (id int, data text);
    SELECT create_distributed_table('target2', 'id');
  2. 當執行如下INSERT...SELECT命令時,將會觸發重新分區執行,執行計畫中會顯示當前命令的執行方式repartitionINSERT的目標列是id,但SELECT出來作為id的卻是t_id列。

    EXPLAIN INSERT INTO target2(id, data) SELECT t_id, data FROM source2;

    返回結果如下:

                                             QUERY PLAN                                          
    ---------------------------------------------------------------------------------------------
     Custom Scan (PolarCluster INSERT ... SELECT)  (cost=0.00..0.00 rows=0 width=0)
       INSERT/SELECT method: repartition -- 明確提示發生了資料重分區
       ->  Custom Scan (PolarCluster Adaptive)  (cost=0.00..0.00 rows=100000 width=36)
             Task Count: 4
             Tasks Shown: One of 4
             ->  Task
                   Node: host=10.188.91.26 port=3006 dbname=testdb
                   ->  Seq Scan on source2_102117 source2  (cost=0.00..22.00 rows=1200 width=36)
    (8 rows)

上拉並重新導向執行

當DML語句包含無法下推的子句(如ORDER BYLIMITOFFSETGROUP BY(如果分組鍵不包含分布列))時,資料必須先被拉取到協調節點(Coordinator Node,CN),在CN上完成合併操作後,再將結果重新下發到目標節點。這會造成CN節點的瓶頸和大量的網路傳輸。

樣本

  1. 假設存在source3表與target3表,表結構如下所示:

    -- 源表
    CREATE TABLE source3 (id int, t_id int, data text);
    SELECT create_distributed_table('source3', 'id');
    -- 目標表
    CREATE TABLE target3 (id int, data text);
    SELECT create_distributed_table('target3', 'id');
    
  2. 當執行如下INSERT...SELECT命令時,將會觸發上拉並重新導向執行,執行計畫中會顯示當前命令的執行方式pull to coordinator

    EXPLAIN INSERT INTO target3(id, data) SELECT t_id, data FROM source3 LIMIT 1;

    返回結果如下:

                                                   QUERY PLAN                                                
    ---------------------------------------------------------------------------------------------------------
     Custom Scan (PolarCluster INSERT ... SELECT)  (cost=0.00..0.00 rows=0 width=0)
       INSERT/SELECT method: pull to coordinator -- 明確提示資料被拉取到協調節點
       ->  Limit  (cost=0.00..0.00 rows=1 width=36)
             ->  Custom Scan (PolarCluster Adaptive)  (cost=0.00..0.00 rows=100000 width=36)
                   Task Count: 4
                   Tasks Shown: One of 4
                   ->  Task
                         Node: host=10.188.91.26 port=3006 dbname=testdb
                         ->  Limit  (cost=0.00..0.02 rows=1 width=36)
                               ->  Seq Scan on source3_102125 source3  (cost=0.00..22.00 rows=1200 width=36)
    (10 rows)