彙集和非彙集索引

彙集索引

                一種索引,該索引中鍵值的邏輯順序決定了表中相應行的物理順序。
  彙集索引肯定表中數據的物理順序。彙集索引相似於電話簿,後者按姓氏排列數據。因爲彙集索引規定數據在表中的物理存儲順序,所以一個表只能包含一個彙集索引。但該索引能夠包含多個列(組合索引),就像電話簿按姓氏和名字進行組織同樣。
    
     彙集索引對於那些常常要搜索範圍值的列特別有效。使用匯集索引找到包含第一個值的行後,即可以確保包含後續索引值的行在物理相鄰。例如,若是應用程序執行 的一個查詢常常檢索某一日期範圍內的記錄,則使用匯集索引能夠迅速找到包含開始日期的行,而後檢索表中全部相鄰的行,直到到達結束日期。這樣有助於提升此 類查詢的性能。一樣,若是對從表中檢索的數據進行排序時常常要用到某一列,則能夠將該表在該列上彙集(物理排序),避免每次查詢該列時都進行排序,從而節 省成本。
    

     當索引值惟一時,使用匯集索引查找特定的行也頗有效率。例如,使用惟一僱員 ID 列 emp_id 查找特定僱員的最快速的方法,是在 emp_id 列上建立彙集索引或 PRIMARY KEY 約束。 數據庫

非彙集索引

               一種索引,該索引中索引的邏輯順序與磁盤上行的物理存儲順序不一樣。 數據結構

索引是經過二叉樹的數據結構來描述的,咱們能夠這麼理解聚簇索引:索引的葉節點就是數據節點。而非聚簇索引的葉節點仍然是索引節點,只不過有一個指針指向對應的數據塊。以下圖: 性能

                                      (非彙集索引) 測試

                                      (彙集索引) spa

    1、深刻淺出理解索引結構

      實際上,您能夠把索引理解爲一種特殊的目錄。微軟的SQL SERVER提供了兩種索引:彙集索引(clustered index,也稱聚類索引、簇集索引)和非彙集索引(nonclustered index,也稱非聚類索引、非簇集索引)。下面,咱們舉例來講明一下彙集索引和非彙集索引的區別:
      其實,咱們的漢語字典的正文自己就是一個彙集索引。好比,咱們要查「安」字,就會很天然地翻開字典的前幾頁,由於「安」的拼音是「an」,而按照拼音排序漢字的字典是以英文字母「a」開頭並以「z」結尾的,那麼「安」字就天然地排在字典的前部。若是您翻完了全部以「a」開頭的部分仍然找不到這個字,那麼就說明您的字典中沒有這個字;一樣的,若是查「張」字,那您也會將您的字典翻到最後部分,由於「張」的拼音是「zhang」。也就是說,字典的正文部分自己就是一個目錄,您不須要再去查其餘目錄來找到您須要找的內容。咱們把這種正文內容自己就是一種按照必定規則排列的目錄稱爲「彙集索引」。
      若是您認識某個字,您能夠快速地從自動中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛纔的方法找到您要查的字,而須要去根據「偏旁部首」查到您要找的字,而後根據這個字後的頁碼直接翻到某頁來找到您要找的字。但您結合「部首目錄」和「檢字表」而查到的字的排序並非真正的正文的排序方法,好比您查「張」字,咱們能夠看到在查部首以後的檢字表中「張」的頁碼是672頁,檢字表中「張」的上面是「馳」字,但頁碼倒是63頁,「張」的下面是「弩」字,頁面是390頁。很顯然,這些字並非真正的分別位於「張」字的上下方,如今您看到的連續的「馳、張、弩」三字實際上就是他們在非彙集索引中的排序,是字典正文中的字在非彙集索引中的映射。咱們能夠經過這種方式來找到您所須要的字,但它須要兩個過程,先找到目錄中的結果,而後再翻到您所須要的頁碼。咱們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱爲「非彙集索引」。
      經過以上例子,咱們能夠理解到什麼是「彙集索引」和「非彙集索引」。進一步引伸一下,咱們能夠很容易的理解:每一個表只能有一個彙集索引,由於目錄只能按照一種方法進行排序。

    2、什麼時候使用匯集索引或非彙集索引

下面的表總結了什麼時候使用匯集索引或非彙集索引(很重要): .net

