數(shù)據(jù)庫優(yōu)化方法(大數(shù)據(jù)量高并發(fā)訪問)

一、數(shù)據(jù)庫結構的設計

如果不能設計一個合理的數(shù)據(jù)庫模型,不僅會增加客戶端和服務器段程序的編程和維護的難度狡刘,而且將會影響系統(tǒng)實際運行的性能。所以,在一個系統(tǒng)開始實施之前借帘,完備的數(shù)據(jù)庫模型的設計是必須的蜘渣。

在一個系統(tǒng)分析、設計階段肺然,因為數(shù)據(jù)量較小蔫缸,負荷較低。我們往往只注意到功能的實現(xiàn)际起,而很難注意到性能的薄弱之處拾碌,等到系統(tǒng)投入實際運行一段時間后,才發(fā)現(xiàn)系統(tǒng)的性能在降低街望,這時再來考慮提高系統(tǒng)性能則要花費更多的人力物力校翔,而整個系統(tǒng)也不可避免的形成了一個打補丁工程。

所以在考慮整個系統(tǒng)的流程的時候灾前,我們必須要考慮防症,在高并發(fā)大數(shù)據(jù)量的訪問情況下,我們的系統(tǒng)會不會出現(xiàn)極端的情況。(例如:對外統(tǒng)計系統(tǒng)在7月16日出現(xiàn)的數(shù)據(jù)異常的情況蔫敲,并發(fā)大數(shù)據(jù)量的的訪問造成饲嗽,數(shù)據(jù)庫的響應時間不能跟上數(shù)據(jù)刷新的速度造成。具體情況是:在日期臨界時(00:00:00)奈嘿,判斷數(shù)據(jù)庫中是否有當前日期的記錄貌虾,沒有則插入一條當前日期的記錄。在低并發(fā)訪問的情況下裙犹,不會發(fā)生問題尽狠,但是當日期臨界時的訪問量相當大的時候,在做這一判斷的時候伯诬,會出現(xiàn)多次條件成立晚唇,則數(shù)據(jù)庫里會被插入多條當前日期的記錄,從而造成數(shù)據(jù)錯誤盗似。)哩陕,數(shù)據(jù)庫的模型確定下來之后,我們有必要做一個系統(tǒng)內數(shù)據(jù)流向圖赫舒,分析可能出現(xiàn)的瓶頸悍及。

為了保證數(shù)據(jù)庫的一致性和完整性,在邏輯設計的時候往往會設計過多的表間關聯(lián)接癌,盡可能的降低數(shù)據(jù)的冗余心赶。(例如用戶表的地區(qū),我們可以把地區(qū)另外存放到一個地區(qū)表中)如果數(shù)據(jù)冗余低缺猛,數(shù)據(jù)的完整性容易得到保證缨叫,提高了數(shù)據(jù)吞吐速度,保證了數(shù)據(jù)的完整性荔燎,清楚地表達數(shù)據(jù)元素之間的關系耻姥。而對于多表之間的關聯(lián)查詢(尤其是大數(shù)據(jù)表)時,其性能將會降低有咨,同時也提高了客戶端程序的編程難度琐簇,因此,物理設計需折衷考慮座享,根據(jù)業(yè)務規(guī)則婉商,確定對關聯(lián)表的數(shù)據(jù)量大小、數(shù)據(jù)項的訪問頻度渣叛,對此類數(shù)據(jù)表頻繁的關聯(lián)查詢應適當提高數(shù)據(jù)冗余設計但增加了表間連接查詢的操作丈秩,也使得程序的變得復雜,為了提高系統(tǒng)的響應時間淳衙,合理的數(shù)據(jù)冗余也是必要的癣籽。設計人員在設計階段應根據(jù)系統(tǒng)操作的類型挽唉、頻度加以均衡考慮。

另外筷狼,最好不要用自增屬性字段作為主鍵與子表關聯(lián)瓶籽。不便于系統(tǒng)的遷移和數(shù)據(jù)恢復。對外統(tǒng)計系統(tǒng)映射關系丟失(******************)埂材。

原來的表格必須可以通過由它分離出去的表格重新構建塑顺。使用這個規(guī)定的好處是,你可以確保不會在分離的表格中引入多余的列俏险,所有你創(chuàng)建的表格結構都與它們的實際需要一樣大严拒。應用這條規(guī)定是一個好習慣,不過除非你要處理一個非常大型的數(shù)據(jù)竖独,否則你將不需要用到它裤唠。(例如一個通行證系統(tǒng),我可以將USERID莹痢,USERNAME种蘸,USERPASSWORD,單獨出來作個表竞膳,再把USERID作為其他表的外鍵)

