一文講透數倉臨時表的用法


摘要:臨時表作為一個SQL標准中的表類型,各個廠商在實現時,往往卻不相同,甚至行為上也存在差異,本文小結下GaussDB(DWS)的臨時表使用場景。

本文分享自華為雲社區《GaussDB(DWS)臨時表小結》,作者: sincatter 。

語法介紹

如下為創建表的基本語法(詳見手冊):

CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name 
    ({ column_name data_type [ compress_mode ] [ COLLATE collation ] [ column_constraint [ ... ] ]
        | table_constraint
        | LIKE source_table [ like_option [...] ] }
        [, ... ])
    [ WITH ( {storage_parameter = value} [, ... ] ) ]
    [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
    [ COMPRESS | NOCOMPRESS ]
    [ TABLESPACE tablespace_name ]
    [ DISTRIBUTE BY { REPLICATION | { HASH ( column_name [,...] ) } } ]
    [ TO { GROUP groupname | NODE ( nodename [, ... ] ) } ];

其中臨時表相關的關鍵字有:

  • [ GLOBAL | LOCAL ]

創建臨時表時可以在TEMP或TEMPORARY前指定GLOBAL或LOCAL關鍵字。目前GaussDB(DWS)設立這兩個關鍵字,僅僅是為了兼容SQL標准,實際行為上無論指定的是GLOBAL還是LOCAL,GaussDB(DWS)都只會創建為本地臨時表,即只有LOCAL關鍵字是有效的。

  • { TEMPORARY | TEMP }

TEMP和TEMPORARY等價。如果指定TEMP或TEMPORARY關鍵字,則創建的表為臨時表。臨時表行為上的主要特征為只在當前會話可見,本會話結束后會自動刪除。由於臨時表只在當前會話創建,對於涉及對臨時表操作的DDL語句,很容易產生DDL失敗的報錯。因此,建議DDL語句中不要對臨時表進行操作。

  • ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP }

ON COMMIT選項決定在事務中執行創建臨時表操作,當事務提交時,此臨時表的后續操作。有以下三個選項:

  • PRESERVE ROWS(缺省值):提交時不對臨時表做任何操作,臨時表及其表數據保持不變。
  • DELETE ROWS:提交時刪除臨時表中數據。
  • DROP:提交時刪除此臨時表。

原理介紹

GaussDB(DWS)的臨時表機制繼承自PostgreSQL,臨時表在元數據和數據存儲上與普通表基本無差異,具體來說,臨時表是通過建表時將其Schema指定為與session id相關的一個schema,其他session實際上也是可以在系統表中查看到當前臨時表的元數據。GaussDB(DWS)會利用schema進行臨時表的不同session間隔離。這里通過兩個現象去說明這個機制:

現象一:

一個session_1創建一個臨時表:

postgres=#  create temp table tt1(a int);
CREATE TABLE
postgres=# \d
                                         List of relations
                  Schema                  | Name | Type  | Owner |             Storage              
------------------------------------------+------+-------+-------+----------------------------------
 pg_temp_coordinator1_2_4_139820525504256 | tt1  | table | xucw  | {orientation=row,compression=no}
(1 row)
postgres=# select relname,relnamespace from pg_class where relname like 'tt%';
 relname | relnamespace 
---------+--------------
 tt1     |        24600
(1 row)

另外一個session_2,可以一樣可以通過pg_class查看臨時表的表結構:

postgres=#  select relname,relnamespace from pg_class where relname like 'tt%';
 relname | relnamespace 
---------+--------------
 tt1     |        24600
(1 row)

但session_2中是無法查看當前臨時表中的數據:

postgres=# select * from pg_temp_coordinator1_2_4_139820525504256.tt1;
ERROR:  Can only access temp objects of the current session.
LINE 1: select * from pg_temp_coordinator1_2_4_139820525504256.tt1;

現象二:

創建一個臨時表后,再根據這個臨時表的schema,去創建一個相同schema的普通表:

