GREENPLUM數據庫,postgresql客戶端-----創建表


與其它關系型數據庫一樣,二維表同樣是GP中最重要的存儲數據對象。只不過為了更好的支持數據倉庫海量數據的訪問,GP在表這個層面為我們提供了更多更好的選項。
從數據存儲方式上看,GP的表可以分成面向行存儲的普通堆積表和面向列存儲的AOT表(APPEND ONLY TABLE)(當然AOT表也可以是按行存儲的,但是按列存儲必須是AOT表)。
這樣,我們在設計應用上可以獲得相當的靈活性。比如經常需要更新的數據,或者較小的維度數據,應該使用普通堆積表存儲。下面是兩個創建普通堆積表的例子。

例子一
CREATE TABLE SALES
 (PROD_ID numeric NOT NULL ,
    CUST_ID numeric NOT NULL ,
    TIME_ID DATE NOT NULL ,
    CHANNEL_ID numeric NOT NULL ,
    PROMO_ID numeric NOT NULL ,
    QUANTITY_SOLD numeric(10,2) NOT NULL ,
    AMOUNT_SOLD numeric(10,2) NOT NULL)
distributed by (prod_id,cust_id,time_id,channel_id,promo_id);

例子二
CREATE TABLE SALES
  (PROD_ID numeric NOT NULL ,
     CUST_ID numeric NOT NULL ,
     TIME_ID DATE NOT NULL ,
     CHANNEL_ID numeric NOT NULL ,
     PROMO_ID numeric NOT NULL ,
     QUANTITY_SOLD numeric(10,2) NOT NULL ,
     AMOUNT_SOLD numeric(10,2) NOT NULL)
distributed randomly;

與其它數據庫相比,GP的表最大的不同是它一定是分區的,也就是表中的所有記錄都會依據相關算法打散,分布到所有的segment當中,從而在充分利用硬件資源的同時,最大化訪問數據的IO,這種特性對於數據倉庫應用是非常有幫助的。GP提供兩種打散數據的算法,一種是HASH算法,參見例子一,在定義表的時候,通過distributed by指定表中的某個列或者某個列的組合作為HASH鍵,相同HASH鍵的記錄會放在同一個SEGMENT當中。所以,GP建議一般選擇記錄分布均勻的鍵作為HASH鍵使用,從而保證表中的記錄可以均勻分布到所有SEGMENT上。如果表上定義了主鍵或者唯一鍵,則這些鍵值列必須作前導列出現在分布鍵當中。假如例子一的PROD_ID是主鍵或者唯一鍵,那么它必須出現在HASH鍵中,並且放在第一位。 如果定義表的時候,沒有指定distributed子句,系統使用第一個列作為HASH鍵使用。另一種數據打散的算法是平均分配法(ROUND-ROBIN),參見例子二, 假設有三個段,那么這種算法會把第一條記錄放在段1, 第二條記錄放在段2,第三條記錄放在段3,第四條記錄放在段四,以此類推,直到所有記錄發放完為止。這種算法比較適合表中的字段存在嚴重數據傾斜的情況。


    在數據倉庫中,對於存儲海量少變化歷史數據的事實表的訪問,會產生大量IO。這些表除了記錄多外(縱向),同時也很寬。比如幾十列,甚至上百列的表也很常見。但是在進行數據分析和挖掘過程中,我們可能只用到這些列的很小一部分內容,而傳統的按行存儲的表,在訪問時總是會訪問記錄中的所有列,導致很多無效IO,當由於表橫向尺寸過大,按行存儲的表還會出現行鏈接,這會使無效IO的問題更嚴重。在GP中,對於這種情況應該考慮使用面向列存儲的AOT表,從而大幅高IO效率,獲取數據查詢性能的收益。下面是AOT表定義的例子。
例子三
CREATE TABLE SALES
   (PROD_ID numeric NOT NULL ,
    CUST_ID numeric NOT NULL ,
    TIME_ID DATE NOT NULL ,
    CHANNEL_ID numeric NOT NULL ,
    PROMO_ID numeric NOT NULL ,
    QUANTITY_SOLD numeric(10,2) NOT NULL ,
    AMOUNT_SOLD numeric(10,2) NOT NULL)
    WITH (appendonly=true,orientation=column,compresstype=zlib,COMPRESSLEVEL=5)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id);
    
