MySQL基本概念(1)

一、什麼是MySQL?

MySQL是現在市面上用的最廣的關係型數據庫。簡單理解也就是存放數據的倉庫。關於關係型數據庫和非關係型數據庫可以點擊這裏查看之前博客。

1、邏輯架構圖

最上層服務不是MySQL所獨有的,大多數基於網絡的客戶端/服務器都通過這種方式實現。其中每個客戶端連接都會在服務器進程中擁有一個線程,該客戶端的查詢操作在這個線程中執行,其中服務器會緩存線程,因此不需要對每一條客戶端連接都創建或銷燬線程。新版的MySQL提供了一個API,支持線程池插件,可以使用少量線程服務大量的客戶端連接。

第二層架構是MySQL最重要的一部分,MySQL的大多數功能如:解析、分析、優化、緩存、函數等等都在這一層實現。

第三層包括存儲引擎,存儲引擎負責數據的存儲和讀取。其中它通過API和服務器通信,不同存儲引擎之間不會相同通信,並且絕大多數存儲引擎不會解析SQL,InnoDB存儲引擎因爲需要解析外鍵,因此它可以解析SQL。

2、併發控制

任何時刻,只要有多個查詢在同一時刻修改數據,都會產生併發控制問題。MySQL分別在服務器層存儲引擎層保證併發。

2.1、鎖

爲了防止兩個線程同時操作同一塊資源,可以在一個線程執行過程中將這塊資源“鎖”住,被鎖住的資源只有自己可以操作,這樣可就可以保證併發的安全,這也就是鎖的意義。

2.1.1、讀寫鎖

從數據庫中讀取數據是不會產生併發問題的,即使同一時刻有多條線程讀取也不會什麼問題,因爲讀取不會修改數據,但是如果一個線程在讀取,另一個線程在修改,這樣就會產生無法控制的結果,可能讀取到修改前的數據,也可能讀取到修改後的數據。

解決這種問題其實也非常簡單,我們可以定義兩種類型的鎖:共享鎖和排他鎖,也可以稱爲讀鎖寫鎖。

  • 讀鎖是共享的,或者說相互不阻塞的,也就是說即使同時有多個線程讀取同一塊資源,也互不干擾
  • 寫鎖是排他的,或者說相互阻塞的,也就是說一個寫鎖會阻塞其他讀鎖和寫鎖

2.1.2、鎖策略

我們知道數據庫加鎖是需要額外耗費資源的,判斷是否加鎖,獲取鎖,釋放鎖等操作都是需要額外的開銷。鎖的粒度越大,這部分開銷越少,但是相對應系統的性能也就越差,鎖的粒度越小,鎖開銷也就越大,系統性能也就越好。

鎖策略就是在開銷和性能之間尋找一個平衡點。

MySQL提供多種鎖策略選擇。不同的鎖策略選擇會給系統帶來不同的性能,一般情況下,在不同的應用場景下使用不同的鎖策略以保證好的性能。現在有兩種比較流行的鎖策略:表鎖行級鎖

表鎖:表鎖是MySQL中最基本的鎖策略,並且是開銷最小的鎖。顧名思義,表鎖的處理方式爲:它會鎖定整張表,一個用戶對錶進行寫操作(插入、刪除、更新)前,需要先獲取寫鎖,這會阻止其他用戶對該表的讀寫操作。

行級鎖:行級鎖可以最大程度的支持併發處理(同時帶來大量的鎖開銷)。行級鎖的處理方式爲,它會鎖定當前用戶正在操作的那一行,保證同一時刻只有一個用戶可以操作這一行。行級鎖只在存儲引擎層實現,MySQL服務層沒有實現。其中InnoDB支持該鎖策略。

3、事務

事務就是一組原子性的SQL查詢,或者說一個獨自的工作單元。如果數據庫儲存引擎能夠成功執行該組查詢的所有SQL語句,那麼就執行該組查詢,如果其中一條語句因爲崩潰或其他原因無法執行,那麼所有語句都不會執行。也就是說:事務內的語句,要麼全部執成功,要麼全部執行失敗

