天天看點

【mysql資料庫001】mysql資料庫的邏輯架構

MYSQL伺服器邏輯架構圖

【mysql資料庫001】mysql資料庫的邏輯架構

 最上層的服務并不是MYSQL所獨有的,大多數基于網絡的用戶端/伺服器的工具或者服務都有類似的架構。例如:連結處理、授權認證、安全等等。

第二層架構MYSQL的核心宮鞥你都再這一層,包括查詢解析、分析、優化、緩存以及所有的内置函數(例如、日期、時間)所有跨存儲引起的功能都在這一層實作:存儲過程、觸發器、視圖

第三層包含了存儲引擎。存儲引擎負責MYSQL中書籍的存儲和提取。MYSQL支援多種存儲引擎,每個存儲引擎都有它的優勢和劣勢。伺服器通過API與存儲引擎進行通信。這些接口屏蔽了不同存儲引擎之間的差異,使得這些差異對上層的查詢過程透明。

1.1連接配接管理與安全性

      每個用戶端連接配接都會在伺服器程序中擁有一個線程,這個連接配接的查詢值會在這個單獨的線程中執行,該線程隻能輪流在某個CPU核心或CPU中運作。伺服器會負責緩存線程,是以不需要為每一個建立的連接配接建立或銷毀線程。

     1.2優化與執行

MYSQL會解析查詢,并建立内部資料結構(解析樹),然後對其進行各種優化,包括重寫查詢,決定表的讀寫順序,以及選擇合适的索引等。使用者看奕通過特殊的關鍵字提示(hint)優化器,影響它的決策過程。也可以請求優化器解釋(explain) 。優化過程的各個因素。(标紅色的應用很多,很多SQL的優化,索引的排查都可以用它)。使使用者可以知道伺服器是如何進行優化決策的,并提供一個參考基準,便于使用者重構查詢和schema,修改相關配置,使應用極可能高效運作。

1.3 并發控制

無論何時,隻要有多個查詢需要在同一時刻修改資料,都會産生并發控制的問題。

此次隻分析兩個層面:1.伺服器層面 ;2.存儲引擎層面

Mysql是如何控制并發讀寫?

1.3.1讀寫鎖

通用的理論,讀 -讀 可以共享;讀-寫 互斥;寫-寫互斥

專業術語叫 :共享鎖(share lock),排他鎖(exclusive lock);

 Java語言裡面也有大量的鎖,也有各種前輩為了提升性能做出的各種不懈的努力。從大的方面這些鎖的優化方向

 可以從鎖的粒度來進行。

1.3.2鎖粒度

一種提高共享資源并發性的方式就是讓鎖定對象更有選擇性。盡量隻鎖定需要修改的部分資料,而不是所有的資源。更理想的方式是,隻對會修改的資料片進行精确的鎖定。任何時候,在給定的資源上,鎖定的資料量越少,則系統的并發度越高,隻要互相間不發生沖突即可。

加鎖也是需要耗費性能的,選擇合适 的鎖政策,就是在鎖的開銷和資料的安全之間尋求平衡,這種平衡當然也會影響到性能。大多數商業資料庫系統沒有提供更多的選擇,一般都是在表上面施加行鎖(row-level lock ).并以各種複雜的方式來實作,以便在鎖比較多的情況下盡可能的提供更好的性能。

表鎖

表鎖是MYSQL中最基本的鎖政策,并且是開銷最小的政策。它會鎖定整張表。一個使用者在對表進行寫操作(插入,删出,更新等)前,需要先獲得寫鎖,這會阻塞其他使用者對該表的讀寫操作。隻有沒有讀寫鎖時,其他讀取 的使用者才能獲得讀鎖,讀鎖之間是不互相阻塞的。

在特定的場景中,表鎖也可能有良好的性能。例如:READ LOCAL 表鎖支援某些類型的并發寫操作。另外,寫鎖也比讀鎖具有更高的優先級,是以一個寫鎖請求可能會被插入到讀鎖的隊列的前面(寫鎖可以插入到鎖隊列中讀鎖的前面,反之讀鎖則不能查到寫鎖的前面)

盡管存儲引擎也可以管理自己的鎖,MYSQl 本身還是會使用各種有效的表鎖來實作不同的目的。例如:伺服器會為諸如 ALTER TABLE 之類的語句使用表鎖,而忽略存儲引擎的鎖機制。

行級鎖

行級鎖可以最大程度的支援并發處理(同僚也帶來了最大的鎖開銷)。中所周知,在InnoDB中實作了行級鎖。行級鎖隻在存儲引擎層面實作,而MYSQL伺服器層,沒有實作。伺服器層完全不了解存儲引擎中的鎖實作。

1.4事務