例子四
 CREATE TABLE SALES
   (PROD_ID numeric NOT NULL ,
    CUST_ID numeric NOT NULL ,
    TIME_ID DATE NOT NULL ,
    CHANNEL_ID numeric NOT NULL ,
    PROMO_ID numeric NOT NULL ,
    QUANTITY_SOLD numeric(10,2) NOT NULL ,
    AMOUNT_SOLD numeric(10,2) NOT NULL)
    WITH (appendonly=true,orientation=column,compresstype=QUICKLZ,COMPRESSLEVEL=1)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id);

    現在硬件系統往往IO效率跟不上CPU處理的速度,而數據倉庫應用恰恰是IO敏感型的應用,所以很多數據倉庫系統上,我會看到CPU很閑,但是出現大量IO等待的情況。所以通過壓縮,尤其是面向列壓縮,允許我們犧牲一定的CPU效率進一步換取IO效率,提高系統的的吞吐量。GP的AOT表允許使用兩種壓縮算法,一種是ZLIB,它的壓縮率較高,對CPU的消耗較多,GP中可以指定1到9 9個級別。1的壓縮比最小,9最大,參見例子三。另一種算法是QUICKLZ,它的壓縮比小,這能設置1,造成的CPU負載也比ZLIB小。
     AOT表雖然查詢和加載數據的效率很高,但是如它的名字那樣它只能支持SELECT,INSERT,truncate操作,不支持UPDATE和DELETE操作。所以它只適合存放經過處理基本最終無變化的歷史數據,用來提供高效的查詢訪問。
    
     從數據數據存放的生命周期看,前面介紹的表都稱之為永久表,這些表的記錄不會因為事務的結束和會話的斷開而消失。而業務中,我們經常要使用到臨時數據集合,如果用永久表存放中間結果,一方面是在並發環境中數據不安全,另一方面頻繁的刪除表中的記錄或者刪除表,會導致大量碎片,在字典層面造成瓶頸。因此GP也支持臨時表,不同連接共享相同的表結構。比如下面的兩個例子。
例子五
CREATE TEMP TABLE SALES_TMP
  (PROD_ID numeric NOT NULL ,
     CUST_ID numeric NOT NULL ,
     TIME_ID DATE NOT NULL ,
     CHANNEL_ID numeric NOT NULL ,
     PROMO_ID numeric NOT NULL ,
     QUANTITY_SOLD numeric(10,2) NOT NULL ,
     AMOUNT_SOLD numeric(10,2) NOT NULL)
     on commit preserve rows
distributed randomly;

例子六
CREATE TEMP TABLE SALES
  (PROD_ID numeric NOT NULL ,
     CUST_ID numeric NOT NULL ,
     TIME_ID DATE NOT NULL ,
     CHANNEL_ID numeric NOT NULL ,
     PROMO_ID numeric NOT NULL ,
     QUANTITY_SOLD numeric(10,2) NOT NULL ,
     AMOUNT_SOLD numeric(10,2) NOT NULL)
     on commit delete rows
distributed by (prod_id,cust_id,time_id,channel_id,promo_id);

    例子五是事務內有效,連接會話間數據獨立。完成事務后數據保留,只有連接會話斷開后數據才消失。例子六是事務內有效,也就是提交事務后(commit)數據就會消失。如果臨時表的名字與永久表名字重復,臨時表的訪問優先。
    除了普通表以外,GP還支持超大表進行分區應用,為我們的提高數據訪問效率的同時,也提高應用的靈活型。它可以支持RANGE分區,LIST分區,以及靈活的復合分區(RANGE-LIST,RANGE-RANGE,LIST-RANGE,LIST-LIST,以及更多層次的分區,比如三層分區),GP的分區是基於SEGMENT基礎之上的。每個分區的數據同樣會打散分布到每個segment中,當WHERE條件上出現分區鍵時,它會進行分區裁剪,減少IO。需要注意的是,目前GREENPLUM還只支持靜態分區裁剪,所以如果希望用到分區裁剪,請在WHERE條件中使用事實表的分區鍵作為條件。
   
例子七 使用LIST分區
  CREATE TABLE SALES
   (PROD_ID numeric NOT NULL ,
    CUST_ID numeric NOT NULL ,
    TIME_ID DATE NOT NULL ,
    CHANNEL_ID numeric NOT NULL ,
    PROMO_ID numeric NOT NULL ,
    QUANTITY_SOLD numeric(10,2) NOT NULL ,
    AMOUNT_SOLD numeric(10,2) NOT NULL)
    WITH (appendonly=true,orientation=column,compresstype=QUICKLZ,COMPRESSLEVEL=1)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id)
    partition by list (channel_id)
    ( partition c_1 values (2, 4),
      partition c_2 values (3,9),
     default partition other);
 