動做描述 使用匯集索引 使用非彙集索引
列常常被分組排序
返回某範圍內的數據 不該
一個或極少不一樣值 不該 不該
小數目的不一樣值 不該
大數目的不一樣值 不該
頻繁更新的列 不該
外鍵列
主鍵列
頻繁修改索引列 不該


      事實上,咱們能夠經過前面彙集索引和非彙集索引的定義的例子來理解上表。如:返回某範圍內的數據一項。好比您的某個表有一個時間列,剛好您把聚合索引創建在了該列,這時您查詢2004年1月1日至2004年10月1日之間的所有數據時,這個速度就將是很快的,由於您的這本字典正文是按日期進行排序的,聚類索引只須要找到要檢索的全部數據中的開頭和結尾數據便可;而不像非彙集索引,必須先查到目錄中查到每一項數據對應的頁碼,而後再根據頁碼查到具體內容。

    3、結合實際,談索引使用的誤區

      理論的目的是應用。雖然咱們剛纔列出了什麼時候應使用匯集索引或非彙集索引,但在實踐中以上規則卻很容易被忽視或不能根據實際狀況進行綜合分析。下面咱們將根據在實踐中遇到的實際問題來談一下索引使用的誤區,以便於你們掌握索引創建的方法。

    一、主鍵就是彙集索引
      這種想法筆者認爲是極端錯誤的,是對彙集索引的一種浪費。雖然SQL SERVER默認是在主鍵上創建彙集索引的。
      一般,咱們會在每一個表中都創建一個ID列,以區分每條數據,而且這個ID列是自動增大的,步長通常爲1。咱們的這個辦公自動化的實例中的列Gid就是如此。此時,若是咱們將這個列設爲主鍵,SQL SERVER會將此列默認爲彙集索引。這樣作有好處,就是可讓您的數據在數據庫中按照ID進行物理排序,但筆者認爲這樣作意義不大。
      顯而易見,彙集索引的優點是很明顯的,而每一個表中只能有一個彙集索引的規則,這使得彙集索引變得更加珍貴。
      從咱們前面談到的彙集索引的定義咱們能夠看出,使用匯集索引的最大好處就是可以根據查詢要求,迅速縮小查詢範圍,避免全表掃描。在實際應用中,由於 ID號是自動生成的,咱們並不知道每條記錄的ID號,因此咱們很難在實踐中用ID號來進行查詢。這就使讓ID號這個主鍵做爲彙集索引成爲一種資源浪費。其次,讓每一個ID號都不一樣的字段做爲彙集索引也不符合「大數目的不一樣值狀況下不該創建聚合索引」規則;固然,這種狀況只是針對用戶常常修改記錄內容,特別是索引項的時候會負做用,但對於查詢速度並無影響。
      在辦公自動化系統中,不管是系統首頁顯示的須要用戶簽收的文件、會議仍是用戶進行文件查詢等任何狀況下進行數據查詢都離不開字段的是「日期」還有用戶自己的「用戶名」。
      一般,辦公自動化的首頁會顯示每一個用戶還沒有簽收的文件或會議。雖然咱們的where語句能夠僅僅限制當前用戶還沒有簽收的狀況,但若是您的系統已創建了很長時間,而且數據量很大,那麼,每次每一個用戶打開首頁的時候都進行一次全表掃描,這樣作意義是不大的,絕大多數的用戶1個月前的文件都已經瀏覽過了,這樣作只能徒增數據庫的開銷而已。事實上,咱們徹底可讓用戶打開系統首頁時,數據庫僅僅查詢這個用戶近3個月來未閱覽的文件,經過「日期」這個字段來限制表掃描,提升查詢速度。若是您的辦公自動化系統已經創建的2年,那麼您的首頁顯示速度理論上將是原來速度8倍,甚至更快。
      在這裏之因此提到「理論上」三字,是由於若是您的彙集索引仍是盲目地建在ID這個主鍵上時,您的查詢速度是沒有這麼高的,即便您在「日期」這個字段上創建的索引(非聚合索引)。下面咱們就來看一下在1000萬條數據量的狀況下各類查詢的速度表現(3個月內的數據爲25萬條):

    (1)僅在主鍵上創建彙集索引,而且不劃分時間段:

    Select gid,fariqi,neibuyonghu,title from tgongwen

    用時:128470毫秒(即:128秒)

    (2)在主鍵上創建彙集索引,在fariq上創建非彙集索引:

    select gid,fariqi,neibuyonghu,title from Tgongwen
    where fariqi> dateadd(day,-90,getdate())

    用時:53763毫秒(54秒)

    (3)將聚合索引創建在日期列(fariqi)上:

    select gid,fariqi,neibuyonghu,title from Tgongwen
    where fariqi> dateadd(day,-90,getdate())

    用時:2423毫秒(2秒)

      雖然每條語句提取出來的都是25萬條數據,各類狀況的差別倒是巨大的,特別是將彙集索引創建在日期列時的差別。事實上,若是您的數據庫真的有1000 萬容量的話,把主鍵創建在ID列上,就像以上的第一、2種狀況,在網頁上的表現就是超時,根本就沒法顯示。這也是我摒棄ID列做爲彙集索引的一個最重要的因素。得出以上速度的方法是:在各個select語句前加:

    declare @d datetime
    set @d=getdate()

    並在select語句後加:

    select [語句執行花費時間(毫秒)]=datediff(ms, @d ,getdate())     二、只要創建索引就能顯著提升查詢速度       事實上,咱們能夠發現上面的例子中,第二、3條語句徹底相同,且創建索引的字段也相同;不一樣的僅是前者在fariqi字段上創建的是非聚合索引,後者在此字段上創建的是聚合索引,但查詢速度卻有着天壤之別。因此,並不是是在任何字段上簡單地創建索引就能提升查詢速度。       從建表的語句中,咱們能夠看到這個有着1000萬數據的表中fariqi字段有5003個不一樣記錄。在此字段上創建聚合索引是再合適不過了。在現實中,咱們天天都會發幾個文件,這幾個文件的發文日期就相同,這徹底符合創建彙集索引要求的:「既不能絕大多數都相同,又不能只有極少數相同」的規則。由此看來,咱們創建「適當」的聚合索引對於咱們提升查詢速度是很是重要的。     三、把全部須要提升查詢速度的字段都加進彙集索引,以提升查詢速度       上面已經談到:在進行數據查詢時都離不開字段的是「日期」還有用戶自己的「用戶名」。既然這兩個字段都是如此的重要,咱們能夠把他們合併起來,創建一個複合索引(compound index)。       不少人認爲只要把任何字段加進彙集索引,就能提升查詢速度,也有人感到迷惑:若是把複合的彙集索引字段分開查詢,那麼查詢速度會減慢嗎?帶着這個問題,咱們來看一下如下的查詢速度(結果集都是25萬條數據):(日期列fariqi首先排在複合彙集索引的起始列,用戶名neibuyonghu排在後列):     (1)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>''2004-5-5''     查詢速度:2513毫秒     (2)select gid,fariqi,neibuyonghu,title from Tgongwen                 where fariqi>''2004-5-5'' and neibuyonghu=''辦公室''     查詢速度:2516毫秒     (3)select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu=''辦公室''     查詢速度:60280毫秒       從以上試驗中,咱們能夠看到若是僅用匯集索引的起始列做爲查詢條件和同時用到複合彙集索引的所有列的查詢速度是幾乎同樣的,甚至比用上所有的複合索引列還要略快(在查詢結果集數目同樣的狀況下);而若是僅用複合彙集索引的非起始列做爲查詢條件的話,這個索引是不起任何做用的。固然,語句一、2的查詢速度同樣是由於查詢的條目數同樣,若是複合索引的全部列都用上,並且查詢結果少的話,這樣就會造成「索引覆蓋」,於是性能能夠達到最優。同時,請記住:不管您是否常用聚合索引的其餘列,但其前導列必定要是使用最頻繁的列。     4、其餘書上沒有的索引使用經驗總結     一、用聚合索引比用不是聚合索引的主鍵速度快       下面是實例語句:(都是提取25萬條數據)     select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16''     使用時間:3326毫秒     select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000     使用時間:4470毫秒     這裏,用聚合索引比用不是聚合索引的主鍵速度快了近1/4。     二、用聚合索引比用通常的主鍵做order by時速度快,特別是在小數據量狀況下     select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi     用時:12936     select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid     用時:18843       這裏,用聚合索引比用通常的主鍵做order by時,速度快了3/10。事實上,若是數據量很小的話,用匯集索引做爲排序列要比使用非彙集索引速度快得明顯的多;而數據量若是很大的話,如10萬以上,則兩者的速度差異不明顯。     三、使用聚合索引內的時間段,搜索時間會按數據佔整個數據表的百分比成比例減小,而不管聚合索引使用了多少個:     select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>''2004-1-1''     用時:6343毫秒(提取100萬條)     select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>''2004-6-6''     用時:3170毫秒(提取50萬條)     select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16''     用時:3326毫秒(和上句的結果如出一轍。若是採集的數量同樣,那麼用大於號和等於號是同樣的)     select gid,fariqi,neibuyonghu,reader,title from Tgongwen                 where fariqi>''2004-1-1'' and fariqi<''2004-6-6''     用時:3280毫秒     四、日期列不會由於有分秒的輸入而減慢查詢速度       下面的例子中,共有100萬條數據,2004年1月1日之後的數據有50萬條,但只有兩個不一樣的日期,日期精確到日;以前有數據50萬條,有5000個不一樣的日期,日期精確到秒。     select gid,fariqi,neibuyonghu,reader,title from Tgongwen               where fariqi>''2004-1-1'' order by fariqi     用時:6390毫秒     select gid,fariqi,neibuyonghu,reader,title from Tgongwen                 where fariqi<''2004-1-1'' order by fariqi     用時:6453毫秒     5、其餘注意事項       「水可載舟,亦可覆舟」,索引也同樣。索引有助於提升檢索性能,但過多或不當的索引也會致使系統低效。由於用戶在表中每加進一個索引,數據庫就要作更多的工做。過多的索引甚至會致使索引碎片。       因此說,咱們要創建一個「適當」的索引體系,特別是對聚合索引的建立,更應精益求精,以使您的數據庫能獲得高性能的發揮。       固然,在實踐中,做爲一個盡職的數據庫管理員,您還要多測試一些方案,找出哪一種方案效率最高、最爲有效。
相關文章
相關標籤/搜索