表的設計具體注意的問題:

1航瞭、數(shù)據(jù)行的長度不要超過8020字節(jié),如果超過這個長度的話在物理頁中這條數(shù)據(jù)會占用兩行從而造成存儲碎片坦辟,降低查詢效率刊侯。

2、能夠用數(shù)字類型的字段盡量選擇數(shù)字類型而不用字符串類型的(電話號碼)锉走,這會降低查詢和連接的性能滨彻,并會增加存儲開銷。這是因為引擎在處理查詢和連接回逐個比較字符串中每一個字符挪蹭,而對于數(shù)字型而言只需要比較一次就夠了亭饵。

3、對于不可變字符類型char和可變字符類型varchar 都是8000字節(jié),char查詢快嚣潜,但是耗存儲空間冬骚,varchar查詢相對慢一些但是節(jié)省存儲空間椅贱。在設計字段的時候可以靈活選擇懂算,例如用戶名、密碼等長度變化不大的字段可以選擇CHAR庇麦,對于評論等長度變化大的字段可以選擇VARCHAR计技。

4、字段的長度在最大限度的滿足可能的需要的前提下山橄,應該盡可能的設得短一些垮媒,這樣可以提高查詢的效率,而且在建立索引的時候也可以減少資源的消耗。


二睡雇、查詢的優(yōu)化

保證在實現(xiàn)功能的基礎上萌衬,盡量減少對數(shù)據(jù)庫的訪問次數(shù);通過搜索參數(shù)它抱,盡量減少對表的訪問行數(shù),最小化結果集秕豫,從而減輕網絡負擔;能夠分開的操作盡量分開處理观蓄,提高每次的響應速度混移;在數(shù)據(jù)窗口使用SQL時,盡量把使用的索引放在選擇的首列侮穿;算法的結構盡量簡單歌径;在查詢時,不要過多地使用通配符如SELECT * FROM T1語句亲茅,要用到幾列就選擇幾列如:SELECT COL1,COL2 FROM T1回铛;在可能的情況下盡量限制盡量結果集行數(shù)如:SELECT TOP 300 COL1,COL2,COL3 FROM T1,因為某些情況下用戶是不需要那么多的數(shù)據(jù)的。

在沒有建索引的情況下芯急,數(shù)據(jù)庫查找某一條數(shù)據(jù)勺届,就必須進行全表掃描了,對所有數(shù)據(jù)進行一次遍歷娶耍,查找出符合條件的記錄免姿。在數(shù)據(jù)量比較小的情況下,也許看不出明顯的差別榕酒,但是當數(shù)據(jù)量大的情況下胚膊,這種情況就是極為糟糕的了。

SQL語句在SQL SERVER中是如何執(zhí)行的想鹰,他們擔心自己所寫的SQL語句會被SQL SERVER誤解紊婉。比如:

select * from table1 where name='zhangsan' and tID > 10000

和執(zhí)行:

select * from table1 where tID > 10000 and name='zhangsan'

一些人不知道以上兩條語句的執(zhí)行效率是否一樣,因為如果簡單的從語句先后上看辑舷,這兩個語句的確是不一樣喻犁,如果tID是一個聚合索引,那么后一句僅僅從表的10000條以后的記錄中查找就行了何缓;而前一句則要先從全表中查找看有幾個name='zhangsan'的肢础,而后再根據(jù)限制條件條件tID>10000來提出查詢結果。

事實上碌廓,這樣的擔心是不必要的传轰。SQL SERVER中有一個“查詢分析優(yōu)化器”,它可以計算出where子句中的搜索條件并確定哪個索引能縮小表掃描的搜索空間谷婆,也就是說慨蛙,它能實現(xiàn)自動優(yōu)化辽聊。雖然查詢優(yōu)化器可以根據(jù)where子句自動的進行查詢優(yōu)化,但有時查詢優(yōu)化器就會不按照您的本意進行快速查詢期贫。

在查詢分析階段跟匆,查詢優(yōu)化器查看查詢的每個階段并決定限制需要掃描的數(shù)據(jù)量是否有用。如果一個階段可以被用作一個掃描參數(shù)(SARG)通砍,那么就稱之為可優(yōu)化的贾铝,并且可以利用索引快速獲得所需數(shù)據(jù)。

SARG的定義:用于限制搜索的一個操作埠帕,因為它通常是指一個特定的匹配垢揩,一個值的范圍內的匹配或者兩個以上條件的AND連接。形式如下:

