1. 程式人生 > 資料庫 >《高效能MySQL》學習筆記——第一章 MySQL架構與歷史

《高效能MySQL》學習筆記——第一章 MySQL架構與歷史

第一章 MySQL架構與歷史

1.1 MySQL邏輯架構

  • 第一層:連線、執行緒處理、授權認證、安全等

  • 第二層:快取、解析器、優化器等。第一層 + 第二層 = 伺服器層???

  • 第三層:儲存引擎,負責MySQL資料的儲存和提取。

1.1.1 連線管理與安全性

每個客戶端連線都會在伺服器中有一個執行緒,這個連線的查詢只會在這個單獨的執行緒中進行。如:

  • 使用命令列登入MySQL伺服器後,show processlist就會新增一個執行緒;

  • 使用workbench開啟一個連線後會新增兩個執行緒(不知道為何有兩個執行緒?),workbench的SQL tab和執行緒無關,開十個SQL tab還是兩個執行緒;

  • 使用datagrip每開啟一個query console並查詢資料後就會開啟一個執行緒。

1.1.2 優化與執行

特殊關鍵字提示優化器:hint

請求優化器解釋優化過程:explain

1.2 併發控制

併發控制的問題:多個查詢在同一時刻修改資料時產生的問題。

併發控制的兩個層面:伺服器層、儲存引擎層。

1.2.1 讀寫鎖

在處理併發的讀或寫時,可以通過實現一個由兩種型別的鎖組成的鎖系統來解決問題。這兩種型別的鎖通常被稱為共享鎖(shared lock)和排他鎖(exclusive lock),也叫讀鎖(read lock)和寫鎖(write lock)

  • 讀鎖:讀鎖之間是共享的,即讀鎖之間互不阻塞;

  • 寫鎖:寫鎖之間是排他的,即一個寫鎖會阻塞其他寫鎖和讀鎖,注意不僅僅是寫鎖會被阻塞,讀鎖也會被阻塞!

寫鎖比讀鎖的優先順序高,即在一個鎖佇列中,一個寫鎖可能會插隊到讀鎖前面,而讀鎖不能被插入到寫鎖前面,想想之前資料結構中學的優先佇列。

1.2.2 鎖粒度

鎖粒度:對正在修改的資料片進行鎖定的精度,即對一條資料進行修改時,是做到僅鎖住這條資料,還是鎖住整個表。

鎖策略:在鎖的開銷和資料的安全性之間尋求平衡。一般商業資料庫是在表上加行級鎖。

表鎖:鎖定整張表。開銷最小,

行級鎖:鎖定獨寫的某一條資料。開銷最大。

1.3 事務

事務(Transaction):是一組原子性的SQL查詢,或者說一個獨立的工作單元。

ACID:

  1. 原子性(atomicity)

一個事務必須被視為一個不可分割的最小單元,整個事務的操作要麼全部提交成功,要麼全部失敗回滾,不能只執行其中的一部分操作,這就是事務的原子性。

  1. 一致性(consistency)

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

對一致性的解釋比較容易理解:

一致性有下面特點:

  • 如果一個操作觸發輔助操作(級聯,觸發器),這些也必須成功,否則交易失敗。

  • 如果系統是由多個節點組成,一致性規定所有的變化必須傳播到所有節點(多主複製)。如果從站節點是非同步更新,那麼我們打破一致性規則,系統成為“最終一致性”。

  • 一個事務是資料狀態的切換,因此,如果事務是併發多個,系統也必須如同序列事務一樣操作

在現實中,事務系統遭遇併發請求時,這種序列化是有成本的, Amdahl法則描述如下:它是描述序列序列執行和併發之間的關係。一個程式在平行計算情況下使用多個處理器所能提升的速度是由這個程式中序列執行部分的時間決定的。

大多數資料庫管理系統選擇(預設情況下)是放寬一致性,以達到更好的併發性。

事務的執行不能破壞資料庫資料的完整性和一致性,一個事務在執行之前和執行之後,資料庫都必須處於一致性狀態。

  1. 隔離性(isolation)

隔離性是指,一個事務所做的修改在最終提交之前,對其他事務是不可見的(並不是完全不可見,有不同的隔離級別)。

  1. 永續性(durability)

事務一旦提交,其所作的修改就會永久儲存到資料庫中,此時即使系統奔潰,修改的資料也不會丟失。不可能有能做到100%永續性保證的策略。

