當在看Monetdb列存行只支持IMPRINTS和ORDERED這兩種索引,且只支持定長數值類型時,就在思考,對於列存,還有必要建索引嗎?在PostgreSQL的索引就要靈活很多,我對常用列建合理的索引,是不是能達到列存的效果?(肯定沒有)。
當然,有索引還是快很多:
1)對於整型列來說,應該是用ORDERED索引,建類似於btree索引,將數據按大小進行了排序,當執行> = < <= >= between in is等操作時,就非常快了,不需要整個列進行掃描比較。
2)對於字符串呢? 列存對字符串的壓縮還是比較給力的,對於特殊的字符串列,還可以用字典的方式來壓縮。但是沒有索引的話,就只能是進行全列掃描了,效率不可能太高。
一、PostgreSQL數據庫的索引類型
1.Btree索引: 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索引: 散列(Hash)索引只能處理簡單的等於比較。當索引列使用等於操作符進行比較時,查詢規划器會考慮使用散列索引。 這里需要額外說明的是,PostgreSQL散列索引的性能不比B-Tree索引強,但是散列索引的尺寸和構造時間則更差。另外,由於散列索引操作目前沒有記錄WAL日志,因此一旦發生了數據庫崩潰,我們將不得不用REINDEX重建散列索引。
適用於字符串數據類型,而字符串一般的操作也是等於判斷。 3.GIN索引: GIN是倒排索引,存儲被索引字段的VALUE或VALUE的元素,以及行號的list或tree。主要用於:當需要搜索多值類型內的VALUE時,適合多值類型,例如數組、全文檢索、TOKEN。(根據不同的類型,支持相交、包含、大於、在左邊、在右邊等搜索); 4.GIST索引: GiST是一個通用的索引接口,可以使用GiST實現b-tree, r-tree等索引結構。一般在地圖插件PostGIS中使用,且不同的類型,支持的索引檢索也各不一樣。例如: 1) 幾何類型,支持位置搜索(包含、相交、在上下左右等),按距離排序。 2) 范圍類型,支持位置搜索(包含、相交、在左右等)。 3) IP類型,支持位置搜索(包含、相交、在左右等)。 4) 空間類型(PostGIS),支持位置搜索(包含、相交、在上下左右等),按距離排序。 5) 標量類型,支持按距離排序。 還有一些其他比較特殊的索引類型,用的太少了。
二、復合索引,也就是多列索引
一般是針對用戶對某個表的查詢條件比較固定。這樣可以考慮創建多列索引,多列索引中的單個列在被查詢時,也可能會走多列索引。但是僅在查詢包含多列索引的最左列時,效率最高,最可能走這個多列索引。
1.多列索引檢索順序:
當b+樹的數據項是復合的數據結構,比如(name,age,sex)的時候,b+數是按照從左到右的順序來建立搜索樹的,比如當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex,最后得到檢索的數據;但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪個節點,因為建立搜索樹的時候name就是第一個比較因子,必須要先根據name來搜索才能知道下一步去哪里查詢。比如當(張三,F)這樣的數據來檢索時,b+樹可以用name來指定搜索方向,但下一個字段age的缺失,所以只能把名字等於張三的數據都找到,然后再匹配性別是F的數據了, 這個是非常重要的性質,即索引的最左匹配特性。
2.多列索引創建原則:
1) 最左前綴匹配原則,非常重要的原則,數據庫會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整;以index(a, b, c,d)為例,多列索引的利用情況是: where a=3 是 where a=3 and b=5 是 where a=3 and b=5 and c=4 是 where b=3 否 where c=4 否 where a=3 and c=4 a列能用到索引,b不能 where a=3 and b>10 and c=7 a能,b能,c不能 where a=3 and b like 'xxx%' and c=7 a能,b能,c不能 2) =和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式; 3) 盡量選擇區分度高的列作為索引,區分度的公式是count(distinct col)/count(*),表示字段不重復的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,那可能有人會問,這個比例有什么經驗值嗎?使用場景不同,這個值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條記錄; 4) 索引列不能參與計算,保持列“干凈”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’); 5) 盡量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那么只需要修改原來的索引即可;
三、條件索引
四、部分索引
五、聚集索引
CLUSTER指示Postgres近似地基於索引indexname的度量對表進行存儲建簇.索引必須已經在表上定義了。 當對一個表建簇后,該表的物理存儲將基於索引信息進行。建簇是靜態的,也就是說,當表被更新后,改變的內容不會建簇。不會試圖對更新過的記錄重新建簇。如果需要,可以通過手工執行該命令的方法重建簇。 注意: 該表實際上按索引順序拷貝到了一個臨時表中,然后重新改成原名。因此,在建簇時所有賦予的權限和其它索引都將丟失。 如果你只是隨機的訪問表中的行,那么在堆表中的數據的實際存儲順序是無關緊要的。但是,如果你對某些數據的訪問多於其他數據,而且有一個索引將這些數據分組,建蔟會提高查詢速度。 另一個很有幫助的例子是當你用索引從一個表中取出幾個記錄時。如果你從一個表中請求一定索引范圍的值,或者是一個索引過的值對應多行,建蔟也會有助於應用,因為如果索引標識出第一匹配行所在的堆存儲頁,所有其他行也可能已經在同一堆存儲頁里了,這樣便節省了磁盤訪問的時間,加速了查詢。 有兩種建簇方法: 第一種是用CLUSTER命令,此命令將原表按你聲明的索引重新排列。這個動作在操作大表時可能會很慢,因為每一行都從堆存儲頁里按索引順序取出,如果存儲頁表沒有排序,整個表是隨機存放在各個頁面的,因而每行都要進行依次磁盤頁面操作。雖然PostgreSQL有一個共享存儲,但一個大表的主體是不可能都放到緩沖區的,需要經過多次交換。 另一個對數據建簇的方法是用SQL語句: SELECT columnlist INTO TABLE newtable FROM table ORDER BY columnlist 這個用法使用排序的代碼ORDER BY來匹配索引,在對未排序的數據操作時速度快得多。然后你可以刪除舊表,用ALTER TABLE/RENAME將newtable改成舊表名,並且重建所有索引。唯一的問題是OID不保留。這時再做聚集將快得多,因為大多數堆棧數據已經排過序了而且使用現有的索引,當然也可以不做聚集了。
部分參考:
http://www.cnblogs.com/stephen-liu74/archive/2012/05/09/2298182.html