列名 操作符 <常數(shù) 或 變量> 或 <常數(shù) 或 變量> 操作符 列名

列名可以出現(xiàn)在操作符的一邊敛瓷,而常數(shù)或變量出現(xiàn)在操作符的另一邊叁巨。如:

Name=’張三’

價格>5000

5000<價格

Name=’張三’ and 價格>5000

如果一個表達式不能滿足SARG的形式,那它就無法限制搜索的范圍了呐籽,也就是SQL SERVER必須對每一行都判斷它是否滿足WHERE子句中的所有條件锋勺。所以一個索引對于不滿足SARG形式的表達式來說是無用的。

所以狡蝶,優(yōu)化查詢最重要的就是庶橱,盡量使語句符合查詢優(yōu)化器的規(guī)則避免全表掃描而使用索引查詢。

具體要注意的:

1.應盡量避免在 where 子句中對字段進行 null 值判斷贪惹,否則將導致引擎放棄使用索引而進行全表掃描苏章,如:

select id from t where num is null

可以在num上設置默認值0,確保表中num列沒有null值奏瞬,然后這樣查詢:

select id from t where num=0

2.應盡量避免在 where 子句中使用!=或<>操作符枫绅,否則將引擎放棄使用索引而進行全表掃描。優(yōu)化器將無法通過索引來確定將要命中的行數(shù),因此需要搜索該表的所有行硼端。

3.應盡量避免在 where 子句中使用 or 來連接條件并淋,否則將導致引擎放棄使用索引而進行全表掃描,如:

select id from t where num=10 or num=20

可以這樣查詢:

select id from t where num=10

union all

select id from t where num=20

4.in 和 not in 也要慎用珍昨,因為IN會使系統(tǒng)無法使用索引,而只能直接搜索表中的數(shù)據(jù)县耽。如:

select id from t where num in(1,2,3)

對于連續(xù)的數(shù)值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

5.盡量避免在索引過的字符數(shù)據(jù)中镣典,使用非打頭字母搜索兔毙。這也使得引擎無法利用索引。

見如下例子:

SELECT * FROM T1 WHERE NAME LIKE ‘%L%’

SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=’L’

SELECT * FROM T1 WHERE NAME LIKE ‘L%’

即使NAME字段建有索引骆撇,前兩個查詢依然無法利用索引完成加快操作瞒御,引擎不得不對全表所有數(shù)據(jù)逐條操作來完成任務父叙。而第三個查詢能夠使用索引來加快操作神郊。

6.必要時強制查詢優(yōu)化器使用某個索引肴裙,如在 where 子句中使用參數(shù),也會導致全表掃描涌乳。因為SQL只有在運行時才會解析局部變量蜻懦,但優(yōu)化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇夕晓。然而宛乃,如果在編譯時建立訪問計劃,變量的值還是未知的蒸辆,因而無法作為索引選擇的輸入項征炼。如下面語句將進行全表掃描:

select id from t wherenum=@num

可以改為強制查詢使用索引:

select id from t with(index(索引名)) wherenum=@num

7.應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描躬贡。如:

SELECT * FROM T1 WHERE F1/2=100

應改為:

SELECT * FROM T1 WHERE F1=100*2

SELECT * FROM RECORD WHERE SUBSTRING(CARD_NO,1,4)=’5378’

應改為:

SELECT * FROM RECORD WHERE CARD_NO LIKE ‘5378%’

SELECT member_number, first_name, last_name FROM members

WHERE DATEDIFF(yy,datofbirth,GETDATE()) > 21

應改為:

SELECT member_number, first_name, last_name FROM members

WHERE dateofbirth < DATEADD(yy,-21,GETDATE())

即:任何對列的操作都將導致表掃描谆奥,它包括數(shù)據(jù)庫函數(shù)、計算表達式等等拂玻,查詢時要盡可能將操作移至等號右邊酸些。

8.應盡量避免在where子句中對字段進行函數(shù)操作,這將導致引擎放棄使用索引而進行全表掃描檐蚜。如:

select id from t where substring(name,1,3)='abc'--name以abc開頭的id

select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id

應改為:

select id from t where name like 'abc%'

select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'

9.不要在 where 子句中的“=”左邊進行函數(shù)魄懂、算術運算或其他表達式運算,否則系統(tǒng)將可能無法正確使用索引闯第。

10.在使用索引字段作為條件時市栗,如果該索引是復合索引,那么必須使用到該索引中的第一個字段作為條件時才能保證系統(tǒng)使用該索引咳短,否則該索引將不會被使用肃廓,并且應盡可能的讓字段順序與索引順序相一致。