3.1、ACID

ACID分別表示原子性(atomicity)、一致性(consistency)、隔離性(isolation)和持久性(duarbility),一個運行良好的事務必須具體這些標準特徵。

  • 原子性:一個事務必須被視爲不可分割的最小單元,整個事務要麼全部提交成功,要麼全部失敗回滾
  • 一致性:數據庫總是從一個一致性狀態轉移到另一個一致性狀態
  • 隔離性:一個事務所做的修改操作在最終提交之前,對其他事務是不可見的
  • 持久性:一旦事務提交,則其所做的修改就會永久性的保存在數據庫中,此時即使系統崩潰,修改數據也不會丟失

事務的ACID特性可以保證事務的正常運行。爲了滿足這些特性,數據庫需要更大的系統開銷。用戶可以根據業務是否需要事務處理,來選擇合適的存儲引擎,對於不需要事務的業務場景,不支持事務的存儲引擎會帶來更好的性能。即使存儲引擎不支持事務,用戶可以通過LOCK TABLES 語句爲應用提供一定程度的保存,這也正是MySQL存儲引擎可以發揮優勢的地方。

3.1.2、事務的隔離級別

事務的隔離性遠比想象中複雜。MySQL標準中定義了四種隔離級別,每一種級別都規定了一個事務的修改,哪些在事務內和事務間可見,哪些是不可見的。較低的隔離級別通常可以執行更高的併發,系統開銷也就越小,相應的系統安全性越低。常見的隔離級別有以下四種:

  • READ UNCOMMITTED(未提交讀):事務中的修改,即使沒有提交,對其他事務也是可見的。事務可以讀取其他事務未提交的數據,這也被稱爲“髒讀”。這個級別會導致很多問題,並且性能提升不會很大,一般很少應用。
  • READ COMMITTED(提交讀):大多數數據庫系統默認的隔離級別(MySQL不是)。事務只能讀取到其他事務已經提交的修改。這個隔離級別有時候也叫不可重複讀,因爲兩次執行相同的查詢,可能得到不一樣的結果,
  • REPEATABLE READ(可重複讀):該隔離級別解決了不可重複讀的問題,它可以保證兩次執行相同的查詢,讀取到的數據是相同的。但是理論上,可重複讀無法解決幻讀的問題。所謂幻讀是指,讀取一定範圍的數據時,兩次執行相同的查詢,可能會讀取到條數不同的數據。InnoDB存儲引擎通過多版本併發控制(MVCC)解決了幻讀的問題,關於MVCC點擊這裏查看之前的博客。
  • SERIALIZABLE(可串行化):該隔離級別是隔離性最高的,它通過強制事務串行執行,避免了幻讀的問題。SERIALIZABLE會在讀取的每一行數據上都加鎖,所以可能導致大量的超時和鎖爭用的問題,只有非常需要確保數據一致性並且接受沒有併發的情況下,才考慮使用該級別。
隔離級別 髒讀 不可重複讀 幻讀 加鎖讀
讀未提交 Yes Yes Yes No
讀已提交 No Yes Yes No
可重複讀 No No Yes(MVCC可解決該問題) No
可串行化 No No No Yes

3.1.3、死鎖

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

爲了解決這個問題,數據庫系統實現了各種死鎖檢測和死鎖超時機制。不同存儲引擎使用不同的處理方式,InnoDB存儲引擎檢測到死鎖的循環依賴後,將持有最少行級排它鎖的事務進行回滾。

3.1.4、事務日誌

事務日誌可以提交事務的執行效率。使用事務日誌,存儲引擎在修改表的數據時,只需要修改其內存拷貝,再把修改行爲及記錄在事務日誌中,而不用每次都持久化到磁盤中。事務日誌使用的是追加的方式,因此寫日誌的操作是磁盤上一小塊區域內的順序I/O,相對隨機I/O來說快得多。事務日誌持久化完成後,後臺可以慢慢同步到磁盤中。目前大多數存儲引擎都是這樣實現的,這樣每次修改數據需要寫兩次磁盤。