事務就是一組原子性的SQL操作,如果資料庫引擎能夠成功的對資料庫應用該組操作語句,那麼就執行該組操作。如果其中有任何一條語句因為崩潰或者其他原因無法執行,那麼所有的語句都不會執行。也就是說,事務内的語句,要麼全執行成功,要麼全執行失敗。

談起資料庫事務就繞不開ACID特性 

 原子性(atomictity)

 一緻性(consistency)

隔離性(isolation)

持久性(durability)

隔離級别

在SQL标準中定義了4中隔離級别,每一種級别都規定了一個事務中所做的修改,哪些在事務内和事務間是可見的,哪些是不可見的。較低級别的隔離通常可以執行更高的并發,系統的開銷也更低。

REDA_UNCOMMITTED(未送出讀)

在READ_UNCOMMITED 級别,事務中的修改,即使沒有送出,對其他事務也都是可見的。事務可以讀取未送出的資料,這也被稱為髒讀(Dirty Read)。這個級别會導緻很多問題,從性能上來講,不會比其他的級别好太多,但卻缺乏其他的級别的很多好處,除非真有非常必要的理由,在實際應用中,一般很少使用。

READ_COMMITTED(讀已送出)

大多數資料庫系統的預設隔離級别都是READ_COMMITED(但MYSQL不是)。它滿足前面提到的隔離性的簡單定義:一個事務開始時,隻能看到已經送出的事務所作的修改。換句話說,一個事務從開始直到送出之前,所做的的任何修改對其他事務都是不可見的。這個級别有時候也叫做不可重複讀(noreapeatable read),因為兩次執行相同的查詢,可能會得到不一樣的結果。

REPEATABLE_READ(可重複度)

此解決了髒讀的問題。該級别保證了在同一個事務中多次讀取同樣記錄的結果是一緻的。但是理論上,可重複讀隔離級别還是無法解決另外一個幻讀記錄的結果是一緻的。但是理論上,可重複讀隔離級别還是無法解決另外一個幻讀的問題。所謂幻讀,指的是當某個事務在讀取某個範圍内的記錄時,另外一個事務又在該範圍内插入了新的記錄,當之前的事務再次讀取該範圍的記錄時,會産生幻行(Phantom Row). 的問題。InnoDB 存儲引擎通過多版本并發控制(MVCC)  muti version Concurrency Controller 解決了幻讀的問題。

可重複讀似乎MYSQL預設的事務隔離級别。

SERAILIZABLE(可串行化)

串行化是最高的隔離級别。它通過強制事務串行執行,避免了前面說的幻讀的問題。簡單來說,串行化會在去讀的每一行上都加上鎖,是以可能導緻大量的逾時和鎖争用的問題。實際應用中也很少用到這個隔離級别。隻有在非常需要確定資料的一緻性而且可以接受沒有并發的情況下,才考慮采用該級别。

1.4.1死鎖

死鎖是指兩個或多個事務在同一資源上互相占用,并請求鎖定對方占用的資源,進而導緻惡性循環的現象。最常見的有當多個事務試圖以不同的順序鎖定資源的時候,就可能會産生死鎖。多個事務同時鎖定同一個資源,也會産生死鎖。

最常見的即使 A -B  和B-A 問題

即 執行 時 事務1 鎖定了A ,等待鎖定B的時候 ,事務2 先鎖定了B ,然後嘗試去擷取A的鎖,這樣就會産生死鎖。

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

1.4.2事務日志

事務日志可以幫助提高事務的效率。使用事務日志,存儲引擎在修改表的資料時隻需要修改其記憶體拷貝,再把該修改行為記錄到持久的磁盤上的事務日志中,而不用每次都将修改的資料本身持久化到磁盤上。事務日志采用的是追假的方式,是以寫日志的操作是磁盤上一小塊區域内的順序I/O,而不像是随機I/O需要再磁盤的多個地方來回移動磁頭,是以采用事務日志的方式相對來說要快的多。事務日志持久化以後,記憶體中被修改的資料再背景可以慢慢的刷回到磁盤。目前大多資料存儲引擎都是這樣實作的,我們通常稱之為預寫式日志(Write-Ahead Logging),修改資料需要寫兩次磁盤(寫記錄檔,和寫資料)。

如果資料的修改已經記錄到事務日志并持久化,但資料本身還沒有寫回磁盤,此時系統崩潰,存儲引擎在重新開機時候能夠自動恢複這部分修改的資料。具體的恢複方式視存儲引擎而定。

這種思路 是結合了 硬碟的特點和作業系統的特性,大多數存儲類的系統都有此設計。 閱讀Rocket源碼的時候發現裡面也有這種設計思路。日志檔案順序寫,性能比記憶體差不了太多,然後在異步處理資料變更。

具體想深入研究資料庫可搞本書看看

本文參考《高性能MYSQL第3版》

繼續閱讀