11.很多時候用 exists是一個好的選擇:

elect num from a where num in(select num from b)

用下面的語句替換:

select num from a where exists(select 1 from b where num=a.num)

SELECT SUM(T1.C1)FROM T1 WHERE(

(SELECT COUNT(*)FROM T2 WHERE T2.C2=T1.C2>0)

SELECT SUM(T1.C1) FROM T1WHERE EXISTS(

SELECT * FROM T2 WHERE T2.C2=T1.C2)

兩者產生相同的結果诲泌,但是后者的效率顯然要高于前者盲赊。因為后者不會產生大量鎖定的表掃描或是索引掃描。

如果你想校驗表里是否存在某條紀錄敷扫,不要用count(*)那樣效率很低哀蘑,而且浪費服務器資源】冢可以用EXISTS代替绘迁。如:

IF (SELECT COUNT(*) FROM table_name WHERE column_name = 'xxx')

可以寫成:

IF EXISTS (SELECT * FROM table_name WHERE column_name = 'xxx')

經常需要寫一個T_SQL語句比較一個父結果集和子結果集,從而找到是否存在在父結果集中有而在子結果集中沒有的記錄卒密,如:

SELECT a.hdr_key FROM hdr_tbl a---- tbl a 表示tbl用別名a代替

WHERE NOT EXISTS (SELECT * FROM dtl_tbl b WHERE a.hdr_key = b.hdr_key)

SELECT a.hdr_key FROM hdr_tbl a

LEFT JOIN dtl_tbl b ON a.hdr_key = b.hdr_key WHERE b.hdr_key IS NULL

SELECT hdr_key FROM hdr_tbl

WHERE hdr_key NOT IN (SELECT hdr_key FROM dtl_tbl)

三種寫法都可以得到同樣正確的結果缀台,但是效率依次降低。

12.盡量使用表變量來代替臨時表哮奇。如果表變量包含大量數(shù)據(jù)膛腐,請注意索引非常有限(只有主鍵索引)睛约。

13.避免頻繁創(chuàng)建和刪除臨時表,以減少系統(tǒng)表資源的消耗哲身。

14.臨時表并不是不可使用辩涝,適當?shù)厥褂盟鼈兛梢允鼓承├谈行В缈碧欤斝枰貜鸵么笮捅砘虺S帽碇械哪硞€數(shù)據(jù)集時怔揩。但是,對于一次性事件脯丝,最好使用導出表商膊。

15.在新建臨時表時,如果一次性插入數(shù)據(jù)量很大宠进,那么可以使用 select into 代替 create table翘狱,避免造成大量 log ,以提高速度砰苍;如果數(shù)據(jù)量不大潦匈,為了緩和系統(tǒng)表的資源,應先create table赚导,然后insert茬缩。

16.如果使用到了臨時表,在存儲過程的最后務必將所有的臨時表顯式刪除吼旧,先 truncate table 凰锡,然后 drop table ,這樣可以避免系統(tǒng)表的較長時間鎖定圈暗。

17.在所有的存儲過程和觸發(fā)器的開始處設置 SET NOCOUNT ON 掂为,在結束時設置 SET NOCOUNT OFF 。無需在執(zhí)行存儲過程和觸發(fā)器的每個語句后向客戶端發(fā)送 DONE_IN_PROC 消息员串。

18.盡量避免大事務操作勇哗,提高系統(tǒng)并發(fā)能力。

19.盡量避免向客戶端返回大數(shù)據(jù)量寸齐,若數(shù)據(jù)量過大欲诺,應該考慮相應需求是否合理。

20. 避免使用不兼容的數(shù)據(jù)類型渺鹦。例如float和int扰法、char和varchar、binary和varbinary是不兼容的毅厚。數(shù)據(jù)類型的不兼容可能使優(yōu)化器無法執(zhí)行一些本來可以進行的優(yōu)化操作塞颁。例如:

SELECT name FROM employee WHERE salary > 60000

在這條語句中,如salary字段是money型的,則優(yōu)化器很難對其進行優(yōu)化,因為60000是個整型數(shù)。我們應當在編程時將整型轉化成為錢幣型,而不要等到運行時轉化。

21.充分利用連接條件祠锣,在某種情況下酷窥,兩個表之間可能不只一個的連接條件,這時在 WHERE 子句中將連接條件完整的寫上锤岸,有可能大大提高查詢速度。

例:

SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO

SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO AND A.ACCOUNT_NO=B.ACCOUNT_NO

第二句將比第一句執(zhí)行快得多板乙。

22是偷、使用視圖加速查詢

把表的一個子集進行排序并創(chuàng)建視圖,有時能加速查詢募逞。它有助于避免多重排序 操作蛋铆,而且在其他方面還能簡化優(yōu)化器的工作。例如:

SELECT cust.name放接,rcvbles.balance刺啦,……other columns

FROM cust,rcvbles

WHERE cust.customer_id = rcvlbes.customer_id

AND rcvblls.balance>0

AND cust.postcode>“98000”

ORDER BY cust.name

如果這個查詢要被執(zhí)行多次而不止一次纠脾,可以把所有未付款的客戶找出來放在一個視圖中玛瘸,并按客戶的名字進行排序:

CREATE VIEW DBO.V_CUST_RCVLBES

AS

SELECT cust.name,rcvbles.balance苟蹈,……other columns

FROM cust糊渊,rcvbles

WHERE cust.customer_id = rcvlbes.customer_id

AND rcvblls.balance>0

ORDER BY cust.name

然后以下面的方式在視圖中查詢:

SELECT * FROM V_CUST_RCVLBES

WHERE postcode>“98000”

視圖中的行要比主表中的行少,而且物理順序就是所要求的順序慧脱,減少了磁盤I/O渺绒,所以查詢工作量可以得到大幅減少。

23菱鸥、能用DISTINCT的就不用GROUP BY

SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID

可改為:

SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10

24.能用UNION ALL就不要用UNION

UNION ALL不執(zhí)行SELECT DISTINCT函數(shù)宗兼,這樣就會減少很多不必要的資源

35.盡量不要用SELECT INTO語句。

SELECT INOT 語句會導致表鎖定氮采,阻止其他用戶訪問該表殷绍。

上面我們提到的是一些基本的提高查詢速度的注意事項,但是在更多的情況下,往往需要反復試驗比較不同的語句以得到最佳方案。最好的方法當然是測試鹊漠,看實現(xiàn)相同功能的SQL語句哪個執(zhí)行時間最少篡帕,但是數(shù)據(jù)庫中如果數(shù)據(jù)量很少,是比較不出來的贸呢,這時可以用查看執(zhí)行計劃镰烧,即:把實現(xiàn)相同功能的多條SQL語句考到查詢分析器,按CTRL+L看查所利用的索引楞陷,表掃描次數(shù)(這兩個對性能影響最大)怔鳖,總體上看詢成本百分比即可。


三固蛾、算法的優(yōu)化

盡量避免使用游標结执,因為游標的效率較差度陆,如果游標操作的數(shù)據(jù)超過1萬行,那么就應該考慮改寫献幔。.使用基于游標的方法或臨時表方法之前懂傀,應先尋找基于集的解決方案來解決問題,基于集的方法通常更有效蜡感。與臨時表一樣蹬蚁,游標并不是不可使用。對小型數(shù)據(jù)集使用 FAST_FORWARD 游標通常要優(yōu)于其他逐行處理方法郑兴,尤其是在必須引用幾個表才能獲得所需的數(shù)據(jù)時犀斋。在結果集中包括“合計”的例程通常要比使用游標執(zhí)行的速度快。如果開發(fā)時間允許情连,基于游標的方法和基于集的方法都可以嘗試一下叽粹,看哪一種方法的效果更好。

游標提供了對特定集合中逐行掃描的手段却舀,一般使用游標逐行遍歷數(shù)據(jù)虫几,根據(jù)取出的數(shù)據(jù)不同條件進行不同的操作。尤其對多表和大表定義的游標(大的數(shù)據(jù)集合)循環(huán)很容易使程序進入一個漫長的等特甚至死機挽拔。

在有些場合持钉,有時也非得使用游標,此時也可考慮將符合條件的數(shù)據(jù)行轉入臨時表中篱昔,再對臨時表定義游標進行操作每强,可時性能得到明顯提高。

(例如:對內統(tǒng)計第一版)

封裝存儲過程


四州刽、建立高效的索引

