SQL Server 2008 索引之十億行數據測試報告

索引簡介


      索引被創建於已有的表中,它可使對行的定位更快速更有效。可以在表格的一個或者多個列上創建索引,每個索引都會被起個名字。用戶無法看到索引,它們只能被用來加速查詢。

當然更新一個包含索引的表需要比更新一個沒有索引的表花更多的時間,這是由於索引本身也需要更新。因此,理想的做法是僅僅在常常用於搜索的列上面創建索引。

    索引的分類:微軟的SQL SERVER提供了兩種索引:聚集索引(clustered index,也稱聚類索引、簇集索引)和非聚集索引(nonclustered index,也稱非聚類索引、非簇集索引)。


深入淺出理解索引結構


       實際上,您可以把索引理解爲一種特殊的目錄。下面,我們舉例來說明一下聚集索引和非聚集索引的區別:
其實,我們的漢語字典的正文本身就是一個聚集索引。比如,我們要查“中”字,就會很自然地翻開字典的後幾頁,因爲“中”的拼音是“zhong”,而按照拼音排序漢字的字典是以英文字母“a”開頭並以“z”結尾的,那麼“中”字就自然地排在字典的前部。

如果您翻完了所有以“z”開頭的部分仍然找不到這個字,那麼就說明您的字典中沒有這個字;同樣的,如果查“啊”字,那您也會將您的字典翻到最前部分,因爲“啊”的拼音是“a”。也就是說,字典的正文部分本身就是一個目錄,您不需要再去查其他目錄來找到您需要找的內容。這種正文內容本身就是一種按照一定規則排列的目錄稱爲“聚集索引”。
    如果你認識某個字,您可以快速地從中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛纔的方法找到您要查的字,而需要去根據“偏旁部首”查到您要找的字,然後根據這個字後的頁碼直接翻到某頁來找到您要找的字。但您結合“部首目錄”和“檢字表”而查到的字的排序並不是真正的正文的排序方法,我們可以通過這種方式來找到您所需要的字,但它需要兩個過程,先找到目錄中的結果,然後再翻到您所需要的頁碼。我們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱爲“非聚集索引”。
通過以上例子,我們可以理解到什麼是“聚集索引”和“非聚集索引”。
我們可以很容易的理解:每個表只能有一個聚集索引,因爲目錄只能按照一種方法進行排序。


何時使用聚集索引或非聚集索引


下面的表總結了何時使用聚集索引或非聚集索引

動作 使用聚集索引 使用非聚集索引
只查詢某範圍內的數據 應該使用 不應該使用
只有一個或極少值不同 不應該使用 不應該使用
小量的不同值 應該使用 不應該使用
大量的不同值 不應該使用 應該使用
頻繁更新的列 不應該使用 應該使用
外鍵列 應該使用 應該使用
主鍵列 應該使用 應該使用
頻繁修改索引列 不應該使用 應該使用
 列經常被分組排序 應該使用 應該使用

       事實上,我們可以通過前面聚集索引和非聚集索引的定義的例子來理解上表。


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



理論的目的是應用。雖然我們剛纔列出了何時應使用聚集索引或非聚集索引,但在實踐中以上規則卻很容易被忽視或不能根據實際情況進行綜合分析。下面我們將根據在實踐中遇到的實際問題來談一下索引使用的誤區,以便於大家掌握索引建立的方法。
1、主鍵就是聚集索引
這種想法我認爲是極端錯誤的,是對聚集索引的一種浪費。雖然SQL SERVER默認是在主鍵上建立聚集索引的。
通常,我們會在每個表中都建立一個ID列,以區分每條數據,並且這個ID列是自動增大的,步長一般爲1。我們的這個辦公自動化的實例中的列Gid就是如此。此時,如果我們將這個列設爲主鍵,SQL SERVER會將此列默認爲聚集索引。這樣做有好處,就是可以讓您的數據在數據庫中按照ID進行物理排序,但筆者認爲這樣做意義不大。
顯而易見,聚集索引的優勢是很明顯的,而每個表中只能有一個聚集索引的規則,這使得聚集索引變得更加珍貴。
從我們前面談到的聚集索引的定義我們可以看出,使用聚集索引的最大好處就是能夠根據查詢要求,迅速縮小查詢範圍,避免全表掃描。在實際應用中,因爲ID號是自動生成的,我們並不知道每條記錄的ID號,所以我們很難在實踐中用ID號來進行查詢。這就使讓ID號這個主鍵作爲聚集索引成爲一種資源浪費。其次,讓每個ID號都不同的字段作爲聚集索引也不符合“大數目的不同值情況下不應建立聚合索引”規則;當然,這種情況只是針對用戶經常修改記錄內容,特別是索引項的時候會負作用,但對於查詢速度並沒有影響。
      在這裏之所以提到“理論上”三字,是因爲如果您的聚集索引還是盲目地建在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列上,就像以上的第1、2種情況,在網頁上的表現就是超時,根本就無法顯示。這也是我摒棄ID列作爲聚集索引的一個最重要的因素。
得出以上速度的方法是:在各個select語句前加:declare @d datetime
set @d=getdate()
並在select語句後加:
select [語句執行花費時間(毫秒)]=datediff(ms,@d,getdate())
2、只要建立索引就能顯著提高查詢速度
事實上,我們可以發現上面的例子中,第2、3條語句完全相同,且建立索引的字段也相同;不同的僅是前者在fariqi字段上建立的是非聚合索引,後者在此字段上建立的是聚合索引,但查詢速度卻有着天壤之別。所以,並非是在任何字段上簡單地建立索引就能提高查詢速度。
從建表的語句中,我們可以看到這個有着1000萬數據的表中fariqi字段有5003個不同記錄。在此字段上建立聚合索引是再合適不過了。在現實中,我們每天都會發幾個文件,這幾個文件的發文日期就相同,這完全符合建立聚集索引要求的:“既不能絕大多數都相同,又不能只有極少數相同”的規則。由此看來,我們建立“適當”的聚合索引對於我們提高查詢速度是非常重要的。
3、把所有需要提高查詢速度的字段都加進聚集索引,以提高查詢速度
上面已經談到:在進行數據查詢時都離不開字段的是“日期”還有用戶本身的“用戶名”。既然這兩個字段都是如此的重要,我們可以把他們合併起來,建立一個複合索引(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毫秒
從 以上試驗中,我們可以看到如果僅用聚集索引的起始列作爲查詢條件和同時用到複合聚集索引的全部列的查詢速度是幾乎一樣的,甚至比用上全部的複合索引列還要 略快(在查詢結果集數目一樣的情況下);而如果僅用複合聚集索引的非起始列作爲查詢條件的話,這個索引是不起任何作用的。當然,語句1、2的查詢速度一樣是因爲查詢的條目數一樣,如果複合索引的所有列都用上,而且查詢結果少的話,這樣就會形成“索引覆蓋”,因而性能可以達到最優。同時,請記住:無論您是否經常使用聚合索引的其他列,但其前導列一定要是使用最頻繁的列。

(五)其他注意事項
“水可載舟,亦可覆舟”,索引也一樣。索引有助於提高檢索性能,但過多或不當的索引也會導致系統低效。因爲用戶在表中每加進一個索引,數據庫就要做更多的工作。過多的索引甚至會導致索引碎片。
所以說,我們要建立一個“適當”的索引體系,特別是對聚合索引的創建,更應精益求精,以使您的數據庫能得到高性能的發揮。
當然,在實踐中,作爲一個盡職的數據庫管理員,您還要多測試一些方案,找出哪種方案效率最高、最爲有效。

海量的數據處理問題,這是一 項艱鉅而複雜的任務。原因有以下幾個方面:

  一、 數據量過大,數據中什麼情況都可能存在。如果說有10條數據,那麼大不了每條逐一檢查,人爲處理,如果有上百條數據,也可以考慮,如果數 據上到千萬級別,甚至過億,那不是手工能解決的了,必須通過工具或者程序進行處理,在海量的數據中,什麼情況都可能存在,例如,數據中某處格式出了問題。 尤其在程序處理時,前面還能正常處理,突然到了某個地方問題出現了,程序終止了。

  二、 軟硬件要求高,系統資源佔用率高。對海量的數據進行處 理,除了好的方法,最重要的就是合理的使用工具,合理分配系統資源。一般情況、如果處理的數據過TB級,小型機是要考慮的,普通的機子如果有好的方法可以考慮,不過也必須加大CPU的內存,就象面對着千軍萬馬,光有勇氣沒有一兵 一卒是很難取勝的。

  三、 要求很高的處理方法和技巧。這也是本文的寫作目的所在,好的處理方法是一位工程師長期工作經驗 的積累,也是個人的經驗的總結。沒有通用的處理方法,但有通用的原理和規則。

  那麼處理海量數據有哪些經驗和技巧?我把我所知道的羅列一下,一共大家參考:

  選擇優秀的數據庫 工具

  現 在的數據庫工具廠家比較多,對海量數據的處理對所使用的數據庫工具要求比較高,一般使用Oracle或者DB2,微軟的SQL Server2005性能也不錯。另外在BI領域:數據庫,數據倉庫,多維數據庫,數據挖掘等相關工具也要進行選擇,象好的ELT工具和好的OLAP工具都十分必要,例如Informatic,Eassbase等。筆者在實際數據分析項目中,對每天6000萬條的日誌數據進行處理,使用SQL Server 2000需要花費6小時,而使用SQL Server 2005則只需要花費3小時。

  編寫優良的程序代碼

  處理數據離不開優秀的程序代碼,尤其在進行復雜數據處理時,必須使用程序。好的程序代 碼對數據的處理至關重要,這不僅僅是數據處理準確度的問題,更是數據處理效率的問題。良好的程序代碼應該包含好的算法,處理流程、效率和異常處理機制等。

  對海量數據進行分去操作

  對海量數據進行分區操作十分必要,例如針對按年份存取的 數據,我們可以按年進行分區,不同的數據有不同的分區方式,不過處理機制大體相同。例如SQL Server的數據庫分區時將不同的數據存於不同的文件組下,而不同的文件組存於不同的磁盤分區下,這樣將數據分散開,減少磁盤I/O,減少系統負荷,而且還可以將日誌、索引等放於 不同的分區下。

  建立廣泛的索引

  對海量的數據處理,對大表建立索引是必行的。建立索引 要考慮到具體情況,例如針對大表的分組、排序等字段,都要建立相應索引,還可以建立複合索引,對經常插入的表則建立索引要小心,筆者在處理數據時曾經在一 個ETL流程中,當插入表時,首先刪除索 引,然後插入完畢,建立索引,並實施聚合操作,聚合完成後,再次插入前還是刪除索引,所以索引要用到好的時機,索引的填充因子和聚集,非聚集索引都要考 慮。

  建立緩存機制

  當數據量增加時,一般 的處理工具都要考慮到緩存問題,緩存大小設置的好差也關係到數據處理的成敗,例如,筆者在處理2億條數據聚合操作時,緩存設置爲100000條/Buffer,這對於這個級別的數據量是可行的。

  加大虛擬內存

  如果系統資源有限,內 存提示不足,則可以靠增加虛擬內存來解決。筆者在實際項目中曾經遇到針對18億條的數據進行處理,內存爲1GB1個P4 2.4G的CPU, 對這麼大的數據量進行聚合操作是有問題的,提示內存不足,筆者採用了加大虛擬內存的方式來解決,在6塊磁盤分區上分別建立了6個4096M的磁盤分區,用於虛擬內存,這樣虛擬內存則增加爲4096*6+1024=25600 M,解決了數據處理中的內存不足問題。

  分批處理

  海量數據處理難因數量 大,那麼解決海量數據處理難的問題其中一個技巧是減少數據量。可以對海量數據分批處理,然後處理後的數據再進行合併操作,這樣逐個擊破,有利於小數據量的 處理,不至於面對大數量帶來的問題,不過這種方法也要因時因勢進行,如果不允許需要拆分數據,還需要另想辦法。不過一般的數據按天、按月、按年等存儲的, 都可以採用先分後合的方法,對數據進行分開處理。

  使用臨時表和中間表

  數據量增加時,處理中要考慮提前彙總。這樣做的目的是化整爲零,大表變小表,分塊處理完成後, 再利用一定的規則進行合併,處理過程的臨時表的使用和中間結果的保存都非常重要,如果對於超海量的數據,大表處理不了,只能拆分爲多個小表。如果處理過程 中需要多步彙總操作,可按彙總步驟一步步來,不要一條語句完成一口氣吃掉一個胖子。

  優化查詢SQL語句

  對海量數據進行查詢處 理過程中,查詢的SQL語句的性能對查詢效 率的影響是非常大的,編寫高效優良的SQL腳本和存儲過程是數據庫工作人員的職責,也是檢驗數據庫工作人員水平的一個標準,在對SQL語句的編寫過程中例如減少關聯,少用或不用遊標, 設計好高效的數據庫表結構等都十分必要。筆者在工作中試着對1億行的數據使用遊標,進行3小時沒有出結果,這時一定要改用程序處理了

  石油文本格式進行處理

  對一般的數據處理可以使用數據庫,如果對複雜的數據處 理,必須藉助程序,那麼在程序操作數據庫和程序操作文本之間選擇,是一定要選擇程序操作文本的,原因爲:程序操作文本速度快;對文本進行處理不容易出錯; 文本的存儲不受限制等。例如一般的海量的網絡日誌都是文本格式或者csv格式(文本格式),對它進行處理牽扯到數據清洗,是要利用程序進行處理的,而不建議導入數據庫在做清洗。

  定製強大的清洗規 則和出錯處理機制

  海量數據中存在着不一致性,極有可能出現某處的瑕疵。 例如同樣的數據中的時間字段,有的可能爲非標準的時間,出現的原因肯能是應用程序的錯誤,系統的錯誤等,這是在進行數據處理時,必須制定強大的數據清洗規 則和出錯處理機構。

  建立視圖或者物化視圖

  視圖中的數據來源於基表,對海量數據的處理,可以將數據按一定的規劃分散到各個基表中,查詢或 處理過程中可以基於視圖進行,這樣分散了磁盤I/O,正如10根繩子吊着一根柱子和一根繩子吊着一根柱子的區別。

  避免使用32位機(極端情況)

  目前的計算機很多都是32位,那麼編寫的程序對內存的需要便受限制,而很多 的海量數據處理是必須大量消耗內存的,這便要求更好性能的機器,其中對位數的限制也十分重要。

  使用數據倉庫和多維數據庫存儲

  數據量加大是一定要考慮OLAP的,傳統的報表可能5、6個小時出來結果,而基於Cube的查詢可能只需要幾分鐘,因此處理海量數據的利器是OLAP多維分析,即建立數據倉庫,建立多維數據集, 基於多維數據集進行報表展現和數據挖掘等。

  使用採樣數據,進行數據挖掘

  基於海量數據的數據挖掘正在逐步興起,面對着超海量的 數據,一般色挖掘軟件或算法往往採用數據插樣的方式進行處理,這樣誤差不會很高,大大提高了處理效率和處理的成功率。一般採樣時要注意數據的完整性,防止 過大的偏差。筆者曾經對1億2千萬行的表數據進行採樣,抽取出400萬行,經測試軟件測試處理的誤差爲千萬之五,客 戶可以接受。

  還有一些方法,需要在 不同的情況和場合下運用,例如使用代理鍵等操作,這樣的好處是加快了聚合時間,因爲對數值型的聚合比對字符型的聚合快的多。類似的情況需要針對不同的需求 進行處理。

  海量數據室發展趨勢,對數據分析和挖掘也越來越重要,從 海量數據中提取有用信息重要而緊迫,這便要求處理要準確,精度要高,而且處理時間要短,得到有價值信息要快,所以,對海量數據的研究很有前途,也很值得進 行廣泛深入的研究

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章