例子八
---使用RANGE分區
   CREATE TABLE SALES
   (PROD_ID numeric NOT NULL ,
    CUST_ID numeric NOT NULL ,
    TIME_ID DATE NOT NULL ,
    CHANNEL_ID numeric NOT NULL ,
    PROMO_ID numeric NOT NULL ,
    QUANTITY_SOLD numeric(10,2) NOT NULL ,
    AMOUNT_SOLD numeric(10,2) NOT NULL)
    WITH (appendonly=true,orientation=column,compresstype=QUICKLZ,COMPRESSLEVEL=1)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id)
    partition by range(time_id)
    ( START (date '1998-01-01') INCLUSIVE
      END (date '2001-12-31') EXCLUSIVE
      EVERY (INTERVAL '1 year'), DEFAULT PARTITION other);
 
---每個分區獨立定義
   CREATE TABLE SALES
   (PROD_ID numeric NOT NULL ,
    CUST_ID numeric NOT NULL ,
    TIME_ID DATE NOT NULL ,
    CHANNEL_ID numeric NOT NULL ,
    PROMO_ID numeric NOT NULL ,
    QUANTITY_SOLD numeric(10,2) NOT NULL ,
    AMOUNT_SOLD numeric(10,2) NOT NULL)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id)
    partition by range(time_id)
    (PARTITION FY1998 START (date '1998-01-01') INCLUSIVE ,
     PARTITION FY1999 START (date '1999-01-01') INCLUSIVE ,
     PARTITION FY2000 START (date '2000-01-01') INCLUSIVE ,
     PARTITION FY2001 START (date '2001-01-01') INCLUSIVE ,
     DEFAULT PARTITION extra);

例子九
--使用復合分區方式 RANGE-LIST
   CREATE TABLE SALES
      (PROD_ID numeric NOT NULL ,
       CUST_ID numeric NOT NULL ,
       TIME_ID DATE NOT NULL ,
       CHANNEL_ID numeric NOT NULL ,
       PROMO_ID numeric NOT NULL ,
       QUANTITY_SOLD numeric(10,2) NOT NULL ,
       AMOUNT_SOLD numeric(10,2) NOT NULL)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id)
    partition by range(time_id)
    SUBPARTITION BY LIST(channel_id)
    SUBPARTITION TEMPLATE
    ( subpartition c_1 values (2, 4),
      subpartition c_2 values (3,9),
      default subpartition other_1)
    ( START (date '1998-01-01') INCLUSIVE
      END (date '2001-12-31') EXCLUSIVE
      EVERY (INTERVAL '1 year'), DEFAULT PARTITION other_2);
 

--使用復合分區方式LIST-RANGE
CREATE TABLE SALES
      (PROD_ID numeric NOT NULL ,
       CUST_ID numeric NOT NULL ,
       TIME_ID DATE NOT NULL ,
       CHANNEL_ID numeric NOT NULL ,
       PROMO_ID numeric NOT NULL ,
       QUANTITY_SOLD numeric(10,2) NOT NULL ,
       AMOUNT_SOLD numeric(10,2) NOT NULL)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id)
    PARTITION BY LIST(channel_id)
    subpartition by range(time_id)   
    SUBPARTITION TEMPLATE
     ( START (date '1998-01-01') INCLUSIVE
      END (date '2001-12-31') EXCLUSIVE
      EVERY (INTERVAL '1 year'), DEFAULT SUBPARTITION other_2)
    ( partition c_1 values (2, 4),
      partition c_2 values (3,9),
      default partition other_1);
  
--使用復合分區方式RANGE-RANGE
CREATE TABLE SALES
      (PROD_ID numeric NOT NULL ,
       CUST_ID numeric NOT NULL ,
       TIME_ID DATE NOT NULL ,
       YEAR    INT ,
       MON     INT,
       CHANNEL_ID numeric NOT NULL ,
       PROMO_ID numeric NOT NULL ,
       QUANTITY_SOLD numeric(10,2) NOT NULL ,
       AMOUNT_SOLD numeric(10,2) NOT NULL)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id)
    PARTITION BY RANGE (year)
    subpartition by range(mon)   
     SUBPARTITION TEMPLATE (
     START (1) INCLUSIVE END (13) EXCLUSIVE EVERY (1),
     DEFAULT SUBPARTITION other_months )
    ( START (1998) INCLUSIVE END (2001) INCLUSIVE EVERY (1),
      DEFAULT PARTITION OTHER_years );
     