創(chuàng)建索引一般有以下兩個目的:維護被索引列的唯一性和提供快速訪問表中數(shù)據(jù)的策略空执。大型數(shù)據(jù)庫有兩種索引即簇索引和非簇索引,一個沒有簇索引的表是按堆結構存儲數(shù)據(jù)穗椅,所有的數(shù)據(jù)均添加在表的尾部辨绊,而建立了簇索引的表,其數(shù)據(jù)在物理上會按照簇索引鍵的順序存儲匹表,一個表只允許有一個簇索引门坷,因此,根據(jù)B樹結構袍镀,可以理解添加任何一種索引均能提高按索引列查詢的速度默蚌,但會降低插入、更新苇羡、刪除操作的性能绸吸,尤其是當填充因子(Fill Factor)較大時。所以對索引較多的表進行頻繁的插入、更新锦茁、刪除操作攘轩,建表和索引時因設置較小的填充因子,以便在各數(shù)據(jù)頁中留下較多的自由空間码俩,減少頁分割及重新組織的工作度帮。

索引是從數(shù)據(jù)庫中獲取數(shù)據(jù)的最高效方式之一。95% 的數(shù)據(jù)庫性能問題都可以采用索引技術得到解決稿存。作為一條規(guī)則笨篷,我通常對邏輯主鍵使用唯一的成組索引,對系統(tǒng)鍵(作為存儲過程)采用唯一的非成組索引挠铲,對任何外鍵列[字段]采用非成組索引冕屯。不過寂诱,索引就象是鹽拂苹,太多了菜就咸了。你得考慮數(shù)據(jù)庫的空間有多大痰洒,表如何進行訪問瓢棒,還有這些訪問是否主要用作讀寫。

實際上丘喻,您可以把索引理解為一種特殊的目錄脯宿。微軟的SQL SERVER提供了兩種索引:聚集索引(clustered index,也稱聚類索引泉粉、簇集索引)和非聚集索引(nonclustered index连霉,也稱非聚類索引、非簇集索引)嗡靡。下面跺撼,我們舉例來說明一下聚集索引和非聚集索引的區(qū)別:

其實,我們的漢語字典的正文本身就是一個聚集索引讨彼。比如歉井,我們要查“安”字,就會很自然地翻開字典的前幾頁哈误,因為“安”的拼音是“an”哩至,而按照拼音排序漢字的字典是以英文字母“a”開頭并以“z”結尾的,那么“安”字就自然地排在字典的前部蜜自。如果您翻完了所有以“a”開頭的部分仍然找不到這個字菩貌,那么就說明您的字典中沒有這個字;同樣的重荠,如果查“張”字菜谣,那您也會將您的字典翻到最后部分,因為“張”的拼音是“zhang”。也就是說尾膊,字典的正文部分本身就是一個目錄媳危,您不需要再去查其他目錄來找到您需要找的內容。

我們把這種正文內容本身就是一種按照一定規(guī)則排列的目錄稱為“聚集索引”冈敛。

如果您認識某個字待笑,您可以快速地從自動中查到這個字。但您也可能會遇到您不認識的字抓谴,不知道它的發(fā)音暮蹂,這時候,您就不能按照剛才的方法找到您要查的字癌压,而需要去根據(jù)“偏旁部首”查到您要找的字仰泻,然后根據(jù)這個字后的頁碼直接翻到某頁來找到您要找的字。但您結合“部首目錄”和“檢字表”而查到的字的排序并不是真正的正文的排序方法滩届,比如您查“張”字集侯,我們可以看到在查部首之后的檢字表中“張”的頁碼是672頁,檢字表中“張”的上面是“馳”字帜消,但頁碼卻是63頁棠枉,“張”的下面是“弩”字,頁面是390頁泡挺。很顯然辈讶,這些字并不是真正的分別位于“張”字的上下方,現(xiàn)在您看到的連續(xù)的“馳娄猫、張贱除、弩”三字實際上就是他們在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射媳溺。我們可以通過這種方式來找到您所需要的字月幌,但它需要兩個過程,先找到目錄中的結果褂删,然后再翻到您所需要的頁碼飞醉。

我們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱為“非聚集索引”屯阀。

進一步引申一下缅帘,我們可以很容易的理解:每個表只能有一個聚集索引,因為目錄只能按照一種方法進行排序难衰。

(一)何時使用聚集索引或非聚集索引

下面的表總結了何時使用聚集索引或非聚集索引(很重要)钦无。

動作描述 使用聚集索引 使用非聚集索引

列經常被分組排序 應 應

返回某范圍內的數(shù)據(jù) 應 不應

一個或極少不同值 不應 不應

小數(shù)目的不同值 應 不應

大數(shù)目的不同值 不應 應

頻繁更新的列 不應 應

外鍵列 應 應

主鍵列 應 應

頻繁修改索引列 不應 應

