PostgreSQL 數據庫開發規範——命名規範 & 設計規範

原文:PostgreSQL 數據庫開發規範html

 

命名規範

強制】庫名、表名限制命名長度,建議表名及字段名字符總長度小於等於63。sql

【強制】對象名(表名、列名、函數名、視圖名、序列名、等對象名稱)規範,對象名務必只使用小寫字母,下劃線,數字。不要以pg開頭,不要以數字開頭,不要使用保留字。
保留字參考
https://www.postgresql.org/docs/9.5/static/sql-keywords-appendix.html數據庫

【強制】query中的別名不要使用 "小寫字母,下劃線,數字" 之外的字符,例如中文。json

【推薦】主鍵索引應以 pk_ 開頭, 惟一索引要以 uk_ 開頭,普通索引要以 idx_ 打頭。數組

【推薦】臨時表以 tmp_ 開頭,子表以規則結尾,例如按年分區的主表若是爲tbl, 則子表爲tbl_2016,tbl_2017,。。。bash

【推薦】庫名最好與應用名稱一致,或便於辨識。網絡

【推薦】不建議使用public schema(不一樣業務共享的對象可使用public schema),應該爲每一個應用分配對應的schema,schema_name最好與user name一致。app

【推薦】comment不要使用中文,由於編碼可能不同,若是存進去和讀取時的編碼不一致,致使可讀性不強。 pg_dump時也必須與comment時的編碼一致,不然可能亂碼。函數

 

設計規範

【強制】多表中的相同列,必須保證列名一致,數據類型一致。post

【強制】btree索引字段不建議超過2000字節,若是有超過2000字節的字段須要建索引,建議使用函數索引(例如哈希值索引),或者使用分詞索引。

【強制】使用外鍵時,若是你使用的PG版本沒有自動創建fk的索引,則必需要對foreign key手工創建索引,不然可能影響references列的更新或刪除性能。

 

postgres=# create table tbl(id int primary key,info text);  
CREATE TABLE  
postgres=# create table tbl1(id int references tbl(id), info text);  
CREATE TABLE  
postgres=# \d tbl  
      Table "public.tbl"  
 Column |  Type   | Modifiers   
--------+---------+-----------  
 id     | integer | not null  
 info   | text    |   
Indexes:  
    "tbl_pkey" PRIMARY KEY, btree (id)  
Referenced by:  
    TABLE "tbl1" CONSTRAINT "tbl1_id_fkey" FOREIGN KEY (id) REFERENCES tbl(id)  
  
postgres=# \d tbl1  
     Table "public.tbl1"  
 Column |  Type   | Modifiers   
--------+---------+-----------  
 id     | integer |   
 info   | text    |   
Foreign-key constraints:  
    "tbl1_id_fkey" FOREIGN KEY (id) REFERENCES tbl(id)  
  
postgres=# \di  
              List of relations  
 Schema |   Name   | Type  |  Owner   | Table   
--------+----------+-------+----------+-------  
 public | tbl_pkey | index | postgres | tbl  
(1 row)  
  
postgres=# create index idx_tbl1_id on tbl1(id);  
CREATE INDEX  

  

【強制】使用外鍵時,必定要設置fk的action,例如cascade,set null,set default。

postgres=# create table tbl2(id int references tbl(id) on delete cascade on update cascade, info text);  
CREATE TABLE  
postgres=# create index idx_tbl2_id on tbl2(id);  
CREATE INDEX  
postgres=# insert into tbl values (1,'test');  
INSERT 0 1  
postgres=# insert into tbl2 values (1,'test');  
INSERT 0 1  
postgres=# update tbl set id=2;  
UPDATE 1  
postgres=# select * from tbl2;  
 id | info   
----+------  
  2 | test  
(1 row)  

  

【強制】對於頻繁更新的表,建議建表時指定表的fillfactor=85,每頁預留15%的空間給HOT更新使用。

postgres=# create table test123(id int, info text) with(fillfactor=85);  
CREATE TABLE  

  

【強制】表結構中字段定義的數據類型與應用程序中的定義保持一致,表之間字段校對規則一致,避免報錯或沒法使用索引的狀況發生。

