MySQL架構

巴扎黑
巴扎黑原創
2017-06-23 14:50:581059瀏覽

重溫《高效能MySQL》的第一章MySQL架構與歷史

1.1   MySQL邏輯架構

參考

 

 

圖1-1:MySQL伺服器邏輯架構圖

 

  最上層的服務並不是MySQL所獨有的,大多數基於網路的客戶端/伺服器的工具或服務都有類似的架構。例如連線處理、授權認證、安全性等等。

  第二層架構是MySQL比較有趣的部分。大多數MySQL的核心服務功能都在這一層,包括查詢解析、分析、最佳化、快取以及所有的內建函數(例如,日期、時間、數學和加密函數),所有跨儲存引擎的功能都在這一層實作:預存程序、觸發器、視圖等。

  第三層包含了儲存引擎。儲存引擎負責MySQL中資料的儲存和提取。和GNU/Linux下的各種檔案系統一樣,每個儲存引擎都有它的優點和缺點。伺服器透過API與儲存引擎通訊。這些介面屏蔽了不同儲存引擎之間的差異,使得這些差異對上層的查詢流程變得透明。儲存引擎API包含數十個底層函數,用於執行諸如「開始一個交易」或「根據主鍵提取一行記錄」等操作。但儲存引擎不會去解析SQL,不同儲存引擎之間也不會互相通信,而只是簡單地回應上層伺服器的請求。

1.2   並發控制

1.2.1  讀寫鎖定

  這兩種類型的鎖定通常稱為共享鎖定(shared lock)和排他鎖定(exclusive lock) ,也叫讀鎖(read lock)和寫鎖(write lock)。讀鎖是共享的,或者說是互相不阻塞的。多個客戶在同一時刻可以同時讀取同一個資源,而互不干擾。寫鎖則是排他的,也就是說一個寫鎖會阻塞其他的寫鎖和讀鎖。

1.2.2 鎖定粒度

  兩個最重要的鎖定策略:表鎖定與行級鎖定

表格鎖定(table lock)

#  表鎖是MySQL中最基本的鎖定策略,也是開銷最小的策略。它會鎖定整張表。一個使用者在對錶進行寫入操作(插入、刪除、更新等)前,需要先取得寫鎖,這會阻塞其他使用者對該表的所有讀寫操作。只有沒有寫鎖時,其他讀取的使用者才能獲得讀鎖,讀鎖之間是不互相阻塞的。

  在特定的場景中,表鎖也可能有良好的效能。例如,READ LOCAL表鎖支特某些類型的並發寫入操作。另外,寫鎖也比讀鎖有更高的優先權,因此一個寫鎖請求可能會被插入到讀鎖佇列的前面(寫鎖可以插入到鎖佇列中讀鎖的前面,反之讀鎖則不能插入到寫鎖的前面)。

行級鎖定( row lock)   

  行級鎖定可以最大程度地支援並發處理(同時也帶來了最大的鎖開銷)。眾所周知,在InnoDB和XtraDB,以及其他一些儲存引擎中實現了行級鎖定。行級鎖只在儲存引擎層實現,而MySQL伺服器層沒有實現。伺服器層完全不了解儲存引擎中的鎖實作。

1.3   事務

事務支援ACID原則。

原子性(atomicity)

  一個交易必須被視為一個不可分割的最小工作單元。

一致性(consistency) 

  資料庫總是從一個一致性的狀態轉換到另一個一致性的狀態。

隔離性(isolation)

  通常來說,一個事務所所做的修改在最終提交以前,對其他事務是不可見的。

持久性(durability)

  一旦交易提交,則其所做的修改就會永久保存到資料庫中。

1.3.1  隔離等級

以下簡單介紹四種隔離等級。

READ UNCOMMITTED(未提交讀取)

  在READ UNCOMMITTED級別,事務中的修改,即使沒有提交,對其他事務也都是可見的。事務可以讀取未提交的數據,這也被稱為髒讀(Dirty Read)。這個等級會導致很多問題,從效能上來說,READ UNCOMMITTED不會比其他的等級好太多,但卻缺乏其他等級的許多好處,除非真的有非常必要的理由,在實際應用中一般很少使用。

READ COMMITTED(提交讀取)

  大多數資料庫系統的預設隔離等級都是READ COMMITTED(但MySQL不是)。一個事務從開始直到提交之前,所做的任何修改對其他事務都是不可見的。這個等級有時候也叫做不可重複讀(nonrepeatable read),因為兩次執行同樣的查詢,可能會得到不一樣的結果。