事實上,我們可以通過前面聚集索引和非聚集索引的定義的例子來理解上表盖袭。如:返回某范圍內的數(shù)據(jù)一項鲫趁。比如您的某個表有一個時間列,恰好您把聚合索引建立在了該列堂竟,這時您查詢2004年1月1日至2004年10月1日之間的全部數(shù)據(jù)時嘁酿,這個速度就將是很快的牲阁,因為您的這本字典正文是按日期進行排序的,聚類索引只需要找到要檢索的所有數(shù)據(jù)中的開頭和結尾數(shù)據(jù)即可;而不像非聚集索引,必須先查到目錄中查到每一項數(shù)據(jù)對應的頁碼摧冀,然后再根據(jù)頁碼查到具體內容。

(二)結合實際系宫,談索引使用的誤區(qū)

理論的目的是應用索昂。雖然我們剛才列出了何時應使用聚集索引或非聚集索引,但在實踐中以上規(guī)則卻很容易被忽視或不能根據(jù)實際情況進行綜合分析扩借。下面我們將根據(jù)在實踐中遇到的實際問題來談一下索引使用的誤區(qū)椒惨,以便于大家掌握索引建立的方法。

1潮罪、主鍵就是聚集索引

這種想法筆者認為是極端錯誤的康谆,是對聚集索引的一種浪費。雖然SQL SERVER默認是在主鍵上建立聚集索引的错洁。

通常秉宿,我們會在每個表中都建立一個ID列戒突,以區(qū)分每條數(shù)據(jù)屯碴,并且這個ID列是自動增大的,步長一般為1膊存。我們的這個辦公自動化的實例中的列Gid就是如此导而。此時,如果我們將這個列設為主鍵隔崎,SQL SERVER會將此列默認為聚集索引今艺。這樣做有好處,就是可以讓您的數(shù)據(jù)在數(shù)據(jù)庫中按照ID進行物理排序爵卒,但筆者認為這樣做意義不大虚缎。

顯而易見,聚集索引的優(yōu)勢是很明顯的钓株,而每個表中只能有一個聚集索引的規(guī)則实牡,這使得聚集索引變得更加珍貴。

從我們前面談到的聚集索引的定義我們可以看出轴合,使用聚集索引的最大好處就是能夠根據(jù)查詢要求创坞,迅速縮小查詢范圍,避免全表掃描受葛。在實際應用中题涨,因為ID號是自動生成的偎谁,我們并不知道每條記錄的ID號,所以我們很難在實踐中用ID號來進行查詢纲堵。這就使讓ID號這個主鍵作為聚集索引成為一種資源浪費巡雨。其次,讓每個ID號都不同的字段作為聚集索引也不符合“大數(shù)目的不同值情況下不應建立聚合索引”規(guī)則席函;當然鸯隅,這種情況只是針對用戶經常修改記錄內容,特別是索引項的時候會負作用向挖,但對于查詢速度并沒有影響蝌以。

在辦公自動化系統(tǒng)中,無論是系統(tǒng)首頁顯示的需要用戶簽收的文件何之、會議還是用戶進行文件查詢等任何情況下進行數(shù)據(jù)查詢都離不開字段的是“日期”還有用戶本身的“用戶名”跟畅。

通常,辦公自動化的首頁會顯示每個用戶尚未簽收的文件或會議溶推。雖然我們的where語句可以僅僅限制當前用戶尚未簽收的情況徊件,但如果您的系統(tǒng)已建立了很長時間,并且數(shù)據(jù)量很大蒜危,那么虱痕,每次每個用戶打開首頁的時候都進行一次全表掃描,這樣做意義是不大的辐赞,絕大多數(shù)的用戶1個月前的文件都已經瀏覽過了部翘,這樣做只能徒增數(shù)據(jù)庫的開銷而已。事實上响委,我們完全可以讓用戶打開系統(tǒng)首頁時新思,數(shù)據(jù)庫僅僅查詢這個用戶近3個月來未閱覽的文件,通過“日期”這個字段來限制表掃描赘风,提高查詢速度夹囚。如果您的辦公自動化系統(tǒng)已經建立的2年,那么您的首頁顯示速度理論上將是原來速度8倍邀窃,甚至更快荸哟。

2、只要建立索引就能顯著提高查詢速度

事實上瞬捕,我們可以發(fā)現(xiàn)上面的例子中鞍历,第2、3條語句完全相同山析,且建立索引的字段也相同堰燎;不同的僅是前者在fariqi字段上建立的是非聚合索引,后者在此字段上建立的是聚合索引笋轨,但查詢速度卻有著天壤之別秆剪。所以赊淑,并非是在任何字段上簡單地建立索引就能提高查詢速度。