1.3.1 隔離級別

READ UNCOMMITTED(未提交讀)、READ COMMITTED(提交讀,也是不可重複讀)、REPEATABLE READ(可重複讀,是MySQL的預設事務隔離級別)、SERIALIZABLE(可序列化)。

對隔離級別的講解很到位(若原文連結失效,可參考我自己的),可以參考。書中都是文字性的講解,不太好理解。額外要注意的一點就是SERIALIZABLE會在讀的每一行上加鎖。

1.3.2 死鎖

死鎖:是指當兩個或多個事務在同一資源上相互佔用,請求鎖定對方佔用的資源,每個事務都在等待對方釋放鎖,從而等待狀態永遠不會結束形成惡性迴圈的現象。

書中這個例子很容易理解,如果湊巧兩個事務的第一條SQL同時執行,則事務1鎖定了stock_id=4的資料(假設叫做第1條資料),事務2鎖定了stock_id=3的資料(假設叫做第2條資料),然後事務1等待事務2釋放第2條資料的鎖,而事務2要執行整個事務提交後才能釋放資料2的鎖,當時在執行第2條SQL時又要等事務1釋放第1條資料的鎖,這樣就導致兩個事務相互等待,且相互等待的狀態永遠不會結束,導致死鎖。

資料庫實現了各種死鎖檢測機制和死鎖超時機制。當前InnoDB處理死鎖的方法是,將持有最少行級排他鎖的事務進行回滾。(書中原文)

死鎖的產生有些還和儲存引擎的實現方式有關。

1.3.3 事務日誌

使用事務日誌,儲存引擎在修改表的資料時,只需要修改其記憶體拷貝,再把該修改行為記錄到持久在硬碟的事務日誌中。可以理解為事務提交後並不會直接修改硬碟中的資料,而是先把事務日誌寫到硬碟的事務日誌中,然後讓資料在後臺慢慢修改到硬碟中。通常稱這種日誌為預寫式日誌,修改資料要寫入兩次資料到硬碟中,第一次是日誌,第二次才是資料根據日誌進行修改。系統奔潰時,也可以通過硬碟中的事務日誌進行資料恢復。

1.3.4 MySQL中的事務

如果不是顯示地使用start transaction,則預設為每一個SQL語句都是一個事務。MySQL預設採用自動提交事務(autocommit)的方式。

mysql> SHOW VARIABLES LIKE 'AUTOCOMMIT';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set, 1 warning (0.01 sec)

mysql> SET AUTOCOMMIT = 0; -- 顯示的將該執行緒下的提交方式修改為手動提交,即要手動使用COMMIT或ROLLBACK
Query OK, 0 rows affected (0.00 sec)

注意!某些命令在執行前會強制執行COMMIT提交掉當前事務的修改,如ALTER TABLE、LOCK TABLES等。

mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 設定下一個事務的隔離級別,可以用在儲存過程的start transaction之前。
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 設定當前會話事務的隔離級別
Query OK, 0 rows affected (0.00 sec)

雖然InnoDB會根據事務隔離級別自動鎖定修改,但MySQL也支援顯式鎖定表:LOCK TABLES。但本書建議,只有在使用set transaction = 0的事務中可以使用LOCK TABLES,其他情況應該避免使用使用LOCK TABLES。

1.4 多版本併發控制(MVCC)

可以認為MVCC是行級鎖的一個變種,但是它很多情況下避免了枷鎖操作,雖然實現機制有所不同,但大都實現了非阻塞的讀操作,寫操作也只鎖定必要的行。

下面是本書中通過InnoDB的簡化版行為來說明MVCC是如何工作的。

1.5 MySQL的儲存引擎

  • InnoDB引擎:支援事務

  • MyISAM引擎:不支援事務和行級鎖,但讀取效率高

  • Archive:不支援引擎,支援行級鎖,插入效率高

  • CSV引擎:用作資料交換很有用

  • Memory引擎:不支援行級鎖,支援hash索引,查詢效率特別高,比MyISAM效率還更高,資料儲存在記憶體中的形式

中間省略大量內容...

如果需要對記錄的日誌做分析報表,則生成報表的SQL可能會導致日誌插入效率下降,怎麼辦?一種辦法是,利用MySQL內建的複製方案,複製一份到從庫,然後在從庫做查詢操作,主庫只用於高效的插入操作,因此從庫的查詢操作就不會影響到主庫的插入效能。