PostgreSQL【索引】

1、索引的類型:


    PostgreSQL提供了多種索引類型:B-Tree、Hash、GiST和GIN,因爲它們使用了不一樣的算法,所以每種索引類型都有其適合的查詢類型,缺省時,CREATE INDEX命令將建立B-Tree索引。
    
    1. B-Tree:
    CREATE TABLE test1 (
        id integer,
        content varchar
    );
    CREATE INDEX test1_id_index ON test1 (id);    
    B-Tree索引主要用於等於和範圍查詢,特別是當索引列包含操做符" <、<=、=、>=和>"做爲查詢條件時,PostgreSQL的查詢規劃器都會考慮使用B-Tree索引。在使用 BETWEEN、IN、IS NULL和IS NOT NULL的查詢中,PostgreSQL也可使用B-Tree索引。然而對於基於模式匹配操做符的查詢,如LIKE、ILIKE、~和 ~*,僅當模式存在一個常量,且該常量位於模式字符串的開頭時,如col LIKE 'foo%'或col ~ '^foo',索引纔會生效,不然將會執行全表掃描,如:col LIKE '%bar'。
    
    2. Hash:
    CREATE INDEX name ON table USING hash (column);
    散列(Hash)索引只能處理簡單的等於比較。當索引列使用等於操做符進行比較時,查詢規劃器會考慮使用散列索引。
    這裏須要額外說明的是,PostgreSQL散列索引的性能不比B-Tree索引強,可是散列索引的尺寸和構造時間則更差。另外,因爲散列索引操做目前沒有記錄WAL日誌,所以一旦發生了數據庫崩潰,咱們將不得不用REINDEX重建散列索引。
    
    3. GiST:
    GiST索引不是一種單獨的索引類型,而是一種架構,能夠在該架構上實現不少不一樣的索引策略。從而可使GiST索引根據不一樣的索引策略,而使用特定的操做符類型。
    
    4. GIN:
    GIN索引是反轉索引,它能夠處理包含多個鍵的值(好比數組)。與GiST相似,GIN一樣支持用戶定義的索引策略,從而可使GIN索引根據不一樣的索 引策略,而使用特定的操做符類型。做爲示例,PostgreSQL的標準發佈中包含了用於一維數組的GIN操做符類型,如:<@、 @>、=、&&等。

2、複合索引:

    PostgreSQL中的索引能夠定義在數據表的多個字段上,如:
    CREATE TABLE test2 (
        major int,
        minor int,
        name varchar
    }
    CREATE INDEX test2_mm_idx ON test2 (major, minor);
    在當前的版本中,只有B-tree、GiST和GIN支持複合索引,其中最多能夠聲明32個字段。
    1. B-Tree類型的複合索引:
    在B-Tree類型的複合索引中,該索引字段的任意子集都可用於查詢條件,不過,只有當複合索引中的第一個索引字段(最左邊)被包含其中時,才能夠得到最高效率。
    
    2. GiST類型的複合索引:
    在GiST類型的複合索引中,只有當第一個索引字段被包含在查詢條件中時,才能決定該查詢會掃描多少索引數據,而其餘索引字段上的條件只是會限制索引返回的條目。假如第一個索引字段上的大多數數據都有相同的鍵值,那麼此時應用GiST索引就會比較低效。

    3. GIN類型的複合索引:
    與B-Tree和GiST索引不一樣的是,GIN複合索引不會受到查詢條件中使用了哪些索引字段子集的影響,不管是哪一種組合,都會獲得相同的效率。 html

    使用複合索引應該謹慎。在大多數狀況下,單一字段上的索引就已經足夠了,而且還節約時間和空間。除非表的使用模式很是固定,不然超過三個字段的索引幾乎沒什麼用處。

3、組合多個索引:

    PostgreSQL能夠在查詢時組合多個索引(包括同一索引的屢次使用),來處理單個索引掃描不能實現的場合。與此同時,系統還能夠在多個索引掃描之 間組成AND和OR的條件。好比,一個相似WHERE x = 42 OR x = 47 OR x = 53 OR x = 99的查詢,能夠被分解成四個獨立的基於x字段索引的掃描,每一個掃描使用一個查詢子句,以後再將這些掃描結果OR在一塊兒並生成最終的結果。另一個例子 是,若是咱們在x和y上分別存在獨立的索引,那麼一個相似WHERE x = 5 AND y = 6的查詢,就會分別基於這兩個字段的索引進行掃描,以後再將各自掃描的結果進行AND操做並生成最終的結果行。
    爲了組合多個索引,系統掃描每一個須要的索引,而後在內存裏組織一個BITMAP,它將給出索引掃描出的數據在數據表中的物理位置。而後,再根據查詢的需 要,把這些位圖進行AND或者OR的操做並得出最終的BITMAP。最後,檢索數據表並返回數據行。表的數據行是按照物理順序進行訪問的,由於這是位圖的 佈局,這就意味着任何原來的索引的排序都將消失。若是查詢中有ORDER BY子句,那麼還將會有一個額外的排序步驟。由於這個緣由,以及每一個額外的索引掃描都會增長額外的時間,這樣規劃器有時候就會選擇使用簡單的索引掃描,即 使有多個索引可用也會如此。      算法

   
4、惟一索引:

    目前,只有B-Tree索引能夠被聲明爲惟一索引。
    CREATE UNIQUE INDEX name ON table (column [, ...]);
    若是索引聲明爲惟一索引,那麼就不容許出現多個索引值相同的行。咱們認爲NULL值相互間不相等。
   