--使用復合分區方式 LIST-LIST
CREATE TABLE SALES
      (PROD_ID numeric NOT NULL ,
       CUST_ID numeric NOT NULL ,
       TIME_ID DATE NOT NULL ,
       YEAR    INT ,
       MON     INT,
       CHANNEL_ID numeric NOT NULL ,
       PROMO_ID numeric NOT NULL ,
       QUANTITY_SOLD numeric(10,2) NOT NULL ,
       AMOUNT_SOLD numeric(10,2) NOT NULL)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id)
    PARTITION BY LIST (year)
    subpartition by LIST(mon)   
     SUBPARTITION TEMPLATE (
      subpartition Q_1 values (1,2, 3),
      subpartition Q_2 values (4,5, 6),
      subpartition Q_3 values (7,8, 9),
      subpartition Q_4 values (10,11, 12),
       DEFAULT SUBPARTITION other_months )        
    ( partition Y_1 values (1998),
      partition Y_2 values (1999),
      partition Y_3 values (2000),
      partition Y_4 values (2001),
      DEFAULT PARTITION OTHER_years );

--三層復合分區,RANGE-RANGE-LIST
    CREATE TABLE SALES
      (PROD_ID numeric NOT NULL ,
       CUST_ID numeric NOT NULL ,
       TIME_ID DATE NOT NULL ,
       YEAR    INT ,
       MON     INT,
       CHANNEL_ID numeric NOT NULL ,
       PROMO_ID numeric NOT NULL ,
       QUANTITY_SOLD numeric(10,2) NOT NULL ,
       AMOUNT_SOLD numeric(10,2) NOT NULL)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id)
    PARTITION BY RANGE (year)
    subpartition by range(mon)   
     SUBPARTITION TEMPLATE (
     START (1) INCLUSIVE END (13) EXCLUSIVE EVERY (1),
     DEFAULT SUBPARTITION other_months )   
    subpartition by list (channel_id)
    SUBPARTITION TEMPLATE
    ( subpartition c_1 values (2, 4),
      subpartition c_2 values (3,9),
      default subpartition other_1)
      ( START (1998) INCLUSIVE END (2001) INCLUSIVE EVERY (1),
      DEFAULT PARTITION OTHER_years );

例子十 分區表的分區可以使用不同的存儲屬性,既可以面向行,也可以面向列
CREATE TABLE SALES
   (PROD_ID numeric NOT NULL ,
    CUST_ID numeric NOT NULL ,
    TIME_ID DATE NOT NULL ,
    CHANNEL_ID numeric NOT NULL ,
    PROMO_ID numeric NOT NULL ,
    QUANTITY_SOLD numeric(10,2) NOT NULL ,
    AMOUNT_SOLD numeric(10,2) NOT NULL)
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id)
    partition by range(time_id)
    (PARTITION FY1998 START (date '1998-01-01') INCLUSIVE WITH (appendonly=true,orientation=column,compresstype=zlib,COMPRESSLEVEL=5),
     PARTITION FY1999 START (date '1999-01-01') INCLUSIVE WITH (appendonly=true,orientation=column,compresstype=zlib,COMPRESSLEVEL=5),
     PARTITION FY2000 START (date '2000-01-01') INCLUSIVE WITH (appendonly=true,orientation=column,compresstype=zlib,COMPRESSLEVEL=5),
     PARTITION FY2001 START (date '2001-01-01') INCLUSIVE ,
     DEFAULT PARTITION extra);

例子十一 分區表可以以其它表為模板復制表結構

CREATE TABLE SALES2 (like sales) 
    distributed by (prod_id,cust_id,time_id,channel_id,promo_id)
    partition by range(time_id)
    (PARTITION FY1998 START (date '1998-01-01') INCLUSIVE WITH (appendonly=true,orientation=column,compresstype=zlib,COMPRESSLEVEL=5),
     PARTITION FY1999 START (date '1999-01-01') INCLUSIVE WITH (appendonly=true,orientation=column,compresstype=zlib,COMPRESSLEVEL=5),
     PARTITION FY2000 START (date '2000-01-01') INCLUSIVE WITH (appendonly=true,orientation=column,compresstype=zlib,COMPRESSLEVEL=5),
     PARTITION FY2001 START (date '2001-01-01') INCLUSIVE ,
     DEFAULT PARTITION extra);
    

    從數據存放的角度看,前面介紹的表數據都存儲在數據庫內。GP也支持外部表,也就是數據以平面文件形式存放在數據庫外,在數據庫內以表的訪問形式來訪問這些平面文件的數據。GP的外部表與其它數據庫的外部表有兩個重要的不同之處。一個是GP的外部表不必放在數據庫服務器上,它可以獨立存放在ETL服務器或者文件服務器上,而且不必集中存放,比如有多個文件,那么可以分組放到不同的文件服務器上,這樣可以大幅提升海量數據的IO處理速度,從而提高數據加載效率。另一個不同之處是,從4.0開始,GP不但支持可讀外部表,還支持可寫外部表。這樣我們可以把GP中的數據快速卸載成平面文件。因此,外部表是GP與其它外部數據源進行數據交換的最終要手段之一,同時也是最高效的手段。如果文件全部在SEGMENT節點上,則可以使用FILE:\\協議進行訪問