REPEATABLE READ(可重複讀取)

  REPEATABLE READ解決了髒讀取的問題。此等級保證了在同一個事務中多次讀取同樣記錄的結果是一致的。但理論上,可重複讀取隔離等級還是無法解決另一個幻讀 (Phantom Read)的問題。所謂幻讀,指的是當某個事務在讀取某個範圍內的記錄時,另外一個事務又在該範圍內插入了新的記錄,當之前的事務再次讀取該範圍的記錄時,會產生幻行(Phantom Row)。 InnoDB和XtraDB儲存引擎透過多版本並發控制(MVCC,Multiversion Concurrency Control)解決了幻讀的問題。

  可重複讀取是MySQL的預設交易隔離等級。

SERIALIZABLE(可串列化​​)

  SERIALIZABLE是最高的隔離等級。它透過強制事務串列執行,避免了前面說的幻讀的問題。簡單來說,SERIALIZABLE會在謨取的每一行資料上都加鎖,所以可能會導致大量的逾時和鎖爭用的問題。實際應用中也很少用到這個隔離級別,只有在非常需要確保資料的一致性而且可以接受沒有並發的情況下,才考慮採用該級別。

 

1.3.2  死鎖

  死鎖是指兩個或多個事務在同一資源上相互佔用,並請求鎖定對方佔用的資源,從而導致惡性循環的現象。當多個事務試圖以不同的順序鎖定資源時,就可能會產生死鎖。多個事務同時鎖定同一個資源時,也會產生死鎖。

  為了解決這個問題,資料庫系統實作了各種死鎖偵測和死鎖逾時機制。越複雜的系統,例如InnoDB儲存引擎,越能偵測到死鎖的循環依賴,並立即回傳一個錯誤。這種解決方式很有效,否則死鎖會導致出現非常慢的查詢。還有一種解決方式,就是當查詢的時間達到鎖等待超時的設定後放棄鎖定請求,這種方式通常來說不太好。 InnoDB目前處理死鎖的方法是,將持有最少行級排他鎖的事務進行回滾(這是相對比較簡單的死鎖回滾演算法)。

  鎖的行為和順序是和儲存引擎相關的。以同樣的順序執行語句,有些儲存引擎會產生死鎖,有些則不會。死鎖的產生有雙重原因:有些是因為真正的資料衝突,這種情況通常很難避免,但有些則完全是由於儲存引擎的實現方式導致的。

1.3.3  交易日誌

  使用交易日誌,儲存引擎在修改表的資料時只需要修改其記憶體拷貝,再把該修改行為記錄到持久在硬碟上的交易日誌中,而不用每次都將修改的資料本身持久到磁碟。交易日誌採用的是追加的方式。交易日誌持久以後,記憶體中被修改的資料在後台可以慢慢地刷回到磁碟。目前大多數儲存引擎都是這樣實現的,我們通常稱為預寫式日誌(Write-Ahead Logging),而修改資料需要寫兩次磁碟。

  如果資料的修改已經記錄到交易日誌並且持久化,但資料本身還沒有寫回磁碟,此時系統崩潰,儲存引擎在重新啟動時能夠自動恢復這部分修改的資料。具體的恢復方式則視儲存引擎而定。

1.3.4  MySQL中的交易

1.4   多版本並發控制

  MVCC的實現,是透過保存資料在某個時間點的快照來實現的。也就是說,不管需要執行多長時間,每個事務看到的資料都是一致的。根據事務開始的時間不同,每個事務對同一張表,同一時刻看到的資料可能是不一樣的。下面我們透過InnoDB的簡化版行為來說明MVCC是如何運作的。

  InnoDB的MVCC,是透過在每行記錄後面保存兩個隱藏的列來實現的。這兩個列,一個保存了行的建立時間,一個保存行的過期時間(或刪除時間)。當然儲存的並不是實際的時間值,而是系統版本號(system version number)。每開始一個新的事務,系統版本號就會自動遞增。事務開始時刻的系統版本號碼會作為事務的版本號,用來和查詢到的每行記錄的版本號進行比較。以下來看看在REPEATABLE READ隔離等級下,MVCC具體是如何操作的。

SELECT

  InnoDB會依照下列兩個條件檢查每行記錄:

      a. InnoDB只查找版本早於目前事務版本的資料行(也就是,行的系統版本號小於或等於事務的系統版本號),這樣可以確保事務讀取的行,要么是在事務開始前已經存在的,要嘛是事務自身插入或修改過的。

      b.行的刪除版本要么未定義,要么大於目前事務版本號。這可以確保事務讀取到的行,在事務開始之前未被刪除。

      只有符合上述兩個條件的記錄,才能傳回作為查詢結果。

