性能問題是困擾數(shù)據(jù)庫用戶的常見問題之一。經(jīng)常會(huì)有人因?yàn)橛龅叫阅軉栴},質(zhì)疑SQL Server處理大型數(shù)據(jù)應(yīng)用的能力。其實(shí),作為一個(gè)在市場上經(jīng)營了二十多年,出了好幾代版本的數(shù)據(jù)庫產(chǎn)品,SQL Server作為一個(gè)企業(yè)級(jí)數(shù)據(jù)庫的能力,是毋庸置疑的。在實(shí)際應(yīng)用中,數(shù)據(jù)量達(dá)到幾百GB,甚至上TB級(jí),并發(fā)連接數(shù)超過1、2千個(gè),每秒鐘處理的請(qǐng)求 數(shù)量超過1000多個(gè)的SQL Server,現(xiàn)在已經(jīng)很多了。在國內(nèi)的一些大客戶那里,我們也越來越多地看到這樣的SQL Server。
那為什么有些SQL Server能跑得那么強(qiáng)勁,而很多用戶的數(shù)據(jù)庫還只有幾十GB,就感覺跑不動(dòng)了呢?在談性能問題的常見原因之前,我們先談?wù)凷QL Server的幾個(gè)和性能有關(guān)的重要特性。了解這些特性,對(duì)設(shè)計(jì)一個(gè)高效的數(shù)據(jù)庫應(yīng)用,是非常必要的。
不管是要查詢的數(shù)據(jù),還是要修改的數(shù)據(jù),SQL Server在運(yùn)行客戶端發(fā)過來的語句,處理這些數(shù)據(jù)之前,都要檢查其訪問的數(shù)據(jù)是否在內(nèi)存中。如果不在內(nèi)存中,SQL Server會(huì)先把存儲(chǔ)數(shù)據(jù)的頁面從磁盤調(diào)入內(nèi)存,然后再做真正的數(shù)據(jù)處理。
數(shù)據(jù)處理完畢后,SQL Server不會(huì)馬上把這段數(shù)據(jù)緩存丟棄。只要SQL Server不缺內(nèi)存,先前訪問過數(shù)據(jù)頁面就會(huì)一直緩存在SQL
Server進(jìn)程的地址空間里。這樣,如果下次有其他用戶要訪問同樣的數(shù)據(jù)記錄,SQL
Server就可以馬上在內(nèi)存中進(jìn)行處理,而不需要再到磁盤上去找。
這樣的設(shè)計(jì),可以最大程度的重用內(nèi)存,提高SQL Server的處理速度。同時(shí)也決定了,SQL Server是一個(gè)非常喜歡大內(nèi)存的應(yīng)用程序。
SQL Server不會(huì)無限制地申請(qǐng)內(nèi)存。它會(huì)根據(jù)用戶的設(shè)置,以及系統(tǒng)的內(nèi)存數(shù)量,計(jì)算自己的最大內(nèi)存數(shù)。如果當(dāng)前的內(nèi)存大小以及達(dá)到了這個(gè)上限,SQL就不 會(huì)再向Windows申請(qǐng)更多的內(nèi)存。這樣的機(jī)理,可以保證SQL Server和Windows,以及運(yùn)行在同一臺(tái)機(jī)器上的其他應(yīng)用程序和平共處。
當(dāng)SQL Server的內(nèi)存數(shù)已經(jīng)達(dá)到最大值,內(nèi)存空間已經(jīng)緩存滿各種各樣的數(shù)據(jù)頁面,而用戶又要訪問新的、還沒有緩存在內(nèi)存里的數(shù)據(jù)時(shí),SQL
Server會(huì)根據(jù)現(xiàn)有數(shù)據(jù)訪問的頻度,把最老的、最不經(jīng)常被人訪問的數(shù)據(jù),從內(nèi)存里清除,從而騰出空間來緩存現(xiàn)在客戶要訪問的新數(shù)據(jù)。
所以最理想的狀態(tài),是用戶要訪問的數(shù)據(jù)永遠(yuǎn)都緩存在內(nèi)存里,SQL Server從來都不需要到磁盤上去找。這也是數(shù)據(jù)庫性能最佳的情況。這時(shí)候SQL Server幾乎從來不需要做磁盤讀。
如果經(jīng)常發(fā)生用戶要訪問的數(shù)據(jù)不在內(nèi)存里的情況,SQL Server就會(huì)被迫不停地在內(nèi)存和磁盤之間倒騰數(shù)據(jù),性能會(huì)受到嚴(yán)重的影響。而這時(shí)候,你會(huì)看到SQL在經(jīng)常地作磁盤讀的動(dòng)作。
SQL Server的一個(gè)顯著的特點(diǎn),是表格里數(shù)據(jù)的存儲(chǔ),是按照聚集索引所在字段的值排序的。而非聚集索引是建立在聚集索引結(jié)構(gòu)之上的。如果一張表格沒有聚集索引,數(shù)據(jù)是按照堆的方式存儲(chǔ),沒有任何順序。
對(duì)于同樣的數(shù)據(jù)量,SQL Server對(duì)一個(gè)有聚集索引的表格的管理,遠(yuǎn)遠(yuǎn)比沒有聚集索引的表格要有效。絕大多數(shù)情況下,一張大表如果想要有良好的性能,就必須有一個(gè)合適的聚集索引。沒有聚集索引,只加非聚集索引,也不能夠達(dá)到優(yōu)化的性能。
這是SQL Server的一個(gè)很重要的特點(diǎn)。
SQL Server實(shí)現(xiàn)的是ANSI 標(biāo)準(zhǔn)的四個(gè)隔離級(jí)別。在Read Committed這個(gè)缺省的隔離級(jí)別上,讀操作會(huì)申請(qǐng)S鎖,修改操作會(huì)申請(qǐng)X鎖,S鎖和X鎖互斥。所以同一條記錄上不能同時(shí)進(jìn)行讀操作和寫操作。
業(yè)界的有些其他數(shù)據(jù)庫產(chǎn)品,缺省使用行版本控制方式實(shí)現(xiàn)事務(wù)隔離,如果一個(gè)用戶在修改某一條記錄、但是沒有提交事務(wù),而另一個(gè)用戶要讀同一條記錄,它會(huì)讓第二個(gè)用戶讀到第一個(gè)人修改之前這條記錄的值。所以讀操作和寫操作是可以同時(shí)進(jìn)行的。
這種版本控制的隔離級(jí)別,并發(fā)度當(dāng)然比SQL Server的要高,讀寫操作之間產(chǎn)生阻塞的幾率要小。可是它的事務(wù)隔離效果和SQL是不同的。例如,對(duì)于某個(gè)銀行賬戶的查詢,假設(shè)賬戶里原來有1萬元, 用戶A開始一個(gè)事務(wù),將賬戶里的1萬元轉(zhuǎn)出。在轉(zhuǎn)出這個(gè)動(dòng)作還沒有完成時(shí),用戶B來查詢賬戶余額。使用版本控制,用戶B可以馬上得到結(jié)果:1萬元,但是這 個(gè)結(jié)果其實(shí)很有可能已經(jīng)過時(shí)。使用SQL Server,用戶B必須等到用戶A轉(zhuǎn)帳完成才能查到余額,但是他得到的一定是一個(gè)最新的值。
這兩種隔離級(jí)別其實(shí)反映了兩種用戶需求,不能講哪種好,哪種不好。喜歡版本控制這種隔離級(jí)別的用戶,如果想要在SQL
Server里有同樣的并發(fā)度,可以選擇SQL里面的Snapshot Isolation Level(這個(gè)功能在SQL 2005里引入)。這樣,SQL里的讀寫操作就不互斥了。
上面的這幾個(gè)特征,決定了SQL Server的很多???為特點(diǎn)。如果跑在SQL Server上應(yīng)用程序沒有很好地按照上述的特征設(shè)計(jì),就容易遇到各種各樣的性能問題。
在現(xiàn)實(shí)應(yīng)用中,SQL Server性能問題的常見原因有下面幾點(diǎn)。
如果沒有好的索引幫助,SQL Server查詢?nèi)魏我粭l記錄都有可能不得不把整張表都掃描一遍。這個(gè)在數(shù)據(jù)庫比較小的時(shí)候影響不大,因?yàn)镾QL能夠把所有數(shù)據(jù)都緩存在內(nèi)存里,就算是全 表掃描也不會(huì)太慢。隨著數(shù)據(jù)量增大,內(nèi)存會(huì)放不下。全表掃描的負(fù)擔(dān)會(huì)越來越重,到最后會(huì)嚴(yán)重影響SQL
Server的整體性能。
這是一個(gè)很常見的SQL Server越跑越慢的原因。
對(duì)于沒有聚集索引的表格,如果里面的記錄數(shù)以萬計(jì)或者更多,管理和查詢都會(huì)增加很多開銷。一個(gè)經(jīng)驗(yàn)是,對(duì)于一個(gè)要經(jīng)常使用的任何大表格,請(qǐng)建立聚集索引。索引所在的數(shù)據(jù)列,應(yīng)該是一個(gè)或者幾個(gè)重復(fù)性的記錄不是很多的數(shù)據(jù)列。
業(yè)界的一些其他數(shù)據(jù)庫可能對(duì)聚集索引這個(gè)概念不是非常強(qiáng)調(diào),或者使用其他的方法管理表格。所以如果把非SQL
Server的數(shù)據(jù)庫遷移到SQL Server里,一定要重新檢查索引結(jié)構(gòu),按照SQL Server的特點(diǎn),調(diào)整索引設(shè)置。否則遷移后在SQL Server上得不到好的性能,是很正常的。
前面我們已經(jīng)說過,SQL
Server要操作的數(shù)據(jù),是必須要緩存在內(nèi)存里的。如果終端所有用戶要經(jīng)常訪問的數(shù)據(jù)量的總和,遠(yuǎn)遠(yuǎn)超出SQL Server所擁有的內(nèi)存數(shù)量,那SQL就會(huì)不得不在內(nèi)存和磁盤間反復(fù)換頁。這時(shí)候的SQL性能下降,可能就不是一兩個(gè)數(shù)量級(jí)了。
用戶需要經(jīng)常訪問的數(shù)據(jù)量的大小,通常跟以下幾個(gè)因素有關(guān)。
a.用戶發(fā)過來的語句定義
如果語句里有良好的條件約束,那數(shù)據(jù)量就能得到控制。如果條件約束性不強(qiáng),那數(shù)據(jù)量就會(huì)隨著表格里的記 錄數(shù)增長而增長。一個(gè)例子是,用戶總是查詢“今年的某某數(shù)據(jù)”。那在一年開始的時(shí)候,查詢肯定很快。到年底時(shí)候的數(shù)據(jù)量可是一月份的12倍。那時(shí)候訪問的 數(shù)據(jù)量也很可能就是12倍。
b.SQL Server是否能夠利用索引,使用Seek的方式找到數(shù)據(jù),而不是掃描全表
如果沒有好的索引,哪怕用戶要訪問的數(shù)據(jù)只是表格里的很小一部分,SQL可能也需要遍歷整個(gè)表格。在這種情況下,也會(huì)出現(xiàn)表格越大,SQL跑得越慢的情況。如果索引設(shè)計(jì)地比較優(yōu)化,SQL Server的數(shù)據(jù)訪問量,不應(yīng)該跟表格的大小有著絕對(duì)的關(guān)系。
c.應(yīng)用程序的性質(zhì)
數(shù)據(jù)庫應(yīng)用基本可以分兩大類:OLTP類型的,和Data Warehouse類型的。前者會(huì)處理大量的小事務(wù),比如病人掛號(hào)、超市結(jié)帳、倉庫進(jìn)貨等。這種應(yīng)用用戶發(fā)過來的請(qǐng)求不會(huì)很復(fù)雜,每次要處理的數(shù)據(jù)量也比 較小,但是要求的響應(yīng)速度要很快。有可能0.5秒的等待都會(huì)帶來性能問題。所以對(duì)這種應(yīng)用,其訪問的數(shù)據(jù)應(yīng)該保證總是緩存在內(nèi)存里。
Data Warehouse類型的應(yīng)用主要是做數(shù)據(jù)分析和整理,經(jīng)常是為了產(chǎn)生一些報(bào)表。這種應(yīng)用主要以查詢?yōu)橹?,肯定?huì)訪問大量的數(shù)據(jù),會(huì)出現(xiàn)數(shù)據(jù)庫越大,訪問的數(shù)據(jù)量越多的情況。但是客戶端可以容忍一定時(shí)間的等待。對(duì)于這種應(yīng)用,其訪問的數(shù)據(jù)不在內(nèi)存里關(guān)系也不大。
對(duì)于OLTP類型的應(yīng)用,應(yīng)該盡量保證用戶經(jīng)常要訪問的數(shù)據(jù)能夠長時(shí)間地緩存在內(nèi)存里。所以在設(shè)計(jì)應(yīng)用邏輯和數(shù)據(jù)庫時(shí),就要有所考慮,控制用戶訪問 的數(shù)據(jù)量,建立有效的索引,避免全表掃描。在管理上,也要有歷史數(shù)據(jù)歸檔的機(jī)制,控制整個(gè)數(shù)據(jù)庫的大小。如果證明用戶需要訪問的數(shù)據(jù)量就是比內(nèi)存大,升級(jí) 內(nèi)存也是需要考慮的方案。
對(duì)于報(bào)表類型的應(yīng)用,因?yàn)樵L問的數(shù)據(jù)量會(huì)很大,磁盤換頁是難以避免的。當(dāng)然良好的索引設(shè)計(jì),對(duì)性能也會(huì)很有幫助。
要避免的情況,是同一個(gè)SQL Server既在運(yùn)行OLTP類型的應(yīng)用,又時(shí)不時(shí)地在運(yùn)行報(bào)表類型的應(yīng)用。它們兩者會(huì)產(chǎn)生很大的相互干擾,對(duì)OLTP應(yīng)用的響應(yīng)速度會(huì)產(chǎn)生破壞性的影響,而且很難用調(diào)整數(shù)據(jù)庫設(shè)計(jì),或者升級(jí)硬件的方法輕易解決。
前面已經(jīng)談過,SQL Server缺省的事務(wù)隔離級(jí)別是Read Committed,和其他數(shù)據(jù)庫可能不一樣。另外,很多應(yīng)用是通過調(diào)用數(shù)據(jù)庫控件的接口來訪問數(shù)據(jù)庫的,而不是直接調(diào)用SQL語句或者存儲(chǔ)過程。要達(dá)到 同一個(gè)目的,程序里有很多種實(shí)現(xiàn)的方法。有些方法可能在其他數(shù)據(jù)庫上性能會(huì)不錯(cuò),但是到SQL
Server上有可能就要換一個(gè)做法,或者換一種驅(qū)動(dòng)。再加上不同的數(shù)據(jù)庫系統(tǒng)對(duì)指令的寫法可能有不同的建議,表格上的索引要求也不一樣。所以要在SQL
Server上實(shí)現(xiàn)良好的性能,就必須對(duì)遷移過來的應(yīng)用在SQL Server平臺(tái)上進(jìn)行優(yōu)化。沒有測試、優(yōu)化而直接移植過來的應(yīng)用,常常不能發(fā)揮SQL Server的長處,性能欠佳。
聯(lián)系客服