需要注意的一點是,即使在事務日誌持久化磁盤過程中系統崩潰,在存儲引擎重啓時這部分資源也會恢復,不會產生丟失修改數據問題。

3.1.5、MySQL中的事務

MySQL中事務默認使用AUTOCOMMIT模式,也就是說,如果不是顯示的開始一個事務,則每個查詢都被當作一個事務執行提交操作。MySQL中常用的事務型存儲引擎有 InnoDB。

MySQL服務層不管理事務,事務是由下層不同的存儲引擎來實現的,所以同一個事務中,不可能使用多個存儲引擎。假如現在同一個事務操作MyISAM表和InnoDB表,假如事務正常執行不會有什麼問題。如果事務中出現問題,MyISAM表因爲不支持事務不能回滾就會造成無法估計的結果。

3.1.6、顯示和隱式鎖定

InnoDB等事務型存儲引擎,在事務執行過程中,InnoDB會根據隔離級別在需要的時候進行加鎖操作,這種InnoDB自動執行的鎖稱爲隱式鎖定。除了InnoDB自動加鎖,也可以通過以下語句進行顯式鎖定:

  • SELECT .... LOCK IN SHARE MODE
  • SELECT .... FOR UPDATE 

MySQL也支持 LOCK TABLES 和 UNLOCK TABLES 等語句,這些語句是在服務層實現的,和存儲引擎無關。一般情況下,如果使用了事務型存儲引擎,不建議使用這些語句,目前事務型存儲引擎的鎖工作的很多,使用這些語句不但對於安全性沒有提升反而會嚴重影響效率。

4、存儲引擎

如上面邏輯架構圖所示,存儲引擎是負責數據存儲和讀取的模塊,它通過爲上層服務層提供接口工作

4.1、常見的存儲引擎

MySQL中最常見的存儲引擎有以下兩種,InnoDB 和 MyISAM (默認存儲引擎)。下面簡單的介紹下兩種存儲引擎的:

InnoDB:

  • 支持事務,通過MVCC支持高併發,並且實現了上述四種隔離界別
  • 基於聚簇索引建立
  • 支持可預測性預讀,支持hash索引
  • 支持熱備份
  • 較好的容災機制

MyISAM:

  • 數據已緊密格式存儲,某些場景下性能好
  • 不支持事務,不支持行級鎖
  • 支持全文索引
  • 容災機制較差

4.2、存儲引擎的選擇

大部分情況下,選擇InnoDB存儲引擎都是正確的情況。除非需要用到某些InnoDB不具備的特性,並且沒有其他方法解決時才考慮使用其他存儲引擎。舉個例子:如果不在乎可拓展能力和併發能力,也不在乎崩潰後數據的丟失問題,並且對InnoDB佔用空間過多比較敏感,那麼就可以使用MyISAMc存儲引擎。

4.3、存儲引擎的轉換

常見的存儲引擎的轉換有以下三種:

  • ALTER TABLE:ALTER TABLE mytable ENGINE = InnoDB 。這種轉化方法需要執行很長的事件,MySQL會按行把數據複製到一張新的表中,在複製期間可能消耗所有的系統I/O能力,同時原表會加讀鎖。這種轉化方式會導致轉換表失去所有的特性,如InnoDB 轉 MyISAM,MyISAM 轉 InnoDB 不會恢復原來表中的外鍵
  • 導出和導入:使用工具導出原表的所有數據,創建新表使用新的存儲引擎,導出數據插入新表。這裏需要注意MySQL中同一個庫不能有同名的表,不同存儲引擎也不行。如果原表數據較大,導出和導入操作會比較慢
  • 創建與查詢:創建新表,使用要轉換的存儲引擎。使用 INSERT ...  SELECT 語句來完成,可以通過控制範圍,每次轉換一部分數據。在操作過程中,可以對原表加鎖,保證數據一致
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章