說明:
(1).好比A表user_id字段數據類型定義爲varchar,可是SQL語句查詢爲 where user_id=1234;

 

【推薦】如何保證分區表的主鍵序列全局惟一。
使用多個序列,每一個序列的步調不同,或者每一個序列的範圍不同便可。

postgres=# create sequence seq_tab1 increment by 10000 start with 1;
CREATE SEQUENCE
postgres=# create sequence seq_tab2 increment by 10000 start with 2;
CREATE SEQUENCE
postgres=# create sequence seq_tab3 increment by 10000 start with 3;
CREATE SEQUENCE
postgres=# create table tab1 (id int primary key default nextval('seq_tab1') check(mod(id,10000)=1), info text);
CREATE TABLE
postgres=# create table tab2 (id int primary key default nextval('seq_tab2') check(mod(id,10000)=2), info text);
CREATE TABLE
postgres=# create table tab3 (id int primary key default nextval('seq_tab3') check(mod(id,10000)=3), info text);
CREATE TABLE

postgres=# insert into tab1 (info) select generate_series(1,10);
INSERT 0 10
postgres=# insert into tab2 (info) select generate_series(1,10);
INSERT 0 10
postgres=# insert into tab3 (info) select generate_series(1,10);
INSERT 0 10
postgres=# select * from tab1;
  id   | info 
-------+------
     1 | 1
 10001 | 2
 20001 | 3
 30001 | 4
 40001 | 5
 50001 | 6
 60001 | 7
 70001 | 8
 80001 | 9
 90001 | 10
(10 rows)

postgres=# select * from tab2;
  id   | info 
-------+------
     2 | 1
 10002 | 2
 20002 | 3
 30002 | 4
 40002 | 5
 50002 | 6
 60002 | 7
 70002 | 8
 80002 | 9
 90002 | 10
(10 rows)

postgres=# select * from tab3;
  id   | info 
-------+------
     3 | 1
 10003 | 2
 20003 | 3
 30003 | 4
 40003 | 5
 50003 | 6
 60003 | 7
 70003 | 8
 80003 | 9
 90003 | 10
(10 rows)

  

postgres=# create sequence seq_tb1 increment by 1 minvalue 1 maxvalue 100000000 start with 1 no cycle ;
CREATE SEQUENCE
postgres=# create sequence seq_tb2 increment by 1 minvalue 100000001 maxvalue 200000000 start with 100000001 no cycle ;
CREATE SEQUENCE
postgres=# create sequence seq_tb3 increment by 1 minvalue 200000001 maxvalue 300000000 start with 200000001 no cycle ;
CREATE SEQUENCE

postgres=# create table tb1(id int primary key default nextval('seq_tb1') check(id >=1 and id<=100000000), info text);
CREATE TABLE
postgres=# create table tb2(id int primary key default nextval('seq_tb2') check(id >=100000001 and id<=200000000), info text);
CREATE TABLE
postgres=# create table tb3(id int primary key default nextval('seq_tb3') check(id >=200000001 and id<=300000000), info text);
CREATE TABLE
postgres=# insert into tb1 (info) select * from generate_series(1,10);
INSERT 0 10
postgres=# insert into tb2 (info) select * from generate_series(1,10);
INSERT 0 10
postgres=# insert into tb3 (info) select * from generate_series(1,10);
INSERT 0 10
postgres=# select * from tb1;
 id | info 
----+------
  1 | 1
  2 | 2
  3 | 3
  4 | 4
  5 | 5
  6 | 6
  7 | 7
  8 | 8
  9 | 9
 10 | 10
(10 rows)

postgres=# select * from tb2;
    id     | info 
-----------+------
 100000001 | 1
 100000002 | 2
 100000003 | 3
 100000004 | 4
 100000005 | 5
 100000006 | 6
 100000007 | 7
 100000008 | 8
 100000009 | 9
 100000010 | 10
(10 rows)

postgres=# select * from tb3;
    id     | info 
-----------+------
 200000001 | 1
 200000002 | 2
 200000003 | 3
 200000004 | 4
 200000005 | 5
 200000006 | 6
 200000007 | 7
 200000008 | 8
 200000009 | 9
 200000010 | 10
(10 rows)

  

【推薦】建議有按期歷史數據刪除需求的業務,表按時間分區,刪除時不要使用DELETE操做,而是DROP或者TRUNCATE對應的表。

【推薦】爲了全球化的需求,全部的字符存儲與表示,均以UTF-8編碼,那麼字符計數方法注意:

# 計算字符長度
postgres=# select length('阿里巴巴');  
 length   
--------  
      4  
(1 row)  


# 計算字節數
postgres=# select octet_length('阿里巴巴');  
 octet_length   
--------------  
           12  
(1 row)  


# 其餘長度相關接口
   Schema   |          Name          | Result data type | Argument data types |  Type    
------------+------------------------+------------------+---------------------+--------  
 pg_catalog | array_length           | integer          | anyarray, integer   | normal  
 pg_catalog | bit_length             | integer          | bit                 | normal  
 pg_catalog | bit_length             | integer          | bytea               | normal  
 pg_catalog | bit_length             | integer          | text                | normal  
 pg_catalog | char_length            | integer          | character           | normal  
 pg_catalog | char_length            | integer          | text                | normal  
 pg_catalog | character_length       | integer          | character           | normal  
 pg_catalog | character_length       | integer          | text                | normal  
 pg_catalog | json_array_length      | integer          | json                | normal  
 pg_catalog | jsonb_array_length     | integer          | jsonb               | normal  
 pg_catalog | length                 | integer          | bit                 | normal  
 pg_catalog | length                 | integer          | bytea               | normal  
 pg_catalog | length                 | integer          | bytea, name         | normal  
 pg_catalog | length                 | integer          | character           | normal  
 pg_catalog | length                 | double precision | lseg                | normal  
 pg_catalog | length                 | double precision | path                | normal  
 pg_catalog | length                 | integer          | text                | normal  
 pg_catalog | length                 | integer          | tsvector            | normal  
 pg_catalog | lseg_length            | double precision | lseg                | normal  
 pg_catalog | octet_length           | integer          | bit                 | normal  
 pg_catalog | octet_length           | integer          | bytea               | normal  
 pg_catalog | octet_length           | integer          | character           | normal  
 pg_catalog | octet_length           | integer          | text                | normal  

  

【推薦】對於值與堆表的存儲順序線性相關的數據,若是一般的查詢爲範圍查詢,建議使用BRIN索引。

例如流式數據,時間字段或自增字段,可使用BRIN索引,減小索引的大小,加快數據插入速度。

create index idx on tbl using brin(id);  

  

【推薦】設計時應儘量選擇合適的數據類型,能用數字的堅定不用字符串,能用樹類型的,堅定不用字符串。 使用好的數據類型,可使用數據庫的索引,操做符,函數,提升數據的查詢效率。

PostgreSQL支持的數據類型以下
精確的數字類型
浮點
貨幣
字符串
字符
字節流
日期
時間
布爾
枚舉
幾何
網絡地址
比特流
文本
UUID
XML
JSON
數組
複合類型
範圍類型
對象
行號
大對象
ltree 樹結構類型
cube 多維類型
earth 地球類型
hstore KV類型
pg_trgm 類似類型
PostGIS(點、線段、面、路徑、經緯度、raster、拓撲、。。。。。。)

 

【推薦】應該儘可能避免全表掃描(除了大數據量掃描的數據分析),PostgreSQL支持幾乎全部數據類型的索引。
索引接口包括
btree
hash
gin
gist
sp-gist
brin
rum (擴展接口)
bloom (擴展接口)

 

【推薦】對於網絡複雜而且RT要求很高的場景,若是業務邏輯冗長,應該儘可能減小數據庫和程序之間的交互次數,儘可能使用數據庫存儲過程(如plpgsql),或內置的函數。
PostgreSQL內置的plpgsql函數語言功能很是強大,能夠處理複雜的業務邏輯。
PostgreSQL內置了很是多的函數,包括分析函數,聚合函數,窗口函數,普通類型函數,複雜類型函數,數學函數,幾何函數,。。。等。

 

