溫習(xí)《高性能mysql》的第一章 mysql架構(gòu)與歷史
1.1?? MySQL邏輯架構(gòu)
參考
?
?
圖1-1:MySQL服務(wù)器邏輯架構(gòu)圖
?
最上層的服務(wù)并不是MySQL所獨(dú)有的,大多數(shù)基于網(wǎng)絡(luò)的客戶端/服務(wù)器的工具或者服務(wù)都有類似的架構(gòu)。比如連接處理、授權(quán)認(rèn)證、安全等等。
第二層架構(gòu)是MySQL比較有意思的部分。大多數(shù)MySQL的核心服務(wù)功能都在這一層,包括查詢解析、分析、優(yōu)化、緩存以及所有的內(nèi)置函數(shù)(例如,日期、時(shí)間、數(shù)學(xué)和加密函數(shù)),所有跨存儲(chǔ)引擎的功能都在這一層實(shí)現(xiàn):存儲(chǔ)過程、觸發(fā)器、視圖等。
第三層包含了存儲(chǔ)引擎。存儲(chǔ)引擎負(fù)責(zé)MySQL中數(shù)據(jù)的存儲(chǔ)和提取。和GNU/Linux下的各種文件系統(tǒng)一樣,每個(gè)存儲(chǔ)引擎都有它的優(yōu)勢(shì)和劣勢(shì)。服務(wù)器通過API與存儲(chǔ)引擎進(jìn)行通信。這些接口屏蔽了不同存儲(chǔ)引擎之間的差異,使得這些差異對(duì)上層的查詢過程透明。存儲(chǔ)引擎API包含幾十個(gè)底層函數(shù),用于執(zhí)行諸如“開始一個(gè)事務(wù)”或者“根據(jù)主鍵提取一行記錄”等操作。但存儲(chǔ)引擎不會(huì)去解析SQL,不同存儲(chǔ)引擎之間也不會(huì)相互通信,而只是簡(jiǎn)單地響應(yīng)上層服務(wù)器的請(qǐng)求。
1.2?? 并發(fā)控制
1.2.1 ?讀寫鎖
這兩種類型的鎖通常被稱為共享鎖(shared lock)和排他鎖(exclusive lock),也叫讀鎖(read lock)和寫鎖(write lock)。讀鎖是共享的,或者說是相互不阻塞的。多個(gè)客戶在同一時(shí)刻可以同時(shí)讀取同一個(gè)資源,而互不干擾。寫鎖則是排他的,也就是說一個(gè)寫鎖會(huì)阻塞其他的寫鎖和讀鎖。
1.2.2 鎖粒度
兩種最重要的鎖策略:表鎖和行級(jí)鎖
表鎖(table lock)
表鎖是MySQL中最基本的鎖策略,并且是開銷最小的策略。它會(huì)鎖定整張表。一個(gè)用戶在對(duì)表進(jìn)行寫操作(插入、刪除、更新等)前,需要先獲得寫鎖,這會(huì)阻塞其他用戶對(duì)該表的所有讀寫操作。只有沒有寫鎖時(shí),其他讀取的用戶才能獲得讀鎖,讀鎖之間是不相互阻塞的。
在特定的場(chǎng)景中,表鎖也可能有良好的性能。例如,READ LOCAL表鎖支特某些類型的并發(fā)寫操作。另外,寫鎖也比讀鎖有更高的優(yōu)先級(jí),因此一個(gè)寫鎖請(qǐng)求可能會(huì)被插入到讀鎖隊(duì)列的前面(寫鎖可以插入到鎖隊(duì)列中讀鎖的前面,反之讀鎖則不能插入到寫鎖的前面)。
行級(jí)鎖( row lock)???
行級(jí)鎖可以最大程度地支持并發(fā)處理(同時(shí)也帶來了最大的鎖開銷)。眾所周知,在InnoDB和XtraDB,以及其他一些存儲(chǔ)引擎中實(shí)現(xiàn)了行級(jí)鎖。行級(jí)鎖只在存儲(chǔ)引擎層實(shí)現(xiàn),而MySQL服務(wù)器層沒有實(shí)現(xiàn)。服務(wù)器層完全不了解存儲(chǔ)引擎中的鎖實(shí)現(xiàn)。
1.3?? 事務(wù)
事務(wù)支持ACID原則。
原子性(atomicity)
一個(gè)事務(wù)必須被視為一個(gè)不可分割的最小工作單元。
一致性(consistency)?
數(shù)據(jù)庫總是從一個(gè)一致性的狀態(tài)轉(zhuǎn)換到另外一個(gè)一致性的狀態(tài)。
隔離性(isolation)
通常來說,一個(gè)事務(wù)所做的修改在最終提交以前,對(duì)其他事務(wù)是不可見的。
持久性(durability)
一旦事務(wù)提交,則其所做的修改就會(huì)永久保存到數(shù)據(jù)庫中。
1.3.1 ?隔離級(jí)別
下面簡(jiǎn)單地介紹一下四種隔離級(jí)別。
READ UNCOMMITTED(未提交讀)
在READ UNCOMMITTED級(jí)別,事務(wù)中的修改,即使沒有提交,對(duì)其他事務(wù)也都是可見的。事務(wù)可以讀取未提交的數(shù)據(jù),這也被稱為臟讀(Dirty Read)。這個(gè)級(jí)別會(huì)導(dǎo)致很多問題,從性能上來說,READ UNCOMMITTED不會(huì)比其他的級(jí)別好太多,但卻缺乏其他級(jí)別的很多好處,除非真的有非常必要的理由,在實(shí)際應(yīng)用中一般很少使用。
READ COMMITTED(提交讀)
大多數(shù)數(shù)據(jù)庫系統(tǒng)的默認(rèn)隔離級(jí)別都是READ COMMITTED(但MySQL不是)。一個(gè)事務(wù)從開始直到提交之前,所做的任何修改對(duì)其他事務(wù)都是不可見的。這個(gè)級(jí)別有時(shí)候也叫做不可重復(fù)讀(nonrepeatable read),因?yàn)閮纱螆?zhí)行同樣的查詢,可能會(huì)得到不一樣的結(jié)果。
REPEATABLE READ(可重復(fù)讀)
REPEATABLE READ解決了臟讀的問題。該級(jí)別保證了在同一個(gè)事務(wù)中多次讀取同樣記錄的結(jié)果是一致的。但是理論上,可重復(fù)讀隔離級(jí)別還是無法解決另外一個(gè)幻讀 (Phantom Read)的問題。所謂幻讀,指的是當(dāng)某個(gè)事務(wù)在讀取某個(gè)范圍內(nèi)的記錄時(shí),另外一個(gè)事務(wù)又在該范圍內(nèi)插入了新的記錄,當(dāng)之前的事務(wù)再次讀取該范圍的記錄時(shí),會(huì)產(chǎn)生幻行(Phantom Row)。InnoDB和XtraDB存儲(chǔ)引擎通過多版本并發(fā)控制(MVCC,Multiversion Concurrency Control)解決了幻讀的問題。
可重復(fù)讀是MySQL的默認(rèn)事務(wù)隔離級(jí)別。
SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔離級(jí)別。它通過強(qiáng)制事務(wù)串行執(zhí)行,避免了前面說的幻讀的問題。簡(jiǎn)單來說,SERIALIZABLE會(huì)在謨?nèi)〉拿恳恍袛?shù)據(jù)上都加鎖,所以可能導(dǎo)致大量的超時(shí)和鎖爭(zhēng)用的問題。實(shí)際應(yīng)用中也很少用到這個(gè)隔離級(jí)別,只有在非常需要確保數(shù)據(jù)的一致性而且可以接受沒有并發(fā)的情況下,才考慮采用該級(jí)別。
?
1.3.2 ?死鎖
死鎖是指兩個(gè)或者多個(gè)事務(wù)在同一資源上相互占用,并請(qǐng)求鎖定對(duì)方占用的資源,從而導(dǎo)致惡性循環(huán)的現(xiàn)象。當(dāng)多個(gè)事務(wù)試圖以不同的順序鎖定資源時(shí),就可能會(huì)產(chǎn)生死鎖。多個(gè)事務(wù)同時(shí)鎖定同一個(gè)資源時(shí),也會(huì)產(chǎn)生死鎖。
為了解決這種問題,數(shù)據(jù)庫系統(tǒng)實(shí)現(xiàn)了各種死鎖檢測(cè)和死鎖超時(shí)機(jī)制。越復(fù)雜的系統(tǒng),比如InnoDB存儲(chǔ)引擎,越能檢測(cè)到死鎖的循環(huán)依賴,并立即返回一個(gè)錯(cuò)誤。這種解決方式很有效,否則死鎖會(huì)導(dǎo)致出現(xiàn)非常慢的查詢。還有一種解決方式,就是當(dāng)查詢的時(shí)間達(dá)到鎖等待超時(shí)的設(shè)定后放棄鎖請(qǐng)求,這種方式通常來說不太好。InnoDB目前處理死鎖的方法是,將持有最少行級(jí)排他鎖的事務(wù)進(jìn)行回滾(這是相對(duì)比較簡(jiǎn)單的死鎖回滾算法)。
鎖的行為和順序是和存儲(chǔ)引擎相關(guān)的。以同樣的順序執(zhí)行語句,有些存儲(chǔ)引擎會(huì)產(chǎn)生死鎖,有些則不會(huì)。死鎖的產(chǎn)生有雙重原因:有些是因?yàn)檎嬲臄?shù)據(jù)沖突,這種情況通常很難避免,但有些則完全是由于存儲(chǔ)引擎的實(shí)現(xiàn)方式導(dǎo)致的。
1.3.3 ?事務(wù)日志
使用事務(wù)日志,存儲(chǔ)引擎在修改表的數(shù)據(jù)時(shí)只需要修改其內(nèi)存拷貝,再把該修改行為記錄到持久在硬盤上的事務(wù)日志中,而不用每次都將修改的數(shù)據(jù)本身持久到磁盤。事務(wù)日志采用的是追加的方式。事務(wù)日志持久以后,內(nèi)存中被修改的數(shù)據(jù)在后臺(tái)可以慢慢地刷回到磁盤。目前大多數(shù)存儲(chǔ)引擎都是這樣實(shí)現(xiàn)的,我們通常稱之為預(yù)寫式日志(Write-Ahead Logging),修改數(shù)據(jù)需要寫兩次磁盤。
如果數(shù)據(jù)的修改已經(jīng)記錄到事務(wù)日志并持久化,但數(shù)據(jù)本身還沒有寫回磁盤,此時(shí)系統(tǒng)崩潰,存儲(chǔ)引擎在重啟時(shí)能夠自動(dòng)恢復(fù)這部分修改的數(shù)據(jù)。具體的恢復(fù)方式則視存儲(chǔ)引擎而定。
1.3.4 ?MySQL中的事務(wù)
1.4?? 多版本并發(fā)控制
MVCC的實(shí)現(xiàn),是通過保存數(shù)據(jù)在某個(gè)時(shí)間點(diǎn)的快照來實(shí)現(xiàn)的。也就是說,不管需要執(zhí)行多長(zhǎng)時(shí)間,每個(gè)事務(wù)看到的數(shù)據(jù)都是一致的。根據(jù)事務(wù)開始的時(shí)間不同,每個(gè)事務(wù)對(duì)同一張表,同一時(shí)刻看到的數(shù)據(jù)可能是不一樣的。下面我們通過InnoDB的簡(jiǎn)化版行為來說明MVCC是如何工作的。
InnoDB的MVCC,是通過在每行記錄后面保存兩個(gè)隱藏的列來實(shí)現(xiàn)的。這兩個(gè)列,一個(gè)保存了行的創(chuàng)建時(shí)間,一個(gè)保存行的過期時(shí)間(或刪除時(shí)間)。當(dāng)然存儲(chǔ)的并不是實(shí)際的時(shí)間值,而是系統(tǒng)版本號(hào)(system version number)。每開始一個(gè)新的事務(wù),系統(tǒng)版本號(hào)都會(huì)自動(dòng)遞增。事務(wù)開始時(shí)刻的系統(tǒng)版本號(hào)會(huì)作為事務(wù)的版本號(hào),用來和查詢到的每行記錄的版本號(hào)進(jìn)行比較。下面看一下在REPEATABLE READ隔離級(jí)別下,MVCC具體是如何操作的。
SELECT
? InnoDB會(huì)根據(jù)以下兩個(gè)條件檢查每行記錄:
??? a.InnoDB只查找版本早于當(dāng)前事務(wù)版本的數(shù)據(jù)行(也就是,行的系統(tǒng)版本號(hào)小于或等于事務(wù)的系統(tǒng)版本號(hào)),這樣可以確保事務(wù)讀取的行,要么是在事務(wù)開始前已經(jīng)存在的,要么是事務(wù)自身插入或者修改過的。
??? b.行的刪除版本要么未定義,要么大于當(dāng)前事務(wù)版本號(hào)。這可以確保事務(wù)讀取到的行,在事務(wù)開始之前未被刪除。
??? 只有符合上述兩個(gè)條件的記錄,才能返回作為查詢結(jié)果。
INSERT
? ????? InnoDB為新插入的每一行保存當(dāng)前系統(tǒng)版本號(hào)作為行版本號(hào)。
DELETE
??? InnoDB為刪除的每一行保存當(dāng)前系統(tǒng)版本號(hào)作為行刪除標(biāo)識(shí)。
UPDATE
??? InnoDB為插入一行新記錄,保存當(dāng)前系統(tǒng)版本號(hào)作為行版本號(hào),同時(shí)保存當(dāng)前系統(tǒng)版本號(hào)到原來的行作為行刪除標(biāo)識(shí)。
保存這兩個(gè)額外系統(tǒng)版本號(hào),使大多數(shù)讀操作都可以不用加鎖。這樣設(shè)計(jì)使得讀數(shù)據(jù)操作很簡(jiǎn)單,性能很好,并且也能保證只會(huì)讀取到符合標(biāo)準(zhǔn)的行。不足之處是每行記錄都需要額外的存儲(chǔ)空間,需要做更多的行檢查工作,以及一些額外的維護(hù)工作。
MVCC只在REPEATABLE READ和READ COMMITTED兩個(gè)隔離級(jí)別下工作。其他兩個(gè)隔離級(jí)別都和MVCC不兼容注4,因?yàn)镽EAD UNCOMMITTED總是讀取最新的數(shù)據(jù)行,而不是符合當(dāng)前事務(wù)版本的數(shù)據(jù)行。而SERIALIZABLE則會(huì)對(duì)所有讀取的行都加鎖。
1.5?? MySQL的存儲(chǔ)引擎
在文件系統(tǒng)中,MySQL將每個(gè)數(shù)據(jù)庫(也可以稱之為schema)保存為數(shù)據(jù)目錄下的一個(gè)子目錄。創(chuàng)建表時(shí),MySQL會(huì)在數(shù)據(jù)庫子目錄下創(chuàng)建一個(gè)和表同名的.frm文件保存表的定義。例如創(chuàng)建一個(gè)名為MyTable的表,MySQL會(huì)在MyTable.frm文件中保存該表的定義。因?yàn)镸ySQL使用文件系統(tǒng)的目錄和文件來保存數(shù)據(jù)庫和表的定義,大小寫敏感性和具體的平臺(tái)密切相關(guān)。在Windows中,大小寫是不敏感的;而在類Unix中則是敏感的。不同的存儲(chǔ)引擎保存數(shù)據(jù)和索引的方式是不同的,但表的定義則是在MySQL服務(wù)層統(tǒng)一處理的。
可以使用SHOW TABLE STATUS命令(在MySQL 5.0以后的版本中,也可以查詢INFORMATION SCHEMA中對(duì)應(yīng)的表)顯示表的相關(guān)信息。例如,對(duì)于mysql數(shù)據(jù)庫中的user表:
mysql> SHOW TABLE STATUS LIKE ‘user’ G
????????? Name: user
???????? Engine: MyISAM
?? ?Row_format: Dynamic
?????????? Rows :? 6
??Avg_row_length: 59
??? ??Data length: 356
Max data length: 4294967295
??? ?Index length: 2048
????? ?Data_free: 0???
??Auto_increment: NULL
??? ?Create_time: 2002-01-24 18:07:17
??? Update_time : 2002 -01-24? 21: 56 : 29
???? ?Check_time: NULL
????? ??Collation : ut f8_bin
??????? Checksum: NULL
? ?? Create_options :
??????? ??Comment: Users and global privileges
1 row in set (o.oo sec)
?
榆出的結(jié)果表明,這是一個(gè)MyISAM表。輸出中還有很多其他信息以及統(tǒng)計(jì)信息。下面簡(jiǎn)單介紹一下每一行的含義。
Name |
表名。 |
Engine |
表的存儲(chǔ)引擎類型。在舊版本中,該列的名字叫Type,而不是Engine。 |
Row- format |
行的格式。對(duì)于MyISAM表,可選的值為Dynamic、Fixed或者Comp ressed。Dynamic的行長(zhǎng)度是可變的,一般包含可變長(zhǎng)度的字段,如VARCHAR或BLOB。Fixed的行長(zhǎng)度則是固定的,只包含固定長(zhǎng)度的列,如CHAR和INTEGER。Compressed的行則只在壓縮表中存在。 |
Rows |
表中的行數(shù)。對(duì)于MyISAM和其他一些存儲(chǔ)引擎,該值是精確的,但對(duì)于InnoDB,該值是估計(jì)值。 |
Avg_ row_length |
平均每行包含的字節(jié)數(shù)。 |
Data_length |
表數(shù)據(jù)的大小(以字節(jié)為單位)。 |
Max- data_length |
表數(shù)據(jù)的最大容量,該值和存儲(chǔ)引擎有關(guān)。 |
Index_length |
索引的大小(以字節(jié)為單位)。 |
Data_free |
對(duì)于MyISAM表,表示已分配但目前沒有使用的空間。這部分空間包括了之前刪除的行,以及后續(xù)可以被INSERT利用到的空間。 |
Auto_increment |
下一個(gè)AUTO INCREMENT的值。 |
Create_time |
表的創(chuàng)建時(shí)間。 |
Update_time |
表數(shù)據(jù)的最后修改時(shí)間。 |
Check_ time |
使用CKECK TABLE命令或者myisamchk工具最后一次檢查表的時(shí)間。 |
Collation |
表的默認(rèn)字符集和字符列排序規(guī)則。 |
Checksum |
如果啟用,保存的是整個(gè)表的實(shí)時(shí)校驗(yàn)和。 |
Create_options |
刨建表時(shí)指定的其他選項(xiàng)。 |
Comment |
該列包含了一些其他的額外信息。對(duì)于MyISAM表,保存的是表在創(chuàng)建時(shí)帶的注釋。對(duì)于InnoDB表,則保存的是InnoDB表空間的剩余空間信息。如果是一個(gè)視圖,則該列包含“VIEW”的文本字樣。 |
?
1.6?? MySQL時(shí)間線
1.7?? MySQL的開發(fā)模式
?
?
參考:《高性能 MySQL》?