postgresql两个列模糊比较_数据分析之SQL优化系列(二)---PostgreSQL 的索引
參考《PostgreSQL11.2-中文手冊》
下面這個鏈接,講的通俗易懂,可以看看。
數據分析師不得不知道的SQL優化 - 鑫獲 - 博客園?www.cnblogs.com索引是提高數據庫性能的常用途徑。比起沒有索引,使用索引可以讓數據庫服務器更快找到
并獲取特定行。
但是索引同時也會增加數據庫系統的日常管理負擔,因此我們應該聰明地使用索引。
簡介
CREATE TABLE test1 ( id integer, content varchar)假設對上表做出如下查詢:
SELECT content FROM test1 WHERE id = constant在沒有事前準備的情況下,系統不得不掃描整個test1表,一行一行地去找到所有匹配的
項。如果test1中有很多行但是只有一小部分行(可能是0或者1)需要被該查詢返回,這顯
然是一種低效的方式。
但是如果系統被指示維護一個在id列上的索引,它就能使用一種更有效的方式來定位匹配行。例如,它可能僅僅需要遍歷一棵搜索樹的幾層而已。
類似的方法也被用于大部分非小說書籍中:經常被讀者查找的術語和概念被收集在一個字母
序索引中放在書籍的末尾。感興趣的讀者可以相對快地掃描索引并跳到合適的頁而不需要閱
讀整本書來尋找感興趣的材料。正如作者的任務是準備好讀者可能會查找的術語一樣,數據
庫程序員也需要預見哪些索引會有用。
正如前面討論的,下列命令可以用來在id列上創建一個索引:
CREATE INDEX test1_id_index ON test1 (id);索引的名字test1_id_index可以自由選擇,但我們最好選擇一個能讓我們想起該索引用途的
名字。
為了移除一個索引,可以使用DROP INDEX命令。索引可以隨時被創建或刪除
一旦一個索引被創建,就不再需要進一步的干預:系統會在表更新時更新索引,而且會在它
覺得使用索引比順序掃描表效率更高時使用索引。
但我們可能需要定期地運行ANALYZE命令
來更新統計信息以便查詢規劃器能做出正確的決定。通過Chapter 14 的信息可以了解如何找
出一個索引是否被使用以及規劃器在何時以及為什么會選擇不使用索引。
索引也會使帶有搜索條件的UPDATE和DELETE命令受益。此外索引還可以在連接搜索中使用。
因此,一個定義在連接條件列上的索引可以顯著地提高連接查詢的速度。
在一個大表上創建一個索引會耗費很長的時間。默認情況下,PostgreSQL允許在索引創建時
并行地進行讀(SELECT命令),但寫(INSERT、UPDATE和DELETE)則會被阻塞直到索引創建完成。
在生產環境中這通常是不可接受的。在創建索引時允許并行的寫是可能的,但是有些警告需要注意,更多信息可以參考并發構建索引。
一個索引被創建后,系統必須保持它與表同步。這增加了數據操作的負擔。因此哪些很少或
從不在查詢中使用的索引應該被移除。
索引類型
PostgreSQL提供了多種索引類型: B-tree、Hash、GiST、SP-GiST 、GIN 和 BRIN。每一種
索引類型使用了 一種不同的算法來適應不同類型的查詢。
B-tree 索引
默認情況下, CREATE INDEX命令創建適合于大部分情況的B-tree 索引。
B-tree可以在可排序數據上的處理等值和范圍查詢。特別地,PostgreSQL的查詢規劃器會
在任何一種涉及到以下操作符的已索引列上考慮使用B-tree索引:
< <= = >= >將這些操作符組合起來,例如BETWEEN和IN,也可以用B-tree索引搜索實現。同樣,在索引
列上的IS NULL或IS NOT NULL條件也可以在B-tree索引中使用。
優化器也會將B-tree索引用于涉及到模式匹配操作符LIKE和~ 的查詢,前提是如果模式是一
個常量且被固定在字符串的開頭—例如:col LIKE 'foo%'或者col ~ '^foo', 但在col
LIKE '%bar'上則不會。但是,如果我們的數據庫沒有使用C區域設置,我們需要創建一個
具有特殊操作符類的索引來支持模式匹配查詢,參見下面的Section 11.10 。同樣可以將Btree索引用于ILIKE和~*,但僅當模式以非字母字符開始,即不受大小寫轉換影響的字符。
B-tree索引也可以用于檢索排序數據。這并不會總是比簡單掃描和排序更快,但是總是有用
的。
Hash索引
Hash索引只能處理簡單等值比較。不論何時當一個索引列涉及到一個使用了=操作符的比較
時,查詢規劃器將考慮使用一個Hash索引。
CREATE INDEX name ON table USING HASH (column)GiST索引
GiST索引并不是一種單獨的索引,而是可以用于實現很多不同索引策略的基礎設施。相應
地,可以使用一個GiST索引的特定操作符根據索引策略(操作符類)而變化。作為一個例
子,PostgreSQL的標準捐獻包中包括了用于多種二維幾何數據類型的GiST操作符類,它用來
支持使用下列操作符的索引化查詢:
<< &< &> >> <<| &<| |&> |>> @> <@ ~= &&(這些操作符的含義見Section 9.11 )Table 64.1 中給出了標準發布中所包括的 GiST 操作
符類。contrib集合中還包括了很多其他GiST操作符類,可見Chapter 64 。
GiST索引也有能力優化“最近鄰”搜索,例如:
SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;它將找到離給定目標點最近的10個位置。能夠支持這種查詢的能力同樣取決于被使用的特定
操作符類。 在Table 64.1中,“Ordering Operators”列中列出了可以在這種方法中使用的
操作符。
SP-GiST索引
GiST相似,SP-GiST索引為支持多種搜索提供了一種基礎結構。SPGiST 允許實現眾多不同的非平衡的基于磁盤的數據結構,例如四叉樹、k-d樹和radix樹。
作為一個例 子,PostgreSQL的標準捐獻包中包含了一個用于二維點的SP-GiST操作符類,它
用于支持使用下列操作符的索引化查詢:
<< >> ~= <@ <^ >^GIN 索引
GIN 索引是“倒排索引”,它適合于包含多個組成值的數據值,例如數組。倒排索引中為
每一個組成值都包含一個單獨的項,它可以高效地處理測試指定組成值是否存在的查詢。
與 GiST 和 SP-GiST相似, GIN 可以支持多種不同的用戶定義的索引策略,并且可以與一
個 GIN 索引配合使用的特定操作符取決于索引策略。
作為一個例子,PostgreSQL的標準貢獻
包中包含了用于數組的GIN操作符類,它用于支持使用下列操作符的索引化查詢:
<@ @> = &&BRIN 索引
BRIN 索引(塊范圍索引的縮寫)存儲有關存放在一個表的連續物理塊范圍上的值摘要信
息。與 GiST、SP-GiST 和 GIN 相似,BRIN 可以支持很多種不同的索引策略,并且可以與一
個 BRIN 索引配合使用的特定操作符取決于索引策略。對于具有線性排序順序的數據類型,
被索引的數據對應于每個塊范圍的列中值的最小值和最大值,使用這些操作符來支持用到索
引的查詢:
< <= = >= >多列索引
一個索引可以定義在表的多個列上;
CREATE INDEX test2_mm_idx ON test2 (major, minor);目前,只有 B-tree、GiST、GIN 和 BRIN 索引類型支持多列索引,最多可以指定32個列(該
限制可以在源代碼文件pg_config_manual.h中修改,但是修改后需要重新編PostgreSQL)。
B-tree索引
一個B-tree索引可以用于條件中涉及到任意索引列子集的查詢,但是當先導列(即最左邊的
那些列)上有約束條件時索引最為有效。
確切的規則是:
例如,在(a, b, c)上有一個索引并且給定一個查詢條件WHERE a = 5 AND b >= 42 AND c < 77,對索引的掃描將從第一個具有a = 5和b = 42的項開始向上進行,直到最后一個具有a = 5的項。
在掃描過程中,具有c>= 77的索引項將被跳過,但是它們還是會被掃描到。這個索引在原則上可以被用于在b和/或c上有約束而在a上沒有約束的查詢,但是整個索引都不得不被掃描,因此在大部分情況下規劃器寧可使用一個順序的表掃描來替代索引。
多列GiST索引
一個多列GiST索引可以用于條件中涉及到任意索引列子集的查詢。在其余列上的條件將限制
由索引返回的項,但是第一列上的條件是決定索引上掃描量的最重要因素。當第一列中具有
很少的可區分值時,一個GiST索引將會相對比較低效,即便在其他列上有很多可區分值。
其他多列索引
一個GIN索引可以用于條件中涉及到任意索引列子集的查詢。與B-tree和GiST不同,GIN的搜
索效率與查詢條件中使用哪些索引列無關。
多列 BRIN 索引可以被用于涉及該索引被索引列的任意子集的查詢條件。和 GIN 相似且不
同于 B-樹 或者 GiST,索引搜索效率與查詢條件使用哪個索引列無關。在單個表上使用多個
BRIN 索引來取代一個多列 BRIN 索引的唯一原因是為了使用不同的pages_per_range存儲參
數。
當然,要使索引起作用,查詢條件中的列必須要使用適合于索引類型的操作符,使用其他操
作符的子句將不會被考慮使用索引。
多列索引應該較少地使用。在絕大多數情況下,單列索引就足夠了且能節約時間和空間。具
有超過三個列的索引不太有用,除非該表的使用是極端程式化的。Section 11.5 以及Section 11.9 中有對不同索引配置優點的討論。
索引和ORDER BY
除了簡單地查找查詢要返回的行外,一個索引可能還需要將它們以指定的順序傳遞。這使得
查詢中的ORDER BY不需要獨立的排序步驟。在PostgreSQL當前支持的索引類型中,只有Btree可以產生排序后的輸出,其他索引類型會把行以一種沒有指定的且與實現相關的順序返
回。
規劃器會考慮以兩種方式來滿足一個ORDER BY說明:掃描一個符合說明的可用索引,或者先
以物理順序掃描表然后再顯式排序。對于一個需要掃描表的大部分的查詢,一個顯式的排序
很可能比使用一個索引更快,因為其順序訪問模式使得它所需要的磁盤I/O更少。只有在少
數行需要被取出時,索引才會更有用。
一種重要的特殊情況是ORDER BY與LIMIT n聯合使用:一個顯式的排序將會處理所有的數據來確定最前面的n行,但如果有一個符合ORDER BY的索引,前n行將會被直接獲取且根本不需要掃描剩下的數據。
默認情況下,B-tree索引將它的項以升序方式存儲,并將空值放在最后。這意味著對列x上
索引的一次前向掃描將產生滿足ORDER BY x(或者更長的形式:ORDER BY x ASC NULLS
LAST)的結果。索引也可以被后向掃描,產生滿足ORDER BY x DESC(ORDER BY x DESC NULLS
FIRST, NULLS FIRST是ORDER BY DESC的默認情況)。
我們可以在創建B-tree索引時通過ASC、DESC、NULLS FIRST和NULLS LAST選項來改變索引的
排序,例如:
CREATE INDEX test2_info_nulls_low ON test2 (info NULLS FIRST);
CREATE INDEX test3_desc_index ON test3 (id DESC NULLS LAST);
一個以升序存儲且將空值前置的索引可以根據掃描方向來支持ORDER BY x ASC NULLS FIRST或ORDER BY x DESC NULLS LAST。
讀者可能會疑惑為什么要麻煩地提供所有四個選項,因為兩個選項連同可能的后向掃描可以
覆蓋所有ORDER BY的變體。在單列索引中這些選項確實有冗余,但是在多列索引中它們卻很
有用。考慮(x, y)上的一個兩列索引:它可以通過前向掃描滿足ORDER BY x, y,或者通過
后向掃描滿足ORDER BY x DESC, y DESC。但是應用可能需要頻繁地使用ORDER BY x ASC,
y DESC。這樣就沒有辦法從通常的索引中得到這種順序,但是如果將索引定義為(x ASC, y
DESC)或者(x DESC, y ASC)就可以產生這種排序。
顯然,具有非默認排序的索引是相當專門的特性,但是有時它們會為特定查詢提供巨大的速
度提升。是否值得維護這樣一個索引取決于我們會多頻繁地使用需要特殊排序的查詢。
組合多個索引
只有查詢子句中在索引列上使用了索引操作符類中的操作符并且通過AND連接時才能使用單
一索引。例如,給定一個(a, b) 上的索引,查詢條件WHERE a = 5 AND b = 6可以使用該索
引,而查詢WHERE a = 5 OR b = 6不能直接使用該索引。
幸運的是,PostgreSQL具有組合多個索引(包括多次使用同一個索引)的能力來處理那些不
能用單個索引掃描實現的情況。系統能在多個索引掃描之間安排AND和OR條件。
例如,
WHERE x = 42 OR x = 47 OR x = 53 OR x= 99這樣一個查詢可以被分解成為四個獨立的在x上索引掃描,每一個掃描使用其中一個條件。
這些查詢的結果將被“或”起來形成最后的結果。另一個例子是如果我們在x和y上都有獨立的索引,WHERE x = 5 AND y = 6這樣的查詢的一種可能的實現方式就是分別使用兩個索引配合相應的條件,然后將結果“與”起來得到最后的結果行。
為了組合多個索引,系統掃描每一個所需的索引并在內存中準備一個位圖用于指示表中符合
索引條件的行的位置。然后這些位圖會被根據查詢的需要“與”和“或”起來。最后,實際
的表行將被訪問并返回。表行將被以物理順序訪問,因為位圖就是以這種順序布局的。
這意味著原始索引中的任何排序都會被丟失,并且如果存在一個ORDER BY子句就需要一個單獨的排序步驟。
由于這個原因以及每一個附加的索引都需要額外的時間,即使有額外的索引可用,規劃器有時也會選擇使用單一索引掃描。
在所有的應用(除了最簡單的應用)中,可能會有多種有用的索引組合,數據庫開發人員必
須做出權衡以決定提供哪些索引。
有時候多列索引最好,但是有時更好的選擇是創建單獨的索引并依賴于索引組合特性。例如,如果我們的查詢中有時只涉及到列x,有時候只涉及到列y,還有時候會同時涉及到兩列,我們可以選擇在x和y上創建兩個獨立索引然后依賴索引組合來處理同時涉及到兩列的查詢。我們當然也可以創建一個(x, y)上的多列索引。
當查詢同時涉及到兩列時,該索引會比組合索引效率更高,但是正如Section 11.3 中討論的,它在只涉及到y的查詢中幾乎完全無用,因此它不能是唯一的一個索引。一個多列索引和一個y上的獨立索引的組合將會工作得很好。
多列索引可以用于那些只涉及到x的查詢,盡管它比x上的獨立索引更大且更慢。最后一種選擇是創建所有三個索引,但是這種選擇最適合表經常被執行所有三種查詢但是很少被更新的情況。如果其中一種查詢要明顯少于其他類型的查詢,
我們可能需要只為常見類型的查詢創建兩個索引。
唯一索引
索引也可以被用來強制列值的唯一性,或者是多個列組合值的唯一性
CREATE UNIQUE INDEX name ON table (column [, ...]);當前,只有B-tree能夠被聲明為唯一。
當一個索引被聲明為唯一時,索引中不允許多個表行具有相同的索引值。空值被視為不相
同。一個多列唯一索引將會拒絕在所有索引列上具有相同組合值的表行。
PostgreSQL會自動為定義了一個唯一約束或主鍵的表創建一個唯一索引。該索引包含組成主
鍵或唯一約束的所有列(可能是一個多列索引),它也是用于強制這些約束的機制。
不需要手工在唯一列上創建索引,如果那樣做也只是重復了自動創建的索引而已。
表達式索引
一個索引列并不一定是底層表的一個列,也可以是從表的一列或多列計算而來的一個函數或
者標量表達式。這種特性對于根據計算結果快速獲取表中內容是有用的。
例如,一種進行大小寫不敏感比較的常用方法是使用lower函數
SELECT * FROM test1 WHERE lower(col1) = 'value'這種查詢可以利用一個建立在lower(col1)函數結果之上的索引:
CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1))如果我們將該索引聲明為UNIQUE,它將阻止創建在col1值上只有大小寫不同的行
另一個例子,如果我們經常進行如下的查詢:
SELECT * FROM people WHERE (first_name || ' ' || last_name) = 'John Smith';那么值得創建一個這樣的索引:
CREATE INDEX people_names ON people ((first_name || ' ' || last_name))正如第二個例子所示,CREATE INDEX命令的語法通常要求在被索引的表達式周圍書寫圓括
號。而如第一個例子所示,當表達式只是一個函數調用時可以省略掉圓括號;
索引表達式的維護代價較為昂貴,因為在每一個行被插入或更新時都得為它重新計算相應的
表達式。然而,索引表達式在進行索引搜索時卻不需要重新計算,因為它們的結果已經被存
儲在索引中了。
在上面兩個例子中,系統將會發現查詢的條件是WHERE indexedcolumn= 'constant',因此查詢的速度將等同于其他簡單索引查詢。因此,表達式索引對于檢索速度遠比插入和更新速度重要的情況非常有用。
部分索引
一個部分索引是建立在表的一個子集上,而該子集則由一個條件表達式(被稱為部分索引
的謂詞)定義。而索引中只包含那些符合該謂詞的表行的項。部分索引是一種專門的特性,
但在很多種情況下它們也很有用。
使用部分索引的一個主要原因是避免索引公值。由于搜索一個公值的查詢(一個在所有表行
中占比超過一定百分比的值)不會使用索引,所以完全沒有理由將這些行保留在索引中。這
可以減小索引的尺寸,同時也將加速使用索引的查詢。它也將加速很多表更新操作,因為這
種索引并不需要在所有情況下都被更新。Example 11.1 展示了一種可能的應用:
Example 11.1. 建立一個部分索引來排除公值
假設我們要在一個數據庫中保存網頁服務器訪問日志。大部分訪問都來自于我們組織內的IP
地址,但是有些來自于其他地方(如使用撥號連接的員工)。如果我們主要通過IP搜索來自
于外部的訪問,我們就沒有必要索引對應于我們組織內網的IP范圍。
假設有這樣一個表:
CREATE TABLE access_log ( url varchar, client_ip inet, ... )用以下命令可以創建適用于我們的部分索引:
CREATE INDEX access_log_client_ip_ix ON access_log (client_ip) WHERE NOT (client_ip > inet '192.168.100.0' AND client_ip < inet '192.168.100.255')一個使用該索引的典型查詢是:
SELECT * FROM access_log WHERE url = '/index.html' AND client_ip = inet '212.78.10.32';一個不能使用該索引的查詢:
SELECT * FROM access_log WHERE client_ip = inet '192.168.100.23';可以看到部分索引查詢要求公值能被預知,因此部分索引最適合于數據分布不會改變的情
況。當然索引也可以偶爾被重建來適應新的數據分布,但是這會增加維護負擔。
Example 11.2 展示了部分索引的另一個可能的用途:從索引中排除那些查詢不感興趣的值。
這導致了上述相同的好處,但它防止了通過索引來訪問“不感興趣的”值,即便在這種情況
下一個索引掃描是有益的。顯然,為這種場景建立部分索引需要很多考慮和實驗。
Example 11.2. 建立一個部分索引來排除不感興趣的值
如果我們有一個表包含已上賬和未上賬的訂單,其中未上賬的訂單在整個表中占據一小部分
且它們是最經常被訪問的行。我們可以通過只在未上賬的行上創建一個索引來提高性能。創
建索引的命令如下:
CREATE INDEX orders_unbilled_index ON orders (order_nr) WHERE billed is not true使用該索引的一個可能查詢是:
SELECT * FROM orders WHERE billed is not true AND order_nr < 10000;然而,索引也可以用于完全不涉及order_nr的查詢,例如
SELECT * FROM orders WHERE billed is not true AND amount > 5000.00;這并不如在amount列上部分索引有效,因為系統必須掃描整個索引。然而,如果有相對較少
的未上賬訂單,使用這個部分索引來查找未上賬訂單將會更好。
注意這個查詢將不會使用該索引:
SELECT * FROM orders WHERE order_nr = 3501;訂單3501可能在已上賬訂單或未上賬訂單中。
Example 11.2 也顯示索引列和謂詞中使用的列并不需要匹配。PostgreSQL支持使用任意謂詞
的部分索引,只要其中涉及的只有被索引表的列。然而,記住謂詞必須匹配在將要受益于索
引的查詢中使用的條件。更準確地,只有當系統能識別查詢的WHERE條件從數學上索引的謂
詞時,一個部分索引才能被用于一個查詢。PostgreSQL并不能給出一個精致的定理證明器來
識別寫成不同形式在數學上等價的表達式(一方面創建這種證明器極端困難,另一方面即便
能創建出來對于實用也過慢)。系統可以識別簡單的不等蘊含,例如“x < 1”蘊含“x
< 2”;否則謂詞條件必須準確匹配查詢的WHERE條件中的部分,或者索引將不會被識別為可
用。匹配發生在查詢規劃期間而不是運行期間。因此,參數化查詢子句無法配合一個部分索
引工作。例如,對于參數的所有可能值來說,一個具有參數“x < ?”的預備查詢絕不會蘊
含“x < 2”。
部分索引的第三種可能的用途并不要求索引被用于查詢。其思想是在一個表的子集上創建一
個唯一索引,如Example 11.3 所示。這對那些滿足索引謂詞的行強制了唯一性,而對那些不
滿足的行則沒有影響。
Example 11.3. 建立一個部分唯一索引
假設我們有一個描述測試結果的表。我們希望保證其中對于一個給定的主題和目標組合只有
一個“成功”項,但其中可能會有任意多個“不成功”項。實現它的方式是:
CREATE TABLE tests ( subject text, target text, success boolean, ... )CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target) WHERE success;當有少數成功測試和很多不成功測試時這是一種特別有效的方法。
最后,一個部分索引也可以被用來重載系統的查詢規劃選擇。同樣,具有特殊分布的數據集
可能導致系統在它并不需要索引的時候選擇使用索引。在此種情況下可以被建立,這樣它將
不會被那些無關的查詢所用。通常,PostgreSQL會對索引使用做出合理的選擇(例如,它會
在檢索公值時避開索引,這樣前面的例子只能節約索引尺寸,它并非是避免索引使用所必需
的),非常不正確的規劃選擇則需要作為故障報告。
記住建立一個部分索引意味著我們知道的至少和查詢規劃器所知的一樣多,尤其是我們知道
什么時候一個索引會是有益的。構建這些知識需要經驗和對于PostgreSQL中索引工作方式的
理解。在大部分情況下,一個部分索引相對于一個普通索引的優勢很小。
關于部分索引的更多信息可以在[ston89b]、[olson93]和[seshadri95]中找到
只用索引的掃描和覆蓋索引
操作符類和操作符族
索引和排序規則
檢查索引使用
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的postgresql两个列模糊比较_数据分析之SQL优化系列(二)---PostgreSQL 的索引的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 未满十八岁可以办定期存款吗?
- 下一篇: 正股价和转股价什么意思?