5、表達式索引:

    表達式索引主要用於在查詢條件中存在基於某個字段的函數或表達式的結果與其餘值進行比較的狀況,如:
    SELECT * FROM test1 WHERE lower(col1) = 'value';
    此時,若是咱們僅僅是在col1字段上創建索引,那麼該查詢在執行時必定不會使用該索引,而是直接進行全表掃描。若是該表的數據量較大,那麼執行該查詢也將會須要很長時間。解決該問題的辦法很是簡單,在test1表上創建基於col1字段的表達式索引,如:
    CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1));
    若是咱們把該索引聲明爲UNIQUE,那麼它會禁止建立那種col1數值只是大小寫有區別的數據行,以及col1數值徹底相同的數據行。所以,在表達式上的索引能夠用於強制那些沒法定義爲簡單惟一約束的約束。如今讓咱們再看一個應用表達式索引的例子。
    SELECT * FROM people WHERE (first_name || ' ' || last_name) = 'John Smith';
    和上面的例子同樣,儘管咱們可能會爲first_name和last_name分別建立獨立索引,或者是基於這兩個字段的複合索引,在執行該查詢語句時,這些索引均不會被使用,該查詢可以使用的索引只有咱們下面建立的表達式索引。
    CREATE INDEX people_names ON people ((first_name || ' ' || last_name));
    CREATE INDEX命令的語法一般要求在索引表達式周圍書寫圓括弧,就像咱們在第二個例子裏顯示的那樣。若是表達式只是一個函數調用,那麼能夠省略,就像咱們在第一個例子裏顯示的那樣。
    從索引維護的角度來看,索引表達式要相對低效一些,由於在插入數據或者更新數據的時候,都必須爲該行計算表達式的結果,並將該結果直接存儲到索引裏。然 而在查詢時,PostgreSQL就會把它們看作WHERE idxcol = 'constant',所以搜索的速度等效於基於簡單索引的查詢。一般而言,咱們只是應該在檢索速度比插入和更新速度更重要的場景下使用表達式索引。
    
6、部分索引:

    部分索引(partial index)是創建在一個表的子集上的索引,而該子集是由一個條件表達式定義的(叫作部分索引的謂詞)。該索引只包含表中那些知足這個謂詞的行。
    因爲不是在全部的狀況下都須要更新索引,所以部分索引會提升數據插入和數據更新的效率。然而又由於部分索引比普通索引要小,所以能夠更好的提升確實須要索引部分的查詢效率。見如下三個示例:
    1. 索引字段和謂詞條件字段一致:
    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';

    2. 索引字段和謂詞條件字段不一致:
    PostgreSQL支持帶任意謂詞的部分索引,惟一的約束是謂詞的字段也要來自於一樣的數據表。注意,若是你但願你的查詢語句可以用到部分索引,那麼 就要求該查詢語句的條件部分必須和部分索引的謂詞徹底匹配。 準確說,只有在PostgreSQL可以識別出該查詢的WHERE條件在數學上涵蓋了該索引的謂詞時,這個部分索引才能被用於該查詢。
    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;
    那麼對於以下查詢呢?
    SELECT * FROM orders WHERE billed is not true AND amount > 5000.00;
    這個查詢將不像上面那個查詢這麼高效,畢竟查詢的條件語句中沒有用到索引字段,然而查詢條件"billed is not true"卻和部分索引的謂詞徹底匹配,所以PostgreSQL將掃描整個索引。這樣只有在索引數據相對較少的狀況下,該查詢才能更有效一些。
    下面的查詢將不會用到部分索引。
    SELECT * FROM orders WHERE order_nr = 3501;
    
    3. 數據表子集的惟一性約束:
    CREATE TABLE tests (
        subject text,
        target text,
        success boolean,
        ...
    );
    CREATE UNIQUE INDEX tests_success_constraint ON tests(subject, target) WHERE success;
    該部分索引將只會對success字段值爲true的數據進行惟一性約束。在實際的應用中,若是成功的數據較少,而不成功的數據較多時,該實現方法將會很是高效。
    
7、檢查索引的使用:

    見如下四條建議:
    1. 老是先運行ANALYZE。
    該命令將會收集表中數值分佈情況的統計。在估算一個查詢返回的行數時須要這個信息,而規劃器則須要這個行數以便給每一個可能的查詢規劃賦予真實的開銷值。 若是缺少任何真實的統計信息,那麼就會使用一些缺省數值,這樣確定是不許確的。所以,若是尚未運行ANALYZE就檢查一個索引的使用情況,那將會是一 次失敗的檢查。
    2. 使用真實的數據作實驗。
    用測試數據填充數據表,那麼該表的索引將只會基於測試數據來評估該如何使用索引,而不是對全部的數據都如此使用。好比從100000行中選1000行, 規劃器可能會考慮使用索引,那麼若是從100行中選1行就很難說也會使用索引了。由於100行的數據極可能是存儲在一個磁盤頁面中,然而沒有任何查詢規劃 能比經過順序訪問一個磁盤頁面更加高效了。與此同時,在模擬測試數據時也要注意,若是這些數據是很是類似的數據、徹底隨機的數據,或按照排序順序插入的數 據,都會令統計信息偏離實際數據應該具備的特徵。    
    3. 若是索引沒有獲得使用,那麼在測試中強制它的使用也許會有些價值。有一些運行時參數能夠關閉各類各樣的查詢規劃。
    4. 強制使用索引用法將會致使兩種可能:一是系統選擇是正確的,使用索引實際上並不合適,二是查詢計劃的開銷計算並不能反映現實狀況。這樣你就應該對使用和不使用索引的查詢進行計時,這個時候EXPLAIN ANALYZE命令就頗有用了。 數據庫

相關文章
相關標籤/搜索