【推薦】應用應該儘可能避免使用數據庫觸發器,這會使得數據處理邏輯複雜,不便於調試。

 

【推薦】若是應用常常要訪問較大結果集的數據(例如100條),可能形成大量的離散掃描。
建議想辦法將數據聚合成1條,例如常常要按ID訪問這個ID的數據,建議能夠按期按ID聚合這些數據,查詢時返回的記錄數越少越快。
若是沒法聚合,建議使用IO較好的磁盤。

 

【推薦】流式的實時統計,爲了防止並行事務致使的統計空洞,建議業務層按分表並行插入,單一分表串行插入。
例如
table1, table2, ...table100;
每一個線程負責一張表的插入,統計時能夠按時間或者表的自增ID進行統計

select xxx from table1 where id>=上一次統計的截至ID group by yyy;  

 

【推薦】範圍查詢,應該儘可能使用範圍類型,以及GIST索引,提升範圍檢索的查詢性能。

例如
使用範圍類型存儲IP地址段,使用包含的GIST索引檢索,性能比兩個字段的between and提高20多倍。

 

CREATE TABLE ip_address_pool_3 (  
  id serial8 primary key ,  
  start_ip inet NOT NULL ,  
  end_ip inet NOT NULL ,  
  province varchar(128) NOT NULL ,  
  city varchar(128) NOT NULL ,  
  region_name varchar(128) NOT NULL ,  
  company_name varchar(128) NOT NULL ,  
  ip_decimal_segment int8range  
) ;  
  
CREATE INDEX ip_address_pool_3_range ON ip_address_pool_3 USING gist (ip_decimal_segment);  
  
select province,ip_decimal_segment  from ip_address_pool_3 where ip_decimal_segment @> :ip::int8;  

 

【推薦】未使用的大對象,必定要同時刪除數據部分,不然大對象數據會一直存在數據庫中,與內存泄露相似。
vacuumlo能夠用來清理未被引用的大對象數據。

 

【推薦】對於固定條件的查詢,可使用部分索引,減小索引的大小,同時提高查詢效率。

select * from tbl where id=1 and col=?; -- 其中id=1爲固定的條件  
create index idx on tbl (col) where id=1;  

  

【推薦】對於常用表達式做爲查詢條件的語句,可使用表達式或函數索引加速查詢。

select * from tbl where exp(xxx);  
create index idx on tbl ( exp );  

  

【推薦】若是須要調試較爲複雜的邏輯時,不建議寫成函數進行調試,可使用plpgsql的online code.

do language plpgsql  

$$
  
declare  
begin  
  -- logical code  
end;  

$$
;  

  

【推薦】當業務有中文分詞的查詢需求時,建議使用PostgreSQL的分詞插件zhparser或jieba,用戶還能夠經過接口自定義詞組。
建議在分詞字段使用gin索引,提高分詞匹配的性能。

  

【推薦】當用戶有規則表達式查詢,或者文本近似度查詢的需求時,建議對字段使用trgm的gin索引,提高近似度匹配或規則表達式匹配的查詢效率,同時覆蓋了先後模糊的查詢需求。若是沒有建立trgm gin索引,則不推薦使用先後模糊查詢例如like %xxxx%。

 

【推薦】當用戶有prefix或者 suffix的模糊查詢需求時,可使用索引,或反轉索引達到提速的需求。

select * from tbl where col ~ '^abc';  -- 前綴查詢
select * from tbl where reverse(col) ~ '^def';  -- 後綴查詢使用反轉函數索引

 

【推薦】用戶應該對頻繁訪問的大表(一般指超過8GB的表,或者超過1000萬記錄的表)進行分區,從而提高查詢的效率、更新的效率、備份與恢復的效率、建索引的效率等等,(PostgreSQL支持多核建立索引後,能夠適當將這個限制放大)。

 

【推薦】用戶在設計表結構時,建議規劃好,避免常常須要添加字段,或者修改字段類型或長度。 某些操做可能觸發表的重寫,例如加字段並設置默認值,修改字段的類型。若是用戶確實很差規劃結構,建議使用jsonb數據類型存儲用戶數據。

相關文章
相關標籤/搜索