若是使用局部索引而不是常規索引,則在可爲空的列上(其中只有一小部分行的該列不具備空值),而後能夠大大縮短插入,更新和刪除的響應時間。 另外,單行選擇的響應時間也縮短了一點。 這篇文章解釋了什麼是部分索引,顯示瞭如何建立部分索引,描述了要求使用部分索引的規範用例,描述了一些簡單的性能測試,並顯示告終果證實有理由建議使用部分索引。 適當的用例。java
有時,業務分析會肯定實體類型具備可選屬性,該屬性在典型狀況下仍未設置,須要標識將可選屬性設置爲某個值的實體出現,而且從不要求 識別未設置的事件。 如今出現了一個典型的例子,一些辦公設施在一個很小的地下室裏提供有限的自行車存放空間,併爲不多有人利用這一點提供了編號的鑰匙。 當看門人交錯了一把鑰匙時,確實須要找出「誰被分配了42號鑰匙?」,可是不須要識別不使用自行車存放處的人員。算法
這樣的實體類型將被實現爲具備可爲空的列以表示可選屬性的表。而且該列將須要輔助索引來支持快速執行查詢,該查詢經過此列的非非空值來標識行。sql
若是你有一個同時包含「開票」和「未開票」訂單的表,則會出現另外一種狀況(與可選屬性的狀況稍有不一樣),其中「未開票」的訂單隻佔總表的一小部分,但一般在OLTP場景中惟一要選擇的對象-正是這樣以即可以對其進行處理並將其設置爲「開票」。使用限制定義了部分索引,以便僅對錶的某些行進行索引。在當前狀況下,在第一種狀況下,限制將指定僅對該列具備不爲空值的行創建索引。或者,在第二種狀況下,將僅索引「未開票」的行。數據庫
這將是有益的,由於部分索引比常規索引要小得多。最重要的是,在插入或刪除行時,在典型狀況下將避免索引的維護成本。架構
假設咱們建立了一個表:分佈式
1 create table t(k int primary key, v int);
(我將在本文中以尾部分號顯示全部SQL語句,由於它們將以ysqlsh命令形式顯示。)請注意,t.v列是可爲空的。函數
建立常規二級索引:oop
1 create index t_v on t(v);
並建立了部分二級索引:性能
1 create index t_v on t(v) where v is not null;
where子句限制能夠是任何僅說起要索引表中的列的單行表達式。 例如,``其中v不爲null且v!= 0''。 就這麼簡單!學習
我用一百萬行填充了表t,其中只有百分之十的行的t.v不爲空。 這是用PostgreSQL語法編寫的SQL,其中%運算符用於表示取模函數。
1 insert into t(k, v) 2 3 4 select 5 6 7 a.v, 8 9 10 case a.v % 10 11 12 13 when 0 then a.v 14 15 16 else null 17 18 19 end 20 21 22 from 23 24 25 (select generate_series(1, 1000000) as v) as a;
首先,我首先在t.v上建立了一個常規索引,對``建立索引''操做進行計時,而後運行計時腳本。 (此腳本實現了測試#2至#4。)而後我刪除了索引並在t.v上建立了部分索引,也對該操做進行了計時,並再次運行了計時腳本.
1 \timing on 2 3 call select_not_null_rows(100000); 4 5 \timing off 6 7
過程select_not_null_rows()經過運行實現的「 for循環」來模擬OLTP單行選擇語句:
1 for j in 1..num_rows loop 2 3 select t.k into the_k from t where t.v = the_v; 4 5 the_v := the_v + sparseness_step; 6 7 end loop; 8 9
固然,沒有人會寫像這樣的真實程序-尤爲是由於在沒有惟一約束ont.v的狀況下,select語句可能會返回多個行! 可是這種幼稚的方法足以用於計時目的。 sparseness_step的值設置爲10,反映了個人選擇,表示爲:
1 case a.v % 10
在上面顯示的插入語句中,我曾用來填充表t。在程序運行時首次遇到該問題時,該程序反覆發出的選擇語句是在幕後準備的,所以SQL執行確實遵循最佳的準備- 執行範例
1 \timing on 2 3 call insert_rows(100000); 4 5 \timing off
過程insert_rows()經過運行如下實現的「 for循環」來模擬OLTP單行插入語句:
1 for j in 1..num_rows loop 2 3 insert into t(k, v) values(1000000 + j, null); 4 5 commit; 6 7 end loop; 8 9
沒有人會在實際程序中提交每一行。 但這是模擬單行OLTP的設備。 在這裏,SQL執行也遵循最佳的執行準備範式。
1 \timing on 2 3 delete from t where k > 1000000; 4 5 \timing off 6 7
我在這裏沒有使用prepare-execute範例,由於SQL編譯和計劃所需的時間只是刪除十萬行所需時間的一小部分。
我在兩個環境中記錄了時間。
我記錄了在兩種測試條件(常規索引和部分索引)和兩種測試環境(「本地」和「雲」)下上述測試的通過時間。對於每一個測量對,常規索引時間都大於部分索引時間。能夠預料,「雲」環境中的時間絕對值大於「本地」環境中的時間絕對值。請注意,使用Raft共識算法和分佈式事務,在RF = 3的三節點羣集上的SQL操做預期比單個節點要慢,RF = 1且沒有節點間通訊且代碼路徑較短。如此有彈性和可擴展的系統有不少有據可查的好處,而這些好處可能會超過成本。
我經過將它們表示爲速比來標準化結果。例如,若是使用常規索引進行的測試花費60秒,而使用部分索引進行的測試花費20秒,則部分索引速度是常規索引速度的3倍。在個人測量精度範圍內,兩個測試環境中的速度比是相同的。
列出測試的部分索引與常規索引速度比
固然,你的結果可能會有所不一樣。 索引列中的非零值稀疏密度將使速度比更大,而稀疏密度較小將使它們變得更小。
這篇文章展現瞭如何在適當的用例中使用部分索引,能夠減小二級索引維護的成本,從而加快有關表的插入,更新和刪除操做。 經過使用部分索引來減小二級索引維護操做的數量在分佈式SQL數據庫(例如YugabyteDB)中特別有利。 這是由於在索引維護期間,看似僅插入,更新或刪除單個行的事務會自動成爲涉及主行和輔助索引行的分佈式事務。
當主行和輔助索引行存在於羣集的潛在兩個不一樣節點上的兩個不一樣分片上時,這種分佈式事務能夠是多分片事務。 能夠想象,多分片事務必然比單分片事務昂貴。
文章寫道這裏,若有不足之處,歡迎補充評論。
抽絲剝繭,細說架構那些事。