從建表的語句中仅讽,我們可以看到這個有著1000萬數(shù)據(jù)的表中fariqi字段有5003個不同記錄陶缺。在此字段上建立聚合索引是再合適不過了。在現(xiàn)實中洁灵,我們每天都會發(fā)幾個文件饱岸,這幾個文件的發(fā)文日期就相同,這完全符合建立聚集索引要求的:“既不能絕大多數(shù)都相同徽千,又不能只有極少數(shù)相同”的規(guī)則苫费。由此看來,我們建立“適當”的聚合索引對于我們提高查詢速度是非常重要的双抽。

3百框、把所有需要提高查詢速度的字段都加進聚集索引,以提高查詢速度

上面已經談到:在進行數(shù)據(jù)查詢時都離不開字段的是“日期”還有用戶本身的“用戶名”牍汹。既然這兩個字段都是如此的重要铐维,我們可以把他們合并起來,建立一個復合索引(compound index)慎菲。

很多人認為只要把任何字段加進聚集索引嫁蛇,就能提高查詢速度,也有人感到迷惑:如果把復合的聚集索引字段分開查詢露该,那么查詢速度會減慢嗎睬棚?帶著這個問題,我們來看一下以下的查詢速度(結果集都是25萬條數(shù)據(jù)):(日期列fariqi首先排在復合聚集索引的起始列有决,用戶名neibuyonghu排在后列)

我們可以看到如果僅用聚集索引的起始列作為查詢條件和同時用到復合聚集索引的全部列的查詢速度是幾乎一樣的闸拿,甚至比用上全部的復合索引列還要略快(在查詢結果集數(shù)目一樣的情況下)空盼;而如果僅用復合聚集索引的非起始列作為查詢條件的話书幕,這個索引是不起任何作用的。當然揽趾,語句1台汇、2的查詢速度一樣是因為查詢的條目數(shù)一樣,如果復合索引的所有列都用上篱瞎,而且查詢結果少的話苟呐,這樣就會形成“索引覆蓋”,因而性能可以達到最優(yōu)俐筋。同時牵素,請記住:無論您是否經常使用聚合索引的其他列澄者,但其前導列一定要是使用最頻繁的列笆呆。

(三)其他注意事項

“水可載舟请琳,亦可覆舟”,索引也一樣赠幕。索引有助于提高檢索性能俄精,但過多或不當?shù)乃饕矔е孪到y(tǒng)低效。因為用戶在表中每加進一個索引榕堰,數(shù)據(jù)庫就要做更多的工作竖慧。過多的索引甚至會導致索引碎片。

所以說逆屡,我們要建立一個“適當”的索引體系圾旨,特別是對聚合索引的創(chuàng)建,更應精益求精魏蔗,以使您的數(shù)據(jù)庫能得到高性能的發(fā)揮

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末碳胳,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子沫勿,更是在濱河造成了極大的恐慌挨约,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件产雹,死亡現(xiàn)場離奇詭異诫惭,居然都是意外死亡,警方通過查閱死者的電腦和手機蔓挖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門夕土,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瘟判,你說我怎么就攤上這事怨绣。” “怎么了拷获?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵篮撑,是天一觀的道長。 經常有香客問我匆瓜,道長赢笨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任驮吱,我火速辦了婚禮茧妒,結果婚禮上,老公的妹妹穿的比我還像新娘左冬。我一直安慰自己桐筏,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布拇砰。 她就那樣靜靜地躺著梅忌,像睡著了一般绊袋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上铸鹰,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天癌别,我揣著相機與錄音,去河邊找鬼蹋笼。 笑死展姐,一個胖子當著我的面吹牛,可吹牛的內容都是我干的剖毯。 我是一名探鬼主播圾笨,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逊谋!你這毒婦竟也來了擂达?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤胶滋,失蹤者是張志新(化名)和其女友劉穎板鬓,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體究恤,經...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡俭令,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了部宿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抄腔。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖理张,靈堂內的尸體忽然破棺而出赫蛇,到底是詐尸還是另有隱情,我是刑警寧澤雾叭,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布悟耘,位于F島的核電站,受9級特大地震影響拷况,放射性物質發(fā)生泄漏作煌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一赚瘦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奏寨,春花似錦起意、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悲酷。三九已至,卻和暖如春亲善,著一層夾襖步出監(jiān)牢的瞬間设易,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工蛹头, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留顿肺,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓渣蜗,卻偏偏與公主長得像屠尊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子耕拷,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容