postgres=# create temp table tt1(a int);
CREATE TABLE
postgres=# \d
                                         List of relations
                  Schema                  | Name | Type  | Owner |             Storage              
------------------------------------------+------+-------+-------+----------------------------------
 pg_temp_coordinator1_2_3_139820525504256 | tt1  | table | xucw  | {orientation=row,compression=no}
(1 row)
postgres=# create table pg_temp_coordinator1_2_3_139820525504256.tt2(a int);
CREATE TABLE
postgres=# select relname,relnamespace from pg_class where relname like 'tt%';
 relname | relnamespace 
---------+--------------
 tt1     |        24592
 tt2     |        24592
(2 rows)

隨后,退出當前session,重新連接查看表狀態,我們會神奇發現之前創建的臨時表tt1消失的同時,創建的普通表tt2也一樣消失了。

postgres=# select relname,relnamespace from pg_class where relname like 'tt%';
 relname | relnamespace 
---------+--------------
(0 rows)

使用注意

  1. 與使用永久表相比,使用臨時表可以提高性能,但存在丟失數據的風險。臨時表只在當前會話可見,本會話結束后將自動刪除。如果數據丟失是不可接受的,請使用永久表。
  2. 臨時表對應的sechema在搜索路徑中的優先級是高於其他sechma的,即臨時表對應schema具有第一搜索優先級。
  3. \parallel模式不支持創建臨時表!如需使用臨時表,需要在開啟parallel之前就創建好,並在parallel內部使用。parallel內部創建的臨時表不會生效。
  4. PG_TOTAL_USER_RESOURCE_INFO視圖中的used_temp_space和total_temp_space可以查看當前臨時表的相關空間使用情況
  5. 創建臨時表時,會同步創建臨時Schema,這些臨時Schema的名稱類似於pg_temp_*和pg_toast_temp_*
  6. CN Retry功能開啟時會為臨時表數據記錄日志,為保證數據一致性,在使用臨時表時不能切換CN Retry開關狀態,保持使用臨時表的會話中CN Retry開關始終處於打開狀態或者關閉狀態。
  7. 臨時表和非日志表的存儲方式建議和基表相同。當基表為行存(列存)表時,臨時表和非日志表也推薦創建為行存(列存)表,可以避免行列混合關聯帶來的高計算代價
  8. 如果上層應用,使用了連接池機制連接GaussDB(DWS),在使用臨時表時,強烈建議將連接歸還連接池之前,將臨時表主動刪除,避免造成連接未斷開導致的數據異常
  9. 在每個會話第一次使用臨時表之前可以改變temp_buffers的值,之后的設置將是無效的
  10. autoanalyze不支持對帶有ON COMMIT [DELETE ROWS|DROP]選項的臨時表觸發autoanalyze,如需收集,需用戶手動執行analyze操作
  11. 如果創建視圖時包含臨時表,則該視圖會自動轉為臨時視圖

典型場景

臨時存儲

臨時表可以減少冗余中間表的存在,在一些復雜操作時,往往需要借助一些中間表去完成功能,但一般來說普通表的創建是需要數據庫管理員來統計創建維護的。臨時表的存在就允許中間表用完即清,減少數據庫系統中冗余表的存在。另外,臨時表在使用時數據是session間隔離的,其他session不能看到當前session的數據,數據安全性在一定程度上也更好。

提升性能

對於過於復雜並且不易通過普通優化方法調整性能的SQL可以考慮拆分的方法,把SQL中某一部分拆分成獨立的SQL並把執行結果存入臨時表,拆分常見的場景包括但不限於:

  • 作業中多個SQL有同樣的子查詢,並且子查詢數據量較大。
  • Plan cost計算不准,導致子查詢hash bucket太小,比如實際數據1000W行,hash bucket只有1000。
  • 函數(如substr,to_number)導致大數據量子查詢選擇度計算不准。
  • 多DN環境下對大表做broadcast的子查詢。

 

點擊關注,第一時間了解華為雲新鮮技術~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM