<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    走在架構(gòu)師的大道上 Jack.Wang's home

    Java, C++, linux c, C#.net 技術(shù),軟件架構(gòu),領(lǐng)域建模,IT 項(xiàng)目管理 Dict.CN 在線詞典, 英語(yǔ)學(xué)習(xí), 在線翻譯

    BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
      195 Posts :: 3 Stories :: 728 Comments :: 0 Trackbacks

    1. 海量數(shù)據(jù)處理分析 (作者 北京邁思奇科技有限公司 戴子良) 原文地址:

    http://blog.csdn.net/DaiZiLiang/archive/2006/12/06/1432193.aspx

    筆者在實(shí)際工作中,有幸接觸到海量的數(shù)據(jù)處理問(wèn)題,對(duì)其進(jìn)行處理是一項(xiàng)艱巨而復(fù)雜的任務(wù)。原因有以

    下幾個(gè)方面:
    一、數(shù)據(jù)量過(guò)大,數(shù)據(jù)中什么情況都可能存在。如果說(shuō)有10條數(shù)據(jù),那么大不了每條去逐一檢查,人為處

    理,如果有上百條數(shù)據(jù),也可以考慮,如果數(shù)據(jù)上到千萬(wàn)級(jí)別,甚至過(guò)億,那不是手工能解決的了,必須

    通過(guò)工具或者程序進(jìn)行處理,尤其海量的數(shù)據(jù)中,什么情況都可能存在,例如,數(shù)據(jù)中某處格式出了問(wèn)題

    ,尤其在程序處理時(shí),前面還能正常處理,突然到了某個(gè)地方問(wèn)題出現(xiàn)了,程序終止了。
    二、軟硬件要求高,系統(tǒng)資源占用率高。對(duì)海量的數(shù)據(jù)進(jìn)行處理,除了好的方法,最重要的就是合理使用

    工具,合理分配系統(tǒng)資源。一般情況,如果處理的數(shù)據(jù)過(guò)TB級(jí),小型機(jī)是要考慮的,普通的機(jī)子如果有好

    的方法可以考慮,不過(guò)也必須加大CPU和內(nèi)存,就象面對(duì)著千軍萬(wàn)馬,光有勇氣沒(méi)有一兵一卒是很難取勝

    的。
    三、要求很高的處理方法和技巧。這也是本文的寫(xiě)作目的所在,好的處理方法是一位工程師長(zhǎng)期工作經(jīng)驗(yàn)

    的積累,也是個(gè)人的經(jīng)驗(yàn)的總結(jié)。沒(méi)有通用的處理方法,但有通用的原理和規(guī)則。
    那么處理海量數(shù)據(jù)有哪些經(jīng)驗(yàn)和技巧呢,我把我所知道的羅列一下,以供大家參考:
    一、選用優(yōu)秀的數(shù)據(jù)庫(kù)工具
    現(xiàn)在的數(shù)據(jù)庫(kù)工具廠家比較多,對(duì)海量數(shù)據(jù)的處理對(duì)所使用的數(shù)據(jù)庫(kù)工具要求比較高,一般使用Oracle或

    者DB2,微軟公司最近發(fā)布的SQL Server 2005性能也不錯(cuò)。另外在BI領(lǐng)域:數(shù)據(jù)庫(kù),數(shù)據(jù)倉(cāng)庫(kù),多維數(shù)據(jù)

    庫(kù),數(shù)據(jù)挖掘等相關(guān)工具也要進(jìn)行選擇,象好的ETL工具和好的OLAP工具都十分必要,例如Informatic,

    Eassbase等。筆者在實(shí)際數(shù)據(jù)分析項(xiàng)目中,對(duì)每天6000萬(wàn)條的日志數(shù)據(jù)進(jìn)行處理,使用SQL Server 2000

    需要花費(fèi)6小時(shí),而使用SQL Server 2005則只需要花費(fèi)3小時(shí)。
    二、編寫(xiě)優(yōu)良的程序代碼
    處理數(shù)據(jù)離不開(kāi)優(yōu)秀的程序代碼,尤其在進(jìn)行復(fù)雜數(shù)據(jù)處理時(shí),必須使用程序。好的程序代碼對(duì)數(shù)據(jù)的處

    理至關(guān)重要,這不僅僅是數(shù)據(jù)處理準(zhǔn)確度的問(wèn)題,更是數(shù)據(jù)處理效率的問(wèn)題。良好的程序代碼應(yīng)該包含好

    的算法,包含好的處理流程,包含好的效率,包含好的異常處理機(jī)制等。
    三、對(duì)海量數(shù)據(jù)進(jìn)行分區(qū)操作
    對(duì)海量數(shù)據(jù)進(jìn)行分區(qū)操作十分必要,例如針對(duì)按年份存取的數(shù)據(jù),我們可以按年進(jìn)行分區(qū),不同的數(shù)據(jù)庫(kù)

    有不同的分區(qū)方式,不過(guò)處理機(jī)制大體相同。例如SQL Server的數(shù)據(jù)庫(kù)分區(qū)是將不同的數(shù)據(jù)存于不同的文

    件組下,而不同的文件組存于不同的磁盤(pán)分區(qū)下,這樣將數(shù)據(jù)分散開(kāi),減小磁盤(pán)I/O,減小了系統(tǒng)負(fù)荷,

    而且還可以將日志,索引等放于不同的分區(qū)下。
    四、建立廣泛的索引
    對(duì)海量的數(shù)據(jù)處理,對(duì)大表建立索引是必行的,建立索引要考慮到具體情況,例如針對(duì)大表的分組、排序

    等字段,都要建立相應(yīng)索引,一般還可以建立復(fù)合索引,對(duì)經(jīng)常插入的表則建立索引時(shí)要小心,筆者在處

    理數(shù)據(jù)時(shí),曾經(jīng)在一個(gè)ETL流程中,當(dāng)插入表時(shí),首先刪除索引,然后插入完畢,建立索引,并實(shí)施聚合

    操作,聚合完成后,再次插入前還是刪除索引,所以索引要用到好的時(shí)機(jī),索引的填充因子和聚集、非聚

    集索引都要考慮。
    五、建立緩存機(jī)制
    當(dāng)數(shù)據(jù)量增加時(shí),一般的處理工具都要考慮到緩存問(wèn)題。緩存大小設(shè)置的好差也關(guān)系到數(shù)據(jù)處理的成敗,

    例如,筆者在處理2億條數(shù)據(jù)聚合操作時(shí),緩存設(shè)置為100000條/Buffer,這對(duì)于這個(gè)級(jí)別的數(shù)據(jù)量是可行

    的。
    六、加大虛擬內(nèi)存
    如果系統(tǒng)資源有限,內(nèi)存提示不足,則可以靠增加虛擬內(nèi)存來(lái)解決。筆者在實(shí)際項(xiàng)目中曾經(jīng)遇到針對(duì)18億

    條的數(shù)據(jù)進(jìn)行處理,內(nèi)存為1GB,1個(gè)P4 2.4G的CPU,對(duì)這么大的數(shù)據(jù)量進(jìn)行聚合操作是有問(wèn)題的,提示內(nèi)

    存不足,那么采用了加大虛擬內(nèi)存的方法來(lái)解決,在6塊磁盤(pán)分區(qū)上分別建立了6個(gè)4096M的磁盤(pán)分區(qū),用

    于虛擬內(nèi)存,這樣虛擬的內(nèi)存則增加為 4096*6 + 1024 = 25600 M,解決了數(shù)據(jù)處理中的內(nèi)存不足問(wèn)題。
    七、分批處理
    海量數(shù)據(jù)處理難因?yàn)閿?shù)據(jù)量大,那么解決海量數(shù)據(jù)處理難的問(wèn)題其中一個(gè)技巧是減少數(shù)據(jù)量。可以對(duì)海量

    數(shù)據(jù)分批處理,然后處理后的數(shù)據(jù)再進(jìn)行合并操作,這樣逐個(gè)擊破,有利于小數(shù)據(jù)量的處理,不至于面對(duì)

    大數(shù)據(jù)量帶來(lái)的問(wèn)題,不過(guò)這種方法也要因時(shí)因勢(shì)進(jìn)行,如果不允許拆分?jǐn)?shù)據(jù),還需要另想辦法。不過(guò)一

    般的數(shù)據(jù)按天、按月、按年等存儲(chǔ)的,都可以采用先分后合的方法,對(duì)數(shù)據(jù)進(jìn)行分開(kāi)處理。
    八、使用臨時(shí)表和中間表
    數(shù)據(jù)量增加時(shí),處理中要考慮提前匯總。這樣做的目的是化整為零,大表變小表,分塊處理完成后,再利

    用一定的規(guī)則進(jìn)行合并,處理過(guò)程中的臨時(shí)表的使用和中間結(jié)果的保存都非常重要,如果對(duì)于超海量的數(shù)

    據(jù),大表處理不了,只能拆分為多個(gè)小表。如果處理過(guò)程中需要多步匯總操作,可按匯總步驟一步步來(lái),

    不要一條語(yǔ)句完成,一口氣吃掉一個(gè)胖子。
    九、優(yōu)化查詢(xún)SQL語(yǔ)句
    在對(duì)海量數(shù)據(jù)進(jìn)行查詢(xún)處理過(guò)程中,查詢(xún)的SQL語(yǔ)句的性能對(duì)查詢(xún)效率的影響是非常大的,編寫(xiě)高效優(yōu)良

    的SQL腳本和存儲(chǔ)過(guò)程是數(shù)據(jù)庫(kù)工作人員的職責(zé),也是檢驗(yàn)數(shù)據(jù)庫(kù)工作人員水平的一個(gè)標(biāo)準(zhǔn),在對(duì)SQL語(yǔ)句

    的編寫(xiě)過(guò)程中,例如減少關(guān)聯(lián),少用或不用游標(biāo),設(shè)計(jì)好高效的數(shù)據(jù)庫(kù)表結(jié)構(gòu)等都十分必要。筆者在工作

    中試著對(duì)1億行的數(shù)據(jù)使用游標(biāo),運(yùn)行3個(gè)小時(shí)沒(méi)有出結(jié)果,這是一定要改用程序處理了。
    十、使用文本格式進(jìn)行處理
    對(duì)一般的數(shù)據(jù)處理可以使用數(shù)據(jù)庫(kù),如果對(duì)復(fù)雜的數(shù)據(jù)處理,必須借助程序,那么在程序操作數(shù)據(jù)庫(kù)和程

    序操作文本之間選擇,是一定要選擇程序操作文本的,原因?yàn)椋撼绦虿僮魑谋舅俣瓤欤粚?duì)文本進(jìn)行處理不

    容易出錯(cuò);文本的存儲(chǔ)不受限制等。例如一般的海量的網(wǎng)絡(luò)日志都是文本格式或者csv格式(文本格式)

    ,對(duì)它進(jìn)行處理牽扯到數(shù)據(jù)清洗,是要利用程序進(jìn)行處理的,而不建議導(dǎo)入數(shù)據(jù)庫(kù)再做清洗。
    十一、       定制強(qiáng)大的清洗規(guī)則和出錯(cuò)處理機(jī)制
    海量數(shù)據(jù)中存在著不一致性,極有可能出現(xiàn)某處的瑕疵。例如,同樣的數(shù)據(jù)中的時(shí)間字段,有的可能為非

    標(biāo)準(zhǔn)的時(shí)間,出現(xiàn)的原因可能為應(yīng)用程序的錯(cuò)誤,系統(tǒng)的錯(cuò)誤等,這是在進(jìn)行數(shù)據(jù)處理時(shí),必須制定強(qiáng)大

    的數(shù)據(jù)清洗規(guī)則和出錯(cuò)處理機(jī)制。
    十二、       建立視圖或者物化視圖
    視圖中的數(shù)據(jù)來(lái)源于基表,對(duì)海量數(shù)據(jù)的處理,可以將數(shù)據(jù)按一定的規(guī)則分散到各個(gè)基表中,查詢(xún)或處理

    過(guò)程中可以基于視圖進(jìn)行,這樣分散了磁盤(pán)I/O,正如10根繩子吊著一根柱子和一根吊著一根柱子的區(qū)別


    十三、       避免使用32位機(jī)子(極端情況)
    目前的計(jì)算機(jī)很多都是32位的,那么編寫(xiě)的程序?qū)?nèi)存的需要便受限制,而很多的海量數(shù)據(jù)處理是必須大

    量消耗內(nèi)存的,這便要求更好性能的機(jī)子,其中對(duì)位數(shù)的限制也十分重要。
    十四、       考慮操作系統(tǒng)問(wèn)題
    海量數(shù)據(jù)處理過(guò)程中,除了對(duì)數(shù)據(jù)庫(kù),處理程序等要求比較高以外,對(duì)操作系統(tǒng)的要求也放到了重要的位

    置,一般是必須使用服務(wù)器的,而且對(duì)系統(tǒng)的安全性和穩(wěn)定性等要求也比較高。尤其對(duì)操作系統(tǒng)自身的緩

    存機(jī)制,臨時(shí)空間的處理等問(wèn)題都需要綜合考慮。
    十五、       使用數(shù)據(jù)倉(cāng)庫(kù)和多維數(shù)據(jù)庫(kù)存儲(chǔ)
    數(shù)據(jù)量加大是一定要考慮OLAP的,傳統(tǒng)的報(bào)表可能5、6個(gè)小時(shí)出來(lái)結(jié)果,而基于Cube的查詢(xún)可能只需要幾

    分鐘,因此處理海量數(shù)據(jù)的利器是OLAP多維分析,即建立數(shù)據(jù)倉(cāng)庫(kù),建立多維數(shù)據(jù)集,基于多維數(shù)據(jù)集進(jìn)

    行報(bào)表展現(xiàn)和數(shù)據(jù)挖掘等。
    十六、       使用采樣數(shù)據(jù),進(jìn)行數(shù)據(jù)挖掘
    基于海量數(shù)據(jù)的數(shù)據(jù)挖掘正在逐步興起,面對(duì)著超海量的數(shù)據(jù),一般的挖掘軟件或算法往往采用數(shù)據(jù)抽樣

    的方式進(jìn)行處理,這樣的誤差不會(huì)很高,大大提高了處理效率和處理的成功率。一般采樣時(shí)要注意數(shù)據(jù)的

    完整性和,防止過(guò)大的偏差。筆者曾經(jīng)對(duì)1億2千萬(wàn)行的表數(shù)據(jù)進(jìn)行采樣,抽取出400萬(wàn)行,經(jīng)測(cè)試軟件測(cè)

    試處理的誤差為千分之五,客戶(hù)可以接受。
    還有一些方法,需要在不同的情況和場(chǎng)合下運(yùn)用,例如使用代理鍵等操作,這樣的好處是加快了聚合時(shí)間

    ,因?yàn)閷?duì)數(shù)值型的聚合比對(duì)字符型的聚合快得多。類(lèi)似的情況需要針對(duì)不同的需求進(jìn)行處理。
    海量數(shù)據(jù)是發(fā)展趨勢(shì),對(duì)數(shù)據(jù)分析和挖掘也越來(lái)越重要,從海量數(shù)據(jù)中提取有用信息重要而緊迫,這便要

    求處理要準(zhǔn)確,精度要高,而且處理時(shí)間要短,得到有價(jià)值信息要快,所以,對(duì)海量數(shù)據(jù)的研究很有前途

    ,也很值得進(jìn)行廣泛深入的研究。

    2. 海量數(shù)據(jù)的查詢(xún)優(yōu)化及分頁(yè)算法方案

    很多人不知道SQL語(yǔ)句在SQL SERVER中是如何執(zhí)行的,他們擔(dān)心自己所寫(xiě)的SQL語(yǔ)句會(huì)被SQL SERVER誤解。中國(guó)自學(xué)編程網(wǎng)提供 www.zxbc.cn 比如:
    select * from table1 where name=’zhangsan’ and tID > 10000
     和執(zhí)行:
    select * from table1 where tID > 10000 and name=’zhangsan’
      一些人不知道以上兩條語(yǔ)句的執(zhí)行效率是否一樣,因?yàn)槿绻?jiǎn)單的從語(yǔ)句先后上看,這兩個(gè)語(yǔ)句的確是不一樣,如果tID是一個(gè)聚合索引,那么后一句僅僅從表的10000條以后的記錄中查找就行了;而前一句則要先從全表中查找看有幾個(gè)name=’zhangsan’的,而后再根據(jù)限制條件條件tID>10000來(lái)提出查詢(xún)結(jié)果。
      事實(shí)上,這樣的擔(dān)心是不必要的。SQL SERVER中有一個(gè)“查詢(xún)分析優(yōu)化器”,它可以計(jì)算出where子句中的搜索條件并確定哪個(gè)索引能縮小表掃描的搜索空間,也就是說(shuō),它能實(shí)現(xiàn)自動(dòng)優(yōu)化。
      雖然查詢(xún)優(yōu)化器可以根據(jù)where子句自動(dòng)的進(jìn)行查詢(xún)優(yōu)化,但大家仍然有必要了解一下“查詢(xún)優(yōu)化器”的工作原理,如非這樣,有時(shí)查詢(xún)優(yōu)化器就會(huì)不按照您的本意進(jìn)行快速查詢(xún)。
      在查詢(xún)分析階段,查詢(xún)優(yōu)化器查看查詢(xún)的每個(gè)階段并決定限制需要掃描的數(shù)據(jù)量是否有用。如果一個(gè)階段可以被用作一個(gè)掃描參數(shù)(SARG),那么就稱(chēng)之為可優(yōu)化的,并且可以利用索引快速獲得所需數(shù)據(jù)。
      SARG的定義:用于限制搜索的一個(gè)操作,因?yàn)樗ǔJ侵敢粋€(gè)特定的匹配,一個(gè)值得范圍內(nèi)的匹配或者兩個(gè)以上條件的AND連接。形式如下:
    列名 操作符 <常數(shù) 或 變量>

    <常數(shù) 或 變量> 操作符列名
      列名可以出現(xiàn)在操作符的一邊,而常數(shù)或變量出現(xiàn)在操作符的另一邊。如:
    Name=’張三’
    價(jià)格>5000
    5000<價(jià)格
    Name=’張三’ and 價(jià)格>5000
      如果一個(gè)表達(dá)式不能滿(mǎn)足SARG的形式,那它就無(wú)法限制搜索的范圍了,也就是SQL SERVER必須對(duì)每一行都判斷它是否滿(mǎn)足WHERE子句中的所有條件。所以一個(gè)索引對(duì)于不滿(mǎn)足SARG形式的表達(dá)式來(lái)說(shuō)是無(wú)用的。
      介紹完SARG后,我們來(lái)總結(jié)一下使用SARG以及在實(shí)踐中遇到的和某些資料上結(jié)論不同的經(jīng)驗(yàn):

      1、Like語(yǔ)句是否屬于SARG取決于所使用的通配符的類(lèi)型

      如:name like ‘張%’ ,這就屬于SARG

      而:name like ‘%張’ ,就不屬于SARG。

      原因是通配符%在字符串的開(kāi)通使得索引無(wú)法使用。

      2、or 會(huì)引起全表掃描

    Name=’張三’ and 價(jià)格>5000 符號(hào)SARG,而:Name=’張三’ or 價(jià)格>5000 則不符合SARG。使用or會(huì)引起全表掃描。

      3、非操作符、函數(shù)引起的不滿(mǎn)足SARG形式的語(yǔ)句

      不滿(mǎn)足SARG形式的語(yǔ)句最典型的情況就是包括非操作符的語(yǔ)句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等,另外還有函數(shù)。下面就是幾個(gè)不滿(mǎn)足SARG形式的例子:

    ABS(價(jià)格)<5000

    Name like ‘%三’

      有些表達(dá)式,如:

    WHERE 價(jià)格*2>5000

      SQL SERVER也會(huì)認(rèn)為是SARG,SQL SERVER會(huì)將此式轉(zhuǎn)化為:

    WHERE 價(jià)格>2500/2 [Page]

      但我們不推薦這樣使用,因?yàn)橛袝r(shí)SQL SERVER不能保證這種轉(zhuǎn)化與原始表達(dá)式是完全等價(jià)的。

      4、IN 的作用相當(dāng)與OR

      語(yǔ)句:

    Select * from table1 where tid in (2,3)

      和

    Select * from table1 where tid=2 or tid=3

      是一樣的,都會(huì)引起全表掃描,如果tid上有索引,其索引也會(huì)失效。

      5、盡量少用NOT

      6、exists 和 in 的執(zhí)行效率是一樣的

      很多資料上都顯示說(shuō),exists要比in的執(zhí)行效率要高,同時(shí)應(yīng)盡可能的用not exists來(lái)代替not in。但事實(shí)上,我試驗(yàn)了一下,發(fā)現(xiàn)二者無(wú)論是前面帶不帶not,二者之間的執(zhí)行效率都是一樣的。因?yàn)樯婕白硬樵?xún),我們?cè)囼?yàn)這次用SQL SERVER自帶的pubs數(shù)據(jù)庫(kù)。運(yùn)行前我們可以把SQL SERVER的statistics I/O狀態(tài)打開(kāi)。

      (1)select title,price from titles where title_id in (select title_id from sales where qty>30)

      該句的執(zhí)行結(jié)果為:

      表 ’sales’。掃描計(jì)數(shù) 18,邏輯讀 56 次,物理讀 0 次,預(yù)讀 0 次。

      表 ’titles’。掃描計(jì)數(shù) 1,邏輯讀 2 次,物理讀 0 次,預(yù)讀 0 次。

      (2)select title,price from titles where exists (select * from sales where sales.title_id=titles.title_id and qty>30)

      第二句的執(zhí)行結(jié)果為:

      表 ’sales’。掃描計(jì)數(shù) 18,邏輯讀 56 次,物理讀 0 次,預(yù)讀 0 次。

      表 ’titles’。掃描計(jì)數(shù) 1,邏輯讀 2 次,物理讀 0 次,預(yù)讀 0 次。

      我們從此可以看到用exists和用in的執(zhí)行效率是一樣的。

      7、用函數(shù)charindex()和前面加通配符%的LIKE執(zhí)行效率一樣

      前面,我們談到,如果在LIKE前面加上通配符%,那么將會(huì)引起全表掃描,所以其執(zhí)行效率是低下的。但有的資料介紹說(shuō),用函數(shù)charindex()來(lái)代替LIKE速度會(huì)有大的提升,經(jīng)我試驗(yàn),發(fā)現(xiàn)這種說(shuō)明也是錯(cuò)誤的:

    select gid,title,fariqi,reader from tgongwen where charindex(’刑偵支隊(duì)’,reader)>0 and fariqi>’2004-5-5’

      用時(shí):7秒,另外:掃描計(jì)數(shù) 4,邏輯讀 7155 次,物理讀 0 次,預(yù)讀 0 次

    select gid,title,fariqi,reader from tgongwen where reader like ’%’ + ’刑偵支隊(duì)’ + ’%’ and fariqi>’2004-5-5’

      用時(shí):7秒,另外:掃描計(jì)數(shù) 4,邏輯讀 7155 次,物理讀 0 次,預(yù)讀 0 次。

      8、union并不絕對(duì)比or的執(zhí)行效率高

      我們前面已經(jīng)談到了在where子句中使用or會(huì)引起全表掃描,一般的,我所見(jiàn)過(guò)的資料都是推薦這里用union來(lái)代替or。事實(shí)證明,這種說(shuō)法對(duì)于大部分都是適用的。 [Page]

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-9-16’ or gid>9990000

      用時(shí):68秒。掃描計(jì)數(shù) 1,邏輯讀 404008 次,物理讀 283 次,預(yù)讀 392163 次。

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-9-16’ 

    union

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid>9990000

      用時(shí):9秒。掃描計(jì)數(shù) 8,邏輯讀 67489 次,物理讀 216 次,預(yù)讀 7499 次。

      看來(lái),用union在通常情況下比用or的效率要高的多。

      但經(jīng)過(guò)試驗(yàn),筆者發(fā)現(xiàn)如果or兩邊的查詢(xún)列是一樣的話,那么用union則反倒和用or的執(zhí)行速度差很多,雖然這里union掃描的是索引,而or掃描的是全表。

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-9-16’ or fariqi=’2004-2-5’

      用時(shí):6423毫秒。掃描計(jì)數(shù) 2,邏輯讀 14726 次,物理讀 1 次,預(yù)讀 7176 次。

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-9-16’ 

    union

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where    fariqi=’2004-2-5’

      用時(shí):11640毫秒。掃描計(jì)數(shù) 8,邏輯讀 14806 次,物理讀 108 次,預(yù)讀 1144 次。

      9、字段提取要按照“需多少、提多少”的原則,避免“select *”

      我們來(lái)做一個(gè)試驗(yàn):

    select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

      用時(shí):4673毫秒

    select top 10000 gid,fariqi,title from tgongwen order by gid desc

      用時(shí):1376毫秒

    select top 10000 gid,fariqi from tgongwen order by gid desc

      用時(shí):80毫秒

      由此看來(lái),我們每少提取一個(gè)字段,數(shù)據(jù)的提取速度就會(huì)有相應(yīng)的提升。提升的速度還要看您舍棄的字段的大小來(lái)判斷。

      10、count(*)不比count(字段)慢

      某些資料上說(shuō):用*會(huì)統(tǒng)計(jì)所有列,顯然要比一個(gè)世界的列名效率低。這種說(shuō)法其實(shí)是沒(méi)有根據(jù)的。我們來(lái)看:

    select count(*) from Tgongwen

      用時(shí):1500毫秒

    select count(gid) from Tgongwen 

      用時(shí):1483毫秒

    select count(fariqi) from Tgongwen

      用時(shí):3140毫秒

    select count(title) from Tgongwen

      用時(shí):52050毫秒

      從以上可以看出,如果用count(*)和用count(主鍵)的速度是相當(dāng)?shù)?而count(*)卻比其他任何除主鍵以外的字段匯總速度要快,而且字段越長(zhǎng),匯總的速度就越慢。我想,如果用count(*), SQL SERVER可能會(huì)自動(dòng)查找最小字段來(lái)匯總的。當(dāng)然,如果您直接寫(xiě)count(主鍵)將會(huì)來(lái)的更直接些。 [Page]

      11、order by按聚集索引列排序效率最高

      我們來(lái)看:(gid是主鍵,fariqi是聚合索引列)

    select top 10000 gid,fariqi,reader,title from tgongwen

      用時(shí):196 毫秒。 掃描計(jì)數(shù) 1,邏輯讀 289 次,物理讀 1 次,預(yù)讀 1527 次。

    select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc

      用時(shí):4720毫秒。 掃描計(jì)數(shù) 1,邏輯讀 41956 次,物理讀 0 次,預(yù)讀 1287 次。

    select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

      用時(shí):4736毫秒。 掃描計(jì)數(shù) 1,邏輯讀 55350 次,物理讀 10 次,預(yù)讀 775 次。

    select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc

      用時(shí):173毫秒。 掃描計(jì)數(shù) 1,邏輯讀 290 次,物理讀 0 次,預(yù)讀 0 次。

    select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc

      用時(shí):156毫秒。 掃描計(jì)數(shù) 1,邏輯讀 289 次,物理讀 0 次,預(yù)讀 0 次。

      從以上我們可以看出,不排序的速度以及邏輯讀次數(shù)都是和“order by 聚集索引列” 的速度是相當(dāng)?shù)?但這些都比“order by 非聚集索引列”的查詢(xún)速度是快得多的。

      同時(shí),按照某個(gè)字段進(jìn)行排序的時(shí)候,無(wú)論是正序還是倒序,速度是基本相當(dāng)?shù)摹?

      12、高效的TOP

      事實(shí)上,在查詢(xún)和提取超大容量的數(shù)據(jù)集時(shí),影響數(shù)據(jù)庫(kù)響應(yīng)時(shí)間的最大因素不是數(shù)據(jù)查找,而是物理的I/0操作。如:

    select top 10 * from ( )
    AS

    DECLARE @Str nVARCHAR(4000)

    SET @Str=’SELECT     TOP ’+CAST(@RecsPerPage AS VARCHAR(20))+’ * FROM (’+@SQL+’) T WHERE T.’+@ID+’NOT IN 
    (SELECT     TOP ’+CAST((@RecsPerPage*(@Page-1)) AS VARCHAR(20))+’ ’+@ID+’ FROM (’+@SQL+’) T9 ORDER BY ’+@Sort+’) ORDER BY ’+@Sort

    PRINT @Str

    EXEC sp_ExecuteSql @Str
    GO

      其實(shí),以上語(yǔ)句可以簡(jiǎn)化為:

    SELECT TOP 頁(yè)大小 *

    FROM Table1

    WHERE (ID NOT IN

                (SELECT TOP 頁(yè)大小*頁(yè)數(shù) id

               FROM 表

               ORDER BY id))

    ORDER BY ID

      但這個(gè)存儲(chǔ)過(guò)程有一個(gè)致命的缺點(diǎn),就是它含有NOT IN字樣。雖然我可以把它改造為:

    SELECT TOP 頁(yè)大小 *

    FROM Table1

    WHERE not exists

    (select * from (select top (頁(yè)大小*頁(yè)數(shù)) * from table1 order by id) b where b.id=a.id )

    order by id [Page]

      即,用not exists來(lái)代替not in,但我們前面已經(jīng)談過(guò)了,二者的執(zhí)行效率實(shí)際上是沒(méi)有區(qū)別的。

      既便如此,用TOP 結(jié)合NOT IN的這個(gè)方法還是比用游標(biāo)要來(lái)得快一些。

      雖然用not exists并不能挽救上個(gè)存儲(chǔ)過(guò)程的效率,但使用SQL SERVER中的TOP關(guān)鍵字卻是一個(gè)非常明智的選擇。因?yàn)榉猪?yè)優(yōu)化的最終目的就是避免產(chǎn)生過(guò)大的記錄集,而我們?cè)谇懊嬉惨呀?jīng)提到了TOP的優(yōu)勢(shì),通過(guò)TOP 即可實(shí)現(xiàn)對(duì)數(shù)據(jù)量的控制。

      在分頁(yè)算法中,影響我們查詢(xún)速度的關(guān)鍵因素有兩點(diǎn):TOP和NOT IN。TOP可以提高我們的查詢(xún)速度,而NOT IN會(huì)減慢我們的查詢(xún)速度,所以要提高我們整個(gè)分頁(yè)算法的速度,就要徹底改造NOT IN,同其他方法來(lái)替代它。

      我們知道,幾乎任何字段,我們都可以通過(guò)max(字段)或min(字段)來(lái)提取某個(gè)字段中的最大或最小值,所以如果這個(gè)字段不重復(fù),那么就可以利用這些不重復(fù)的字段的max或min作為分水嶺,使其成為分頁(yè)算法中分開(kāi)每頁(yè)的參照物。在這里,我們可以用操作符“>”或“<”號(hào)來(lái)完成這個(gè)使命,使查詢(xún)語(yǔ)句符合SARG形式。如:

    Select top 10 * from table1 where id>200

      于是就有了如下分頁(yè)方案:

    select top 頁(yè)大小 *

    from table1 

    where id>

            (select max (id) from 

            (select top ((頁(yè)碼-1)*頁(yè)大小) id from table1 order by id) as T

             )     

        order by id

      在選擇即不重復(fù)值,又容易分辨大小的列時(shí),我們通常會(huì)選擇主鍵。下表列出了筆者用有著1000萬(wàn)數(shù)據(jù)的辦公自動(dòng)化系統(tǒng)中的表,在以GID(GID是主鍵,但并不是聚集索引。)為排序列、提取gid,fariqi,title字段,分別以第1、10、100、500、1000、1萬(wàn)、10萬(wàn)、25萬(wàn)、50萬(wàn)頁(yè)為例,測(cè)試以上三種分頁(yè)方案的執(zhí)行速度:(單位:毫秒)

    頁(yè)    碼
    方案1
    方案2
    方案3

    1
    60
    30
    76

    10
    46
    16
    63

    100
    1076
    720
    130

    500
    540
    12943
    83

    1000
    17110
    470
    250

    1萬(wàn)
    24796
    4500
    140

    10萬(wàn)
    38326
    42283
    1553

    25萬(wàn)
    28140
    128720
    2330

    50萬(wàn)
    121686
    127846
    7168


      從上表中,我們可以看出,三種存儲(chǔ)過(guò)程在執(zhí)行100頁(yè)以下的分頁(yè)命令時(shí),都是可以信任的,速度都很好。但第一種方案在執(zhí)行分頁(yè)1000頁(yè)以上后,速度就降了下來(lái)。第二種方案大約是在執(zhí)行分頁(yè)1萬(wàn)頁(yè)以上后速度開(kāi)始降了下來(lái)。而第三種方案卻始終沒(méi)有大的降勢(shì),后勁仍然很足。

      在確定了第三種分頁(yè)方案后,我們可以據(jù)此寫(xiě)一個(gè)存儲(chǔ)過(guò)程。大家知道SQL SERVER的存儲(chǔ)過(guò)程是事先編譯好的SQL語(yǔ)句,它的執(zhí)行效率要比通過(guò)WEB頁(yè)面?zhèn)鱽?lái)的SQL語(yǔ)句的執(zhí)行效率要高。下面的存儲(chǔ)過(guò)程不僅含有分頁(yè)方案,還會(huì)根據(jù)頁(yè)面?zhèn)鱽?lái)的參數(shù)來(lái)確定是否進(jìn)行數(shù)據(jù)總數(shù)統(tǒng)計(jì)。

    -- 獲取指定頁(yè)的數(shù)據(jù)

    CREATE PROCEDURE pagination3 [Page]

    @tblName     varchar(255),         -- 表名

    @strGetFields varchar(1000) = ’*’,    -- 需要返回的列 

    @fldName varchar(255)=’’,        -- 排序的字段名

    @PageSize     int = 10,            -- 頁(yè)尺寸

    @PageIndex    int = 1,             -- 頁(yè)碼

    @doCount    bit = 0,     -- 返回記錄總數(shù), 非 0 值則返回

    select top 10000 gid,fariqi,title from tgongwen

    where neibuyonghu=’辦公室’

    order by gid desc) as a

    order by gid asc

      這條語(yǔ)句,從理論上講,整條語(yǔ)句的執(zhí)行時(shí)間應(yīng)該比子句的執(zhí)行時(shí)間長(zhǎng),但事實(shí)相反。因?yàn)?子句執(zhí)行后返回的是10000條記錄,而整條語(yǔ)句僅返回10條語(yǔ)句,所以影響數(shù)據(jù)庫(kù)響應(yīng)時(shí)間最大的因素是物理I/O操作。而限制物理I/O操作此處的最有效方法之一就是使用TOP關(guān)鍵詞了。TOP關(guān)鍵詞是SQL SERVER中經(jīng)過(guò)系統(tǒng)優(yōu)化過(guò)的一個(gè)用來(lái)提取前幾條或前幾個(gè)百分比數(shù)據(jù)的詞。經(jīng)筆者在實(shí)踐中的應(yīng)用,發(fā)現(xiàn)TOP確實(shí)很好用,效率也很高。但這個(gè)詞在另外一個(gè)大型數(shù)據(jù)庫(kù)ORACLE中卻沒(méi)有,這不能說(shuō)不是一個(gè)遺憾,雖然在ORACLE中可以用其他方法(如:rownumber)來(lái)解決。在以后的關(guān)于“實(shí)現(xiàn)千萬(wàn)級(jí)數(shù)據(jù)的分頁(yè)顯示存儲(chǔ)過(guò)程”的討論中,我們就將用到TOP這個(gè)關(guān)鍵詞。

      到此為止,我們上面討論了如何實(shí)現(xiàn)從大容量的數(shù)據(jù)庫(kù)中快速地查詢(xún)出您所需要的數(shù)據(jù)方法。當(dāng)然,我們介紹的這些方法都是“軟”方法,在實(shí)踐中,我們還要考慮各種“硬”因素,如:網(wǎng)絡(luò)性能、服務(wù)器的性能、操作系統(tǒng)的性能,甚至網(wǎng)卡、交換機(jī)等。 [Page]

     三、實(shí)現(xiàn)小數(shù)據(jù)量和海量數(shù)據(jù)的通用分頁(yè)顯示存儲(chǔ)過(guò)程

      建立一個(gè)web 應(yīng)用,分頁(yè)瀏覽功能必不可少。這個(gè)問(wèn)題是數(shù)據(jù)庫(kù)處理中十分常見(jiàn)的問(wèn)題。經(jīng)典的數(shù)據(jù)分頁(yè)方法是:ADO 紀(jì)錄集分頁(yè)法,也就是利用ADO自帶的分頁(yè)功能(利用游標(biāo))來(lái)實(shí)現(xiàn)分頁(yè)。但這種分頁(yè)方法僅適用于較小數(shù)據(jù)量的情形,因?yàn)橛螛?biāo)本身有缺點(diǎn):游標(biāo)是存放在內(nèi)存中,很費(fèi)內(nèi)存。游標(biāo)一建立,就將相關(guān)的記錄鎖住,直到取消游標(biāo)。游標(biāo)提供了對(duì)特定集合中逐行掃描的手段,一般使用游標(biāo)來(lái)逐行遍歷數(shù)據(jù),根據(jù)取出數(shù)據(jù)條件的不同進(jìn)行不同的操作。而對(duì)于多表和大表中定義的游標(biāo)(大的數(shù)據(jù)集合)循環(huán)很容易使程序進(jìn)入一個(gè)漫長(zhǎng)的等待甚至死機(jī)。

      更重要的是,對(duì)于非常大的數(shù)據(jù)模型而言,分頁(yè)檢索時(shí),如果按照傳統(tǒng)的每次都加載整個(gè)數(shù)據(jù)源的方法是非常浪費(fèi)資源的。現(xiàn)在流行的分頁(yè)方法一般是檢索頁(yè)面大小的塊區(qū)的數(shù)據(jù),而非檢索所有的數(shù)據(jù),然后單步執(zhí)行當(dāng)前行。

      最早較好地實(shí)現(xiàn)這種根據(jù)頁(yè)面大小和頁(yè)碼來(lái)提取數(shù)據(jù)的方法大概就是“俄羅斯存儲(chǔ)過(guò)程”。這個(gè)存儲(chǔ)過(guò)程用了游標(biāo),由于游標(biāo)的局限性,所以這個(gè)方法并沒(méi)有得到大家的普遍認(rèn)可。

      后來(lái),網(wǎng)上有人改造了此存儲(chǔ)過(guò)程,下面的存儲(chǔ)過(guò)程就是結(jié)合我們的辦公自動(dòng)化實(shí)例寫(xiě)的分頁(yè)存儲(chǔ)過(guò)程:

    CREATE procedure pagination1

    (@pagesize int,    --頁(yè)面大小,如每頁(yè)存儲(chǔ)20條記錄

    @pageindex int     --當(dāng)前頁(yè)碼

    )

    as

    set nocount on

    begin

    declare @indextable table(id int identity(1,1),nid int)    --定義表變量

    declare @PageLowerBound int    --定義此頁(yè)的底碼

    declare @PageUpperBound int    --定義此頁(yè)的頂碼

    set @PageLowerBound=(@pageindex-1)*@pagesize

    set @PageUpperBound=@PageLowerBound+@pagesize

    set rowcount @PageUpperBound

    insert into @indextable(nid) select gid from TGongwen where fariqi >dateadd(day,-365,getdate()) order by fariqi desc

    select O.gid,O.mid,O.title,O.fadanwei,O.fariqi from TGongwen O,@indextable t where O.gid=t.nid

    and t.id>@PageLowerBound and t.id<=@PageUpperBound order by t.id

    end

    set nocount off

      以上存儲(chǔ)過(guò)程運(yùn)用了SQL SERVER的最新技術(shù)――表變量。應(yīng)該說(shuō)這個(gè)存儲(chǔ)過(guò)程也是一個(gè)非常優(yōu)秀的分頁(yè)存儲(chǔ)過(guò)程。當(dāng)然,在這個(gè)過(guò)程中,您也可以把其中的表變量寫(xiě)成臨時(shí)表:CREATE TABLE #Temp。但很明顯,在SQL SERVER中,用臨時(shí)表是沒(méi)有用表變量快的。所以筆者剛開(kāi)始使用這個(gè)存儲(chǔ)過(guò)程時(shí),感覺(jué)非常的不錯(cuò),速度也比原來(lái)的ADO的好。但后來(lái),我又發(fā)現(xiàn)了比此方法更好的方法。

      筆者曾在網(wǎng)上看到了一篇小短文《從數(shù)據(jù)表中取出第n條到第m條的記錄的方法》,全文如下: [Page]

    從publish 表中取出第 n 條到第 m 條的記錄: 
    SELECT TOP m-n+1 * 
    FROM publish 
    WHERE (id NOT IN 
        (SELECT TOP n-1 id 
         FROM publish)) 

    id 為publish 表的關(guān)鍵字 

      我當(dāng)時(shí)看到這篇文章的時(shí)候,真的是精神為之一振,覺(jué)得思路非常得好。等到后來(lái),我在作辦公自動(dòng)化系統(tǒng)(ASP.NET+ C#+SQL SERVER)的時(shí)候,忽然想起了這篇文章,我想如果把這個(gè)語(yǔ)句改造一下,這就可能是一個(gè)非常好的分頁(yè)存儲(chǔ)過(guò)程。于是我就滿(mǎn)網(wǎng)上找這篇文章,沒(méi)想到,文章還沒(méi)找到,卻找到了一篇根據(jù)此語(yǔ)句寫(xiě)的一個(gè)分頁(yè)存儲(chǔ)過(guò)程,這個(gè)存儲(chǔ)過(guò)程也是目前較為流行的一種分頁(yè)存儲(chǔ)過(guò)程,我很后悔沒(méi)有爭(zhēng)先把這段文字改造成存儲(chǔ)過(guò)程:

    CREATE PROCEDURE pagination2
    (
    @SQL nVARCHAR(4000),      --不帶排序語(yǔ)句的SQL語(yǔ)句
    @Page int,                --頁(yè)碼
    @RecsPerPage int,         --每頁(yè)容納的記錄數(shù)
    @ID VARCHAR(255),         --需要排序的不重復(fù)的ID號(hào)
    @Sort VARCHAR(255)        --排序字段及規(guī)則
    @OrderType bit = 0,    -- 設(shè)置排序類(lèi)型, 非 0 值則降序

    @strWhere    varchar(1500) = ’’    -- 查詢(xún)條件 (注意: 不要加 where)

    AS

    declare @strSQL     varchar(5000)         -- 主語(yǔ)句

    declare @strTmp     varchar(110)          -- 臨時(shí)變量

    declare @strOrder varchar(400)          -- 排序類(lèi)型



    if @doCount != 0

        begin

          if @strWhere !=’’

          set @strSQL = \"select count(*) as Total from [\" + @tblName + \"] where \"+@strWhere

          else

          set @strSQL = \"select count(*) as Total from [\" + @tblName + \"]\"

    end  

    --以上代碼的意思是如果@doCount傳遞過(guò)來(lái)的不是0,就執(zhí)行總數(shù)統(tǒng)計(jì)。以下的所有代碼都是@doCount為0的情況

    else

    begin



    if @OrderType != 0

    begin

          set @strTmp = \"<(select min\"

    set @strOrder = \" order by [\" + @fldName +\"] desc\"

    --如果@OrderType不是0,就執(zhí)行降序,這句很重要!

    end

    else

    begin

          set @strTmp = \">(select max\"

          set @strOrder = \" order by [\" + @fldName +\"] asc\" [Page]

    end



    if @PageIndex = 1

    begin

          if @strWhere != ’’   

          set @strSQL = \"select top \" + str(@PageSize) +\" \"+@strGetFields+ \"    from [\" + @tblName + \"] where \" + @strWhere + \" \" + @strOrder

           else

           set @strSQL = \"select top \" + str(@PageSize) +\" \"+@strGetFields+ \"    from [\"+ @tblName + \"] \"+ @strOrder

    --如果是第一頁(yè)就執(zhí)行以上代碼,這樣會(huì)加快執(zhí)行速度

    end

    else

    begin

    --以下代碼賦予了@strSQL以真正執(zhí)行的SQL代碼

    set @strSQL = \"select top \" + str(@PageSize) +\" \"+@strGetFields+ \"    from [\"

          + @tblName + \"] where [\" + @fldName + \"]\" + @strTmp + \"([\"+ @fldName + \"]) from (select top \" + str((@PageIndex-1)*@PageSize) + \" [\"+ @fldName + \"] from [\" + @tblName + \"]\" + @strOrder + \") as tblTmp)\"+ @strOrder



    if @strWhere != ’’

          set @strSQL = \"select top \" + str(@PageSize) +\" \"+@strGetFields+ \"    from [\"

              + @tblName + \"] where [\" + @fldName + \"]\" + @strTmp + \"([\"

              + @fldName + \"]) from (select top \" + str((@PageIndex-1)*@PageSize) + \" [\"

              + @fldName + \"] from [\" + @tblName + \"] where \" + @strWhere + \" \"

              + @strOrder + \") as tblTmp) and \" + @strWhere + \" \" + @strOrder

    end 

    end   

    exec (@strSQL) [Page]

    GO

      上面的這個(gè)存儲(chǔ)過(guò)程是一個(gè)通用的存儲(chǔ)過(guò)程,其注釋已寫(xiě)在其中了。

      在大數(shù)據(jù)量的情況下,特別是在查詢(xún)最后幾頁(yè)的時(shí)候,查詢(xún)時(shí)間一般不會(huì)超過(guò)9秒;而用其他存儲(chǔ)過(guò)程,在實(shí)踐中就會(huì)導(dǎo)致超時(shí),所以這個(gè)存儲(chǔ)過(guò)程非常適用于大容量數(shù)據(jù)庫(kù)的查詢(xún)。

      筆者希望能夠通過(guò)對(duì)以上存儲(chǔ)過(guò)程的解析,能給大家?guī)?lái)一定的啟示,并給工作帶來(lái)一定的效率提升,同時(shí)希望同行提出更優(yōu)秀的實(shí)時(shí)數(shù)據(jù)分頁(yè)算法。
    四、聚集索引的重要性和如何選擇聚集索引

      在上一節(jié)的標(biāo)題中,筆者寫(xiě)的是:實(shí)現(xiàn)小數(shù)據(jù)量和海量數(shù)據(jù)的通用分頁(yè)顯示存儲(chǔ)過(guò)程。這是因?yàn)樵趯⒈敬鎯?chǔ)過(guò)程應(yīng)用于“辦公自動(dòng)化”系統(tǒng)的實(shí)踐中時(shí),筆者發(fā)現(xiàn)這第三種存儲(chǔ)過(guò)程在小數(shù)據(jù)量的情況下,有如下現(xiàn)象:

      1、分頁(yè)速度一般維持在1秒和3秒之間。

      2、在查詢(xún)最后一頁(yè)時(shí),速度一般為5秒至8秒,哪怕分頁(yè)總數(shù)只有3頁(yè)或30萬(wàn)頁(yè)。

      雖然在超大容量情況下,這個(gè)分頁(yè)的實(shí)現(xiàn)過(guò)程是很快的,但在分前幾頁(yè)時(shí),這個(gè)1-3秒的速度比起第一種甚至沒(méi)有經(jīng)過(guò)優(yōu)化的分頁(yè)方法速度還要慢,借用戶(hù)的話說(shuō)就是“還沒(méi)有ACCESS數(shù)據(jù)庫(kù)速度快”,這個(gè)認(rèn)識(shí)足以導(dǎo)致用戶(hù)放棄使用您開(kāi)發(fā)的系統(tǒng)。

      筆者就此分析了一下,原來(lái)產(chǎn)生這種現(xiàn)象的癥結(jié)是如此的簡(jiǎn)單,但又如此的重要:排序的字段不是聚集索引!

      本篇文章的題目是:“查詢(xún)優(yōu)化及分頁(yè)算法方案”。筆者只所以把“查詢(xún)優(yōu)化”和“分頁(yè)算法”這兩個(gè)聯(lián)系不是很大的論題放在一起,就是因?yàn)槎叨夹枰粋€(gè)非常重要的東西――聚集索引。

      在前面的討論中我們已經(jīng)提到了,聚集索引有兩個(gè)最大的優(yōu)勢(shì):

      1、以最快的速度縮小查詢(xún)范圍。

      2、以最快的速度進(jìn)行字段排序。

      第1條多用在查詢(xún)優(yōu)化時(shí),而第2條多用在進(jìn)行分頁(yè)時(shí)的數(shù)據(jù)排序。

      而聚集索引在每個(gè)表內(nèi)又只能建立一個(gè),這使得聚集索引顯得更加的重要。聚集索引的挑選可以說(shuō)是實(shí)現(xiàn)“查詢(xún)優(yōu)化”和“高效分頁(yè)”的最關(guān)鍵因素。

      但要既使聚集索引列既符合查詢(xún)列的需要,又符合排序列的需要,這通常是一個(gè)矛盾。

      筆者前面“索引”的討論中,將fariqi,即用戶(hù)發(fā)文日期作為了聚集索引的起始列,日期的精確度為“日”。這種作法的優(yōu)點(diǎn),前面已經(jīng)提到了,在進(jìn)行劃時(shí)間段的快速查詢(xún)中,比用ID主鍵列有很大的優(yōu)勢(shì)。

      但在分頁(yè)時(shí),由于這個(gè)聚集索引列存在著重復(fù)記錄,所以無(wú)法使用max或min來(lái)最為分頁(yè)的參照物,進(jìn)而無(wú)法實(shí)現(xiàn)更為高效的排序。而如果將ID主鍵列作為聚集索引,那么聚集索引除了用以排序之外,沒(méi)有任何用處,實(shí)際上是浪費(fèi)了聚集索引這個(gè)寶貴的資源。

       為解決這個(gè)矛盾,筆者后來(lái)又添加了一個(gè)日期列,其默認(rèn)值為getdate()。用戶(hù)在寫(xiě)入記錄時(shí),這個(gè)列自動(dòng)寫(xiě)入當(dāng)時(shí)的時(shí)間,時(shí)間精確到毫秒。即使這樣,為了避免可能性很小的重合,還要在此列上創(chuàng)建UNIQUE約束。將此日期列作為聚集索引列。

      有了這個(gè)時(shí)間型聚集索引列之后,用戶(hù)就既可以用這個(gè)列查找用戶(hù)在插入數(shù)據(jù)時(shí)的某個(gè)時(shí)間段的查詢(xún),又可以作為唯一列來(lái)實(shí)現(xiàn)max或min,成為分頁(yè)算法的參照物。

      經(jīng)過(guò)這樣的優(yōu)化,筆者發(fā)現(xiàn),無(wú)論是大數(shù)據(jù)量的情況下還是小數(shù)據(jù)量的情況下,分頁(yè)速度一般都是幾十毫秒,甚至0毫秒。而用日期段縮小范圍的查詢(xún)速度比原來(lái)也沒(méi)有任何遲鈍。 [Page]

      聚集索引是如此的重要和珍貴,所以筆者總結(jié)了一下,一定要將聚集索引建立在:

      1、您最頻繁使用的、用以縮小查詢(xún)范圍的字段上;

      2、您最頻繁使用的、需要排序的字段上。

      結(jié)束語(yǔ):

      本篇文章匯集了筆者近段在使用數(shù)據(jù)庫(kù)方面的心得,是在做“辦公自動(dòng)化”系統(tǒng)時(shí)實(shí)踐經(jīng)驗(yàn)的積累。希望這篇文章不僅能夠給大家的工作帶來(lái)一定的幫助,也希望能讓大家能夠體會(huì)到分析問(wèn)題的方法;最重要的是,希望這篇文章能夠拋磚引玉,掀起大家的學(xué)習(xí)和討論的興趣,以共同促進(jìn),共同為公安科技強(qiáng)警事業(yè)和金盾工程做出自己最大的努力。

      最后需要說(shuō)明的是,在試驗(yàn)中,我發(fā)現(xiàn)用戶(hù)在進(jìn)行大數(shù)據(jù)量查詢(xún)的時(shí)候,對(duì)數(shù)據(jù)庫(kù)速度影響最大的不是內(nèi)存大小,而是CPU。在我的P4 2.4機(jī)器上試驗(yàn)的時(shí)候,查看“資源管理器”,CPU經(jīng)常出現(xiàn)持續(xù)到100%的現(xiàn)象,而內(nèi)存用量卻并沒(méi)有改變或者說(shuō)沒(méi)有大的改變。即使在我們的HP ML 350 G3服務(wù)器上試驗(yàn)時(shí),CPU峰值也能達(dá)到90%,一般持續(xù)在70%左右。

      本文的試驗(yàn)數(shù)據(jù)都是來(lái)自我們的HP ML 350服務(wù)器。服務(wù)器配置:雙Inter Xeon 超線程 CPU 2.4G,內(nèi)存1G,操作系統(tǒng)Windows Server 2003 Enterprise Edition,數(shù)據(jù)庫(kù)SQL Server 2000 SP3。 



    本博客為學(xué)習(xí)交流用,凡未注明引用的均為本人作品,轉(zhuǎn)載請(qǐng)注明出處,如有版權(quán)問(wèn)題請(qǐng)及時(shí)通知。由于博客時(shí)間倉(cāng)促,錯(cuò)誤之處敬請(qǐng)諒解,有任何意見(jiàn)可給我留言,愿共同學(xué)習(xí)進(jìn)步。
    posted on 2009-04-12 09:17 Jack.Wang 閱讀(18754) 評(píng)論(7)  編輯  收藏 所屬分類(lèi): 開(kāi)發(fā)技術(shù)

    Feedback

    # re: 海量數(shù)據(jù)處理匯總[未登錄](méi) 2012-07-16 15:51 海納百川
    學(xué)習(xí)啦,寫(xiě)的很詳細(xì)。謝謝!  回復(fù)  更多評(píng)論
      

    # re: 海量數(shù)據(jù)處理匯總 2013-03-14 17:02 qwre
    sfsdfsdf  回復(fù)  更多評(píng)論
      

    # re: 海量數(shù)據(jù)處理匯總[未登錄](méi) 2013-03-14 17:06 life
    嗯,內(nèi)容非常的給力,學(xué)習(xí)了!  回復(fù)  更多評(píng)論
      

    # re: 海量數(shù)據(jù)處理匯總 2013-06-23 17:21 拍磚
    in or 不走索引? not in 和 != <>才不走索引吧.  回復(fù)  更多評(píng)論
      

    # re: 海量數(shù)據(jù)處理匯總 2013-07-26 09:33 劃破
    這樣重復(fù)的文章,轉(zhuǎn)載過(guò)去,意義何在?  回復(fù)  更多評(píng)論
      

    # re: 海量數(shù)據(jù)處理匯總 2013-08-06 11:22 殘骸
    寫(xiě)的不錯(cuò),學(xué)校到了,但最后的結(jié)束語(yǔ)有問(wèn)題,內(nèi)存不動(dòng),是因?yàn)閿?shù)據(jù)庫(kù)和操作系統(tǒng)一直保存著,用來(lái)快速響應(yīng)文件復(fù)制或者數(shù)據(jù)處理,他會(huì)在設(shè)定的時(shí)間后銷(xiāo)毀。說(shuō)白了就是操作系統(tǒng) 或數(shù)據(jù)庫(kù) 一直占用著 快速響應(yīng)你的操作。
    再說(shuō)一下,你的服務(wù)器 配置好低啊~~!!!一年前(2012)我們就hp多少來(lái)著,cpu 8核 ,邏輯核16 ,內(nèi)存 16G(一樣用完,操作系統(tǒng) 和數(shù)據(jù)庫(kù)在運(yùn)行后占用,用于快速響應(yīng))。  回復(fù)  更多評(píng)論
      

    # re: 海量數(shù)據(jù)處理匯總 2013-08-06 11:24 殘骸
    沒(méi)看見(jiàn)你2009年,寫(xiě)的,可能,服務(wù)器就那樣吧。  回復(fù)  更多評(píng)論
      

    主站蜘蛛池模板: 亚洲精品美女在线观看播放| 精品亚洲A∨无码一区二区三区| 中中文字幕亚洲无线码| 97在线视频免费| 亚洲黄色中文字幕| 日韩精品无码专区免费播放| 亚洲国产精品无码久久久秋霞2 | xxxxxx日本处大片免费看| 亚洲国模精品一区| 美女被免费网站91色| 亚洲处破女AV日韩精品| 97青青草原国产免费观看| 亚洲国产成人久久| 免费高清av一区二区三区| 麻豆一区二区三区蜜桃免费| 亚洲色婷婷综合开心网| 久久成人免费播放网站| 亚洲另类春色校园小说| 日本成人免费在线| 国产高潮久久免费观看| 无码欧精品亚洲日韩一区| 免费AA片少妇人AA片直播 | 深夜福利在线免费观看| 夜夜春亚洲嫩草影院| 91av免费观看| 亚洲国产成人AV网站| 中文字幕人成人乱码亚洲电影 | 欧美好看的免费电影在线观看| 亚洲国产成人手机在线观看| 亚洲精品视频在线观看你懂的| 日本一区午夜艳熟免费| 亚洲毛片免费视频| 亚洲AV成人精品日韩一区18p| 黄色免费在线网站| 亚洲欧美国产日韩av野草社区| 亚洲中文字幕伊人久久无码| 在线观看的免费网站无遮挡| 国产成人 亚洲欧洲| 亚洲视频精品在线| 亚洲成网777777国产精品| 免费无遮挡无码永久视频|