mysql索引和優(yōu)化

總結(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。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 本文轉(zhuǎn)自美團(tuán)技術(shù)團(tuán)隊感謝提供這么棒的文章 MySQL憑借著出色的性能++、低廉的成本、豐富的資源,已經(jīng)成為絕大多數(shù)...
    抓兔子的貓閱讀 611評論 0 10
  • 原文鏈接:MySQL索引背后的數(shù)據(jù)結(jié)構(gòu)及算法原理 本文以MySQL數(shù)據(jù)庫為研究對象,討論與數(shù)據(jù)庫索引相關(guān)的一些話題...
    加油小杜閱讀 882評論 0 8
  • 人們也許會長久的凝視它們,甚至在一百年后,帶著渴念追憶它。 ——致我們年少的小秘...
    綠羽閱讀 782評論 4 12
  • SEO知識: SEO運(yùn)營:把網(wǎng)站或者自己的產(chǎn)品推廣出去,SEO主要做的事情就是當(dāng)用戶在百度搜索一個關(guān)鍵詞的時候,可...
    sheepmiee閱讀 256評論 0 2
  • 痛苦,是保持清醒的最好方式——范增 你既然已經(jīng)做出了選擇,又何必去問為什么選擇——衛(wèi)莊 星星為什么看起來如此渺小,...
    V逐夢少年閱讀 1,691評論 0 10