例子十二,這是一個只讀外部表的例子,它讀取的文件分布在四台機器上(如何啟動gpfdist服務,參見手冊或者文章http://www.itpub.net/viewthread.php?tid=1410982),這些文件使用CSV格式,使用分割符'|',使用'"'作為第二分割符,並跳過文件的首行,文本中使用的字符集是GBK(系統會自動轉換成服務端字符集,比如UTF8),如果記錄格式有問題,跳過,並寫入錯誤表err_sales_ext(如果錯誤表不存在,建外部表時系統自動創建),格式有問題的記錄最多允許7000000條
create external table sales_ext1
(PROD_ID numeric,
 CUST_ID numeric,
 TIME_ID DATE,
 CHANNEL_ID numeric ,
 PROMO_ID numeric ,
 QUANTITY_SOLD numeric(10,2),
 AMOUNT_SOLD numeric(10,2))
 LOCATION ('gpfdist://etlhost-1:8081/sales/*','gpfdist://etlhost-2:8081/sales/*','gpfdist://etlhost-3:8081/sales/*','gpfdist://etlhost-4:8081/sales/*')
FORMAT 'CSV'(header DELIMITER '|' QUOTE '"')
encoding 'GBK'
log errors into err_sales_ext segment reject limit 7000000 rows;

可讀外部表只能查詢,不能進行DML操作,也不支持索引。

例子十三,這是一個可寫外部表的例子,它可以把查詢結果寫入到指定CSV格式文件中。
create writable external table sales_ext_w
(PROD_ID numeric,
 CUST_ID numeric,
 TIME_ID DATE,
 CHANNEL_ID numeric ,
 PROMO_ID numeric ,
 QUANTITY_SOLD numeric(10,2),
 AMOUNT_SOLD numeric(10,2))
 LOCATION ('gpfdist://md:8081/sales/sales.csv','gpfdist://etlhost-2:8081/sales/sales.csv','gpfdist://etlhost-3:8081/sales/sales.csv','gpfdist://etlhost-4:8081/sales/sales.csv')
FORMAT 'CSV'(DELIMITER '|' QUOTE '"')
encoding 'GBK'
distributed by (prod_id,cust_id,time_id,channel_id,promo_id);

可寫外部表只支持insert操作,不支持DELETE,UPDATE,SELECT ,TRUNCATE , 插入的數據是以追加方式寫入文件。

外部表也可以通過WEB server,使用HTTP協議訪問數據,或者使用系統命令獲取數據,這種形式的外部表也有可寫外部表。
例子十四
create external web table sales_ext1
(PROD_ID numeric,
 CUST_ID numeric,
 TIME_ID DATE,
 CHANNEL_ID numeric ,
 PROMO_ID numeric ,
 QUANTITY_SOLD numeric(10,2),
 AMOUNT_SOLD numeric(10,2))
 LOCATION ('http://etlhost-1:8081/sales/*','http://etlhost-2:8081/sales/*','http://etlhost-3:8081/sales/*','http://etlhost-4:8081/sales/*')
FORMAT 'CSV'(header DELIMITER '|' QUOTE '"')
encoding 'GBK'
log errors into err_sales_ext segment reject limit 7000000 rows;


例子十五,需要以超級管理員身份執行,並且所有節點都有comm.sh的腳本
create external web table sales_ext1
(PROD_ID numeric,
 CUST_ID numeric,
 TIME_ID DATE,
 CHANNEL_ID numeric ,
 PROMO_ID numeric ,
 QUANTITY_SOLD numeric(10,2),
 AMOUNT_SOLD numeric(10,2))
 execute 'comm.sh' on all
FORMAT 'CSV'(header DELIMITER '|' QUOTE '"')
encoding 'GBK'
log errors into err_sales_ext segment reject limit 7000000 rows;


免責聲明!

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



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