總結(jié)一下自己的一點(diǎn)見解
1.1首先需要了解數(shù)據(jù)庫查找的過程
磁盤是一個扁平的圓盤(與電唱機(jī)的唱片類似)。盤面上有許多稱為磁道的圓圈,數(shù)據(jù)就記錄在這些磁道上。磁盤可以是單片的,也可以是由若干盤片組成的盤組,每一盤片上有兩個面。如下圖中所示的6片盤組為例,除去最頂端和最底端的外側(cè)面不存儲數(shù)據(jù)之外,一共有10個面可以用來保存信息。
當(dāng)磁盤驅(qū)動器執(zhí)行讀/寫功能時。盤片裝在一個主軸上,并繞主軸高速旋轉(zhuǎn),當(dāng)磁道在讀/寫頭(又叫磁頭) 下通過時,就可以進(jìn)行數(shù)據(jù)的讀 / 寫了。
一般磁盤分為固定頭盤(磁頭固定)和活動頭盤。固定頭盤的每一個磁道上都有獨(dú)立的磁頭,它是固定不動的,專門負(fù)責(zé)這一磁道上數(shù)據(jù)的讀/寫。
活動頭盤 (如上圖)的磁頭是可移動的。每一個盤面上只有一個磁頭(磁頭是雙向的,因此正反盤面都能讀寫)。它可以從該面的一個磁道移動到另一個磁道。所有磁頭都裝在同一個動臂上,因此不同盤面上的所有磁頭都是同時移動的(行動整齊劃一)。當(dāng)盤片繞主軸旋轉(zhuǎn)的時候,磁頭與旋轉(zhuǎn)的盤片形成一個圓柱體。各個盤面上半徑相同的磁道組成了一個圓柱面,我們稱為柱面 。因此,柱面的個數(shù)也就是盤面上的磁道數(shù)。
1.2磁盤的讀/寫原理和效率
磁盤上數(shù)據(jù)必須用一個三維地址唯一標(biāo)示:柱面號、盤面號、塊號(磁道上的盤塊)。
讀/寫磁盤上某一指定數(shù)據(jù)需要下面3個步驟:
(1) 首先移動臂根據(jù)柱面號使磁頭移動到所需要的柱面上,這一過程被稱為定位或查找 。
(2) 如上圖11.3中所示的6盤組示意圖中,所有磁頭都定位到了10個盤面的10條磁道上(磁頭都是雙向的)。這時根據(jù)盤面號來確定指定盤面上的磁道。
(3) 盤面確定以后,盤片開始旋轉(zhuǎn),將指定塊號的磁道段移動至磁頭下。
經(jīng)過上面三個步驟,指定數(shù)據(jù)的存儲位置就被找到。這時就可以開始讀/寫操作了。
訪問某一具體信息,由3部分時間組成:
● 查找時間(seek time) Ts: 完成上述步驟(1)所需要的時間。這部分時間代價最高,最大可達(dá)到0.1s左右。
● 等待時間(latency time) Tl: 完成上述步驟(3)所需要的時間。由于盤片繞主軸旋轉(zhuǎn)速度很快,一般為7200轉(zhuǎn)/分(電腦硬盤的性能指標(biāo)之一, 家用的普通硬盤的轉(zhuǎn)速一般有5400rpm(筆記本)、7200rpm幾種)。因此一般旋轉(zhuǎn)一圈大約0.0083s。
● 傳輸時間(transmission time) Tt: 數(shù)據(jù)通過系統(tǒng)總線傳送到內(nèi)存的時間,一般傳輸一個字節(jié)(byte)大概0.02us=210^(-8)s
磁盤讀取數(shù)據(jù)是以盤塊(block)為基本單位的。位于同一盤塊中的所有數(shù)據(jù)都能被一次性全部讀取出來。而磁盤IO代價主要花費(fèi)在查找時間Ts上。因此我們應(yīng)該盡量將相關(guān)信息存放在同一盤塊,同一磁道中。或者至少放在同一柱面或相鄰柱面上,以求在讀/寫信息時盡量減少磁頭來回移動的次數(shù),避免過多的查找時間Ts*。
1.3
sql語句和其他語言沒有多大的區(qū)別,需要經(jīng)過解析器和編譯器來轉(zhuǎn)換成二進(jìn)制代碼。在sql中還有優(yōu)化性能的緩存等組件。
block數(shù)據(jù)由DMA從硬盤copy到內(nèi)存中
2.1
mysql優(yōu)化可以朝著上面描述等幾個方面去著手。
● 對于查找時間的優(yōu)化,sql聚集索引使用b-樹來減小查找時間
由上圖可以明白,查找到對應(yīng)盤塊記錄的次數(shù)為樹的高度,這也是它比二叉樹所在對優(yōu)勢,底的增大使得樹的高度減小,從而減小查詢次數(shù)。在此基礎(chǔ)上,發(fā)展出了B+樹,b*樹更加增加了數(shù)據(jù)的查找的性能。
●對與聚集索引,因為其為在空間中連續(xù)的存儲,所以最好不要進(jìn)行大量的修改和增刪操作。
b+樹在表單數(shù)據(jù)量非常龐大時會退化成遍歷查詢,所以在表單數(shù)據(jù)量變得很大的時候我們需要對表進(jìn)行分區(qū)操作,具體操作請看(http://www.cnblogs.com/AK2012/archive/2012/12/25/2012-1228.html)
●對于sql非聚集索引,采用的是另外一種為r樹的數(shù)據(jù)結(jié)構(gòu)。
下圖是Guttman論文中的一幅圖:
這里簡單解釋一下,對于空間中不規(guī)則的r8,用最小的矩形來把它包裹住,而r9,r10也是相同的道理。r8,r9,r10的頁節(jié)點(diǎn)為r3,為葉子節(jié)點(diǎn)的最小包裹矩形,依次類推。
r樹很好的解決了不連貫的空間存儲如何查找的問題,但是擴(kuò)展到6維以上會導(dǎo)致r樹性能下降退化為遍歷操作(我不是很明白這點(diǎn))
非聚集索引使用指針來表達(dá)對應(yīng)的區(qū)塊,使得修改操作變得容易很多。
3.優(yōu)化
3.1
對于sql語句編譯和解析器來說,
select ** from dual *
SELECT * FROM dual
為不同的語句,這樣在編譯的時候會增加編譯的時間,所以在寫sql語句的時候盡聊使用規(guī)范化的語句,保證一致性。
**select * from orderheader where changetime >'2010-10-20 00:00:01'
**select * from orderheader where changetime >'2010-09-22 00:00:01'
以上兩句語句,查詢優(yōu)化器認(rèn)為是不同的SQL語句,需要解析兩次。如果采用綁定變量
select * from orderheader where changetime >@chgtime
3.2
有的時候會需要進(jìn)行一些模糊查詢比如
select*from contact where username like ‘%yue%’
關(guān)鍵詞%yue%,由于yue前面用到了“%”,索引這時候?qū)嵭ql引擎便會遍歷全表,除非必要,否則不要在關(guān)鍵詞前加%。盡量使用yue%等確定的前綴
3.3
綁定變量在php和java對數(shù)據(jù)庫操作的時候很常見,我在查找python對次此采取的操作時并沒有查到對應(yīng)的方法。
先給sql引擎發(fā)送一個sql語句,后面使用綁定變量,引擎中語句緩存會識別語句從而減少解析和編譯的時間。
我看到一篇blog中寫的批量執(zhí)行語句時已經(jīng)對綁定變量做了優(yōu)化的。(MySQLdb中的cursor.executemany我懷疑并沒有做對應(yīng)的優(yōu)化策略)
3.4盡量少使用or語句,在執(zhí)行or語句的時候,sql引擎會放棄索引查找的方式而使用遍歷查找,極大的增加了查詢的時間。
3.5
使用temp表單來減少對主表對查詢操作,減輕主表熱點(diǎn)程度。大大減少了程序執(zhí)行中“共享鎖”阻塞“更新鎖”,減少了阻塞,提高了并發(fā)性能。
3.6
SQL Server中一句SQL語句默認(rèn)就是一個事務(wù),在該語句執(zhí)行完成后也是默認(rèn)commit的。其實(shí),這就是begin tran的一個最小化的形式,好比在每句語句開頭隱含了一個begin tran,結(jié)束時隱含了一個commit。
有些情況下,我們需要顯式聲明begin tran,比如做“插、刪、改”操作需要同時修改幾個表,要求要么幾個表都修改成功,要么都不成功。begin tran 可以起到這樣的作用,它可以把若干SQL語句套在一起執(zhí)行,最后再一起commit。好處是保證了數(shù)據(jù)的一致性,但任何事情都不是完美無缺的。Begin tran付出的代價是在提交之前,所有SQL語句鎖住的資源都不能釋放,直到commit掉。
可見,如果Begin tran套住的SQL語句太多,那數(shù)據(jù)庫的性能就糟糕了。在該大事務(wù)提交之前,必然會阻塞別的語句,造成block很多。
Begin tran使用的原則是,在保證數(shù)據(jù)一致性的前提下,begin tran 套住的SQL語句越少越好!有些情況下可以采用觸發(fā)器同步數(shù)據(jù),不一定要用begin tran。
3.7
SQL Server 表連接的三種方式(參考鏈接地址:http://www.cnblogs.com/ATree/archive/2011/02/13/sql_optimize_1.html)
(1) Merge Join
(2) Nested Loop Join
(3) Hash Join
SQL Server 2000只有一種join方式——Nested Loop Join,如果A結(jié)果集較小,那就默認(rèn)作為外表,A中每條記錄都要去B中掃描一遍,實(shí)際掃過的行數(shù)相當(dāng)于A結(jié)果集行數(shù)x B結(jié)果集行數(shù)。所以如果兩個結(jié)果集都很大,那Join的結(jié)果很糟糕。
SQL Server 2005新增了Merge Join,如果A表和B表的連接字段正好是聚集索引所在字段,那么表的順序已經(jīng)排好,只要兩邊拼上去就行了,這種join的開銷相當(dāng)于A表的結(jié)果集行數(shù)加上B表的結(jié)果集行數(shù),一個是加,一個是乘,可見merge join 的效果要比Nested Loop Join好多了。
如果連接的字段上沒有索引,那SQL2000的效率是相當(dāng)?shù)偷模鳶QL2005提供了Hash join,相當(dāng)于臨時給A,B表的結(jié)果集加上索引,因此SQL2005的效率比SQL2000有很大提高,我認(rèn)為,這是一個重要的原因。
總結(jié)一下,在表連接時要注意以下幾點(diǎn):
(1) 連接字段盡量選擇聚集索引所在的字段
(2) 仔細(xì)考慮where條件,盡量減小A、B表的結(jié)果集
4
在物理層面上
●增加磁盤的轉(zhuǎn)速(比較難以實(shí)現(xiàn))
●橫向擴(kuò)展和縱向擴(kuò)展(由于硬件設(shè)備價格的持續(xù)下降,很多都會采用這樣的方式)
使用橫向擴(kuò)展,用負(fù)載均衡設(shè)備(h5)或者是計算機(jī)(使用負(fù)載均衡軟件Ngix等)對大量sql語句請求做分流可以大大減輕單個設(shè)備的壓力。
縱向擴(kuò)展不太了解,先暫時放一放(==)
●讀寫分離策略
實(shí)際上大量的sql請求為讀請求,在一臺master機(jī)器上執(zhí)行update/insert操作,而在多臺slave機(jī)器上執(zhí)行讀請求也可以降低設(shè)備壓力。master把更后的表單再 copy給slave。