文章目錄
1. Mysql架構
1.1 架構圖
1.2 讀寫鎖
共享鎖:讀鎖
多個用戶同一時刻可以同時讀取同一個資源互不干擾
排他鎖:寫鎖
寫鎖會阻塞其他的寫鎖和讀鎖
1.3 鎖粒度
表級鎖
- 開銷最小的鎖策略
- 對整張表加鎖
獲取表級寫鎖會阻塞其他用戶對該表的所有讀寫操作,讀鎖之間是不互相阻塞的
寫鎖比讀鎖具有更高的優先級,寫鎖請求可能會插入到讀鎖隊列前
除儲存引擎,Mysql本身也可以使用有效的表鎖來實現不同的目的
行級鎖
- 帶來最大的鎖開銷
- 可以最大程度支持併發處理
行級鎖只在儲存引擎(InnoDB 和 XtraDB)層實現,Mysql服務器層沒有實現
1.4 事務
- 事務內的語句,要麼全部執行成功,要麼全部執行失敗。支持事務的系統要通過嚴格的ACID測試。
- 對於不需要事務的業務場景,用戶可以選擇一個非事務型的存儲引擎,來獲得更高的性能,當然即使存儲引擎不支持事務,Mysql服務器的表級鎖也可以提供一定程度的保護。
原子性(atomicity)
一個事務必須被視爲一個不可分割的最小執行單元,要麼全部提交成功,要麼全部失敗回滾,不能只執行其中的一部分。
一致性(consistency)
數據庫總是從一個一致性狀態轉換到另一個一致性狀態,除非事務最終提交,不然事務的修改不會提交到數據庫中。
隔離性(isolation)
一個事務在最總提交前,通常對其他事務不可見,如事務a過程中對賬號金額200進行了修改,但是事務a還未提交,這時事務b看到的賬號金額還是是未修改的200。
持久性(durability)
一旦事務提交,其所做修改會永久保存在數據庫中。
1.5 隔離級別
每種存儲引擎實現的隔離級別不盡相同
READ UNCOMMITTED(未提交讀)
- 事務中的修改,即使沒有提交,對其他事務也都是可見的。
- 事務可以讀取到未提交的數據,這也稱爲髒讀(Dirty Read)。
- 除非真的有非常必要的理由,在實際應用中一般很少使用。
READ COMMITTED(提交讀)
- 大多數數據庫系統的默認隔離級別(Mysql不是0)。
- 一個事務只能“看見”已經提交的事務所做的修改。一個事務從開始直到提交之前,所做的修改對其他事務不可見。但是可能出現一個事務中兩次執行同樣查詢時,得到結果不一樣的情況。
- 該級別也叫不可重複讀(nonrepeatable read)。
不可重複讀,例如:
- 事務A:
select money from user where account = "123";
結果:2000
- 事務B:
update user set money = 3000 where account = "123";
事務B提交
- 事務A:
select money from user where account = "123";
結果: 3000
事務A提交
事務A過程中出現了前後查詢讀取到的數據不一致的問題,所以該級別被稱作不可重複讀。
REPEATABLE READ(可重複讀)
- 該級別保證了同一事務中多次讀取同樣記錄的結果是一致的。
- 該級別是Mysql的默認事務級別。但是理論上,該級別還是無法解決另一個幻讀(Phantom Read)問題。
- 幻讀(Phantom Read)指某個事務在讀取某個範圍內的記錄時,另一個事務又在該範圍內插入了新的記錄,當之前的事務再次讀取某個該範圍的記錄時,就會產生環行。
- InnoDB和XtraDB存儲引擎通過多版本併發控制(MVCC, Multiversion Concurrency Control)解決幻讀問題。
幻讀,例如:
- 事務A:
select * from user;
結果有10行
- 事務B:
Insert into user value(11,"asd");
事務B提交
- 事務A:
select * from user;
結果有11行
此時就出現了幻讀,兩次讀取結果行數不一致。
SERIALIZABLE(可串行化)
- 最高隔離級別。
- 通過強制事務串行執行避免幻讀,簡單說就是在讀取的每一行數據上都加鎖,可能導致大量超市和鎖爭用的問題。
- 實際很少使用,除非要非常需要確保數據的一致性而且可以接受沒有併發的情況下。
隔離級別圖表
1.6 死鎖
當多個事務視圖以不同的順序鎖定資源時,就可能會產生死鎖。例如,設想下面兩個事務同時處理StockPrice表:
事務1:
START TRANSACTION;
UPDATE StockPrice SET close = 45.50 WHERE stock_id = 4 and date = '2002-05-01';
UPDATE StockPrice SET close = 19.80 WHERE stock_id = 3 and date = '2002-05-02';
COMMIT;
事務2:
START TRANSACTION;
UPDATE StockPrice SET close = 45.50 WHERE stock_id = 3 and date = '2002-05-01';
UPDATE StockPrice SET close = 19.80 WHERE stock_id = 4 and date = '2002-05-02';
如果湊巧,兩個事務都執行了第一條UPDATE語句,同時也鎖定了該行數據,接着每個事務都嘗試去執行第二條UPDATE語句,卻發現該行被鎖定了,然後兩個事務都等待對方釋放鎖,同時又持有對方需要的鎖,陷入死循環。
- 爲了解決這些問題,數據庫系統實現了各種死鎖檢測和死鎖超時機制。
- InnoDB目前處理死鎖的方法是,將持有最少行級排他鎖的事務進行回滾。
1.7 事務日誌
修改數據流程:
修改數據改內存拷貝 -> 修改事務日誌 -> 持久化事務日誌 -> 內存拷貝持久化
- 事務日誌可以提升事務的效率。
- 存儲引擎在修改表的數據時只需要修改其內存拷貝,再把該修改行爲記錄到持久在硬盤上的事務日誌中,而不用每次都將修改的數據本身持久到磁盤上。
- 事務日誌採取追加方式,寫日誌的操作是在磁盤上的一小塊區域進行順序I/O,不像隨機I/O多次移動磁頭,所以事務日誌效率較高。
- 事務日誌持久以後,內存中被修改的數據在後臺可以慢慢地刷回到磁盤。
- 如果修改已經記錄到事務日誌並持久化,但是數據本身並沒有寫回磁盤,此時系統崩潰,存儲引擎在重啓時可以自動恢復這部分修改的數據。
1.8 MySQL中的事務
MySQL提供了兩種事務型的存儲引擎:InnoDB和NDB Cluster,另外還有一些第三方的存儲引擎也支持事務:XtraDB和PBXT。
自動提交(AUTOCOMMIT)
- Mysql默認採用自動提交,如果不是顯示地開始一個事務,則每個查詢都會被當作一個事務執行提交操作。
- 當前連接中,可以通過設置AUTOCOMMIT變量來啓用或者禁用自動提交操作。