INSERT

        InnoDB為新插入的每一行儲存目前系統版本號碼作為行版本號。

DELETE

      InnoDB為刪除的每一行儲存目前系統版本號碼作為行刪除識別。

UPDATE

      InnoDB為插入一行新記錄,保存目前系統版本號作為行版本號,同時保存當前系統版本號到原來的行作為行刪除標識。

  保存這兩個額外系統版本號,使大多數讀取操作都可以不用加鎖。這樣設計使得讀取資料操作很簡單,效能很好,也能保證只會讀取到符合標準的行。不足之處是每行記錄都需要額外的儲存空間,需要做更多的行檢查工作,以及一些額外的維護工作。

  MVCC只在REPEATABLE READ和READ COMMITTED兩個隔離等級下運作。其他兩個隔離等級都和MVCC不相容於註4,因為READ UNCOMMITTED總是讀取最新的資料行,而不是符合目前交易版本的資料行。而SERIALIZABLE則會對所有讀取的行都加鎖。

1.5   MySQL的儲存引擎

  在檔案系統中,MySQL將每個資料庫(也可以稱為schema)儲存為資料目錄下的子目錄。建立表格時,MySQL會在資料庫子目錄下建立一個和表格同名的.frm檔案保存表的定義。例如建立一個名為MyTable的表,MySQL會在MyTable.frm檔案中儲存該表的定義。因為MySQL使用檔案系統的目錄和檔案來保存資料庫和表格的定義,大小寫敏感度和具體的平台密切相關。在Windows中,大小寫是不敏感的;而在類別Unix中則是敏感的。不同的儲存引擎保存資料和索引的方式是不同的,但表的定義則是在MySQL服務層統一處理的。

  可以使用SHOW TABLE STATUS指令(在MySQL 5.0以後的版本中,也可以查詢INFORMATION SCHEMA中對應的表)顯示表格的相關資訊。例如,對於mysql資料庫中的user表:

mysql> SHOW TABLE STATUS LIKE 'user' \G

          Name: user

#  Row_format: Dynamic

           Rows :  6

  Avg_row_length: 59

   #     Index length : 2048

       Data_free: 0   

  Auto_increment: NULL

##     Create_time: 2002-

##     Create_time: 2002-

##     Create_time: 2002-

##     Create_time: 2002-01024 18 01- 24  21: 56 : 29

      Check_time: NULL

        Collat​​ion : ut f8_bin# 

  ##          Comment: Users and global privileges

1 row in set (o.oo sec)

 

榆出的結果表明,這是一個MyISAM表。輸出中還有很多其他資訊以及統計資料。下面簡單介紹一下每一行的含義。

Name

表名。

Engine表的儲存引擎類型。在舊版本中,該列的名字叫Type,而不是Engine。 Row- format#行的格式。對於MyISAM表,可選的值為Dynamic、Fixed或Comp ressed。 Dynamic的行長度是可變的,一般包含可變長度的字段,如VARCHAR或BLOB。 Fixed的行長度則是固定的,只包含固定長度的列,如CHAR和INTEGER。 Compressed的行則只存在於壓縮表中。 Rows#表中的行數。對於MyISAM和其他一些儲存引擎,該值是精確的,但對於InnoDB,該值是估計值。 Avg_ row_length

平均每行包含的位元組數。

Data_length

#表格資料的大小(以位元組為單位)。

Max- data_length

#表格資料的最大容量,該值和儲存引擎有關。

Index_length

#索引的大小(以位元組為單位)。

Data_free

#對於MyISAM表,表示已指派但目前沒有使用的空間。這部分空間包括了先前刪除的行,以及後續可以被INSERT利用的空間。

Auto_increment

#下一個AUTO INCREMENT的值。

Create_time

#表的建立時間。

Update_time

#表格資料的最後修改時間。

Check_ time

#使用CKECK TABLE指令或myisamchk工具最後一次檢查表的時間。

Collat​​ion

表的預設字元集和字元列排序規則。

Checksum

#如果啟用,則儲存的是整個資料表的即時校驗和。

Create_options

#刨建表時指定的其他選項。

Comment

#該欄位包含了一些其他的額外資訊。對於MyISAM表,儲存的是表在建立時所帶的註解。對於InnoDB表,則保存的是InnoDB表空間的剩餘空間資訊。如果是視圖,則該列包含「VIEW」的文字字樣。

 

1.6   MySQL時間軸

1.7   MySQL的發展模式

 

參考:《高效能MySQL》 

以上是MySQL架構的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn