1. 程式人生 > >MySQL 四種事務隔離級別

MySQL 四種事務隔離級別

一、事務的基本要素(ACID)

1、原子性(Atomicity):事務開始後所有操作,要麼全部完成,要麼全部不做,不可能停滯在中間環節。事務執行過程中出錯,會回滾到事務開始前的狀態。也就是說事務是一個不可分割的整體。 2、一致性(Consistency):事務開始前和結束後,資料庫的完整性約束沒有被破壞。比如 A 向 B 轉賬,不可能 A 扣了錢,B 卻沒有收到。 3、隔離性(Isolation):同一時間,只允許一個事務請求同一資料,不同的事務之間彼此沒有任何干擾。 4、永續性(Durability):事務完成後,事務對資料庫的所有操作都被儲存到資料庫,不能回滾。

二、事務的併發問題

1、髒讀:事務 A 讀取了事務 B 更新的資料,然後 B 回滾操作,那麼 A 讀取到的資料就是髒資料。 2、不可重複讀:事務 A 多次讀取同一資料,事務 B 在事務 A 多次讀取過程中,對資料做了更新並提交,導致事務 A 多次讀取同一資料時,結果不一致。 3、幻讀:系統管理員 A 將資料庫中所有學生的成績從具體分數改為ABCDE等級,但是系統管理員 B 就在這個時候插入了一條具體分數的記錄,當系統管理員 A 改結束後發現還有一條記錄沒有改過來,就好像發生了幻覺一樣,這就叫幻讀。

小結:不可重複讀側重於修改,幻讀側重於新增或刪除。解決幻讀需要鎖表。

三、MySQL 事務隔離級別

Mysql 事務隔離級別 mysql 預設的事務隔離級別為 repeatable-read 預設事務隔離級別

四、演示說明各個隔離級別的情況

1、讀未提交(read-uncommitted):

(1)開啟客戶端 A,並設定當前事務模式為 read uncommitted,查詢表 goods 的初始值; 在這裡插入圖片描述 (2)在客戶端 A 的事務提交之前,開啟另一個客戶端 B,更新 goods; 在這裡插入圖片描述 (3)這時,雖然客戶端 B 的事務還沒提交,但是客戶端 A 就可以查詢到 B 已經更新的資料; 在這裡插入圖片描述 (4)一旦客戶端 B 的事務因為某種原因回滾,所有的操作都會將被撤銷,那客戶端 A 查詢到的資料其實就是髒資料; 在這裡插入圖片描述

(5)在客戶端 A 執行更新語句 update goods set goods_stock = goods_stock - 10 where id =1,iphonex 的 goods_stock沒有變成30,居然是40,出現了髒讀。也就是說,在應用程式中,我們會用40-10=30,並不知道其他會話的回滾,要想解決這個問題,可以採用讀已提交的隔離級別。 在這裡插入圖片描述

2、讀已提交(read committed)

(1)開啟一個客戶端 A ,並設定當前事務級別為 read committed,查詢表 goods的初始值; 在這裡插入圖片描述 (2)在客戶端 A 的事務提交之前,開啟另一個客戶端 B,更新 goods ; 在這裡插入圖片描述 (3)這時,客戶端 B 的事務還沒提交,客戶端 A 不能查詢到 B 已經更新的資料,解決了髒讀的問題; 在這裡插入圖片描述

(4)客戶端 B 的事務提交; 在這裡插入圖片描述 (5)客戶端 A 執行上一步相同的查詢,結果與上一步不一致,即產生了不可重複讀的問題; 在這裡插入圖片描述

3、可重複讀(repeatable read)

一切目的是:保證可重複讀;A 事務開啟時,若A 的讀操作先於 事務B 的寫操作,則為了可重複讀,在 A 事務提交前,A事務 不會將 B 的最新值讀過來;若A 的讀操作晚於 B 的寫操作,即事務的首次讀,會讀取資料的最新值。寫操作為了保證資料的一致性,都是會基於最新資料操作的。

(1)開啟一個客戶端 A,並設定當前事務級別為 repeatable read,查詢表 goods; 在這裡插入圖片描述 (2)在客戶端 A 提交事務之前,開啟另一個客戶端 B,更新表 goods 並提交; 在這裡插入圖片描述 (3)客戶端 A 再次執行查詢操作,兩次查詢結果一致,沒有出現不可重複讀的問題; 在這裡插入圖片描述 (4)客戶端 A 接著執行 update goods set goods_stock = goods_stock - 10 where id =1,iphonex 的 goods_stock沒有變成50 - 10 = 40,iphonex 的 goods_stock的值用的是步驟(2)中提交的40來算的,所以結果是30,資料一致性沒有被破壞。 在這裡插入圖片描述 (5)客戶端 A 提交事務,查詢表 goods; 在這裡插入圖片描述 (6)在客戶端 B 開啟事務,新增一條資料,其中 goods_stock欄位值為40,並提交; 在這裡插入圖片描述 (7) 在客戶端 A 計算 goods_stock 之和,值為30+50=80,沒有把客戶端 B 的值算進去,客戶端 A 提交後再計算 goods_stock 之和,居然變成了120,這是因為把客戶端 B 的40算進去了; 在這裡插入圖片描述 對於客戶端 A,提交事務前後,統計的資料不一致,導致了幻讀。站在開發者角度,資料的一致性並沒有被破壞。但是在應用程式中,我們的程式碼可能會把80提交給使用者,但是這個概率很小。想避免這種情況,除非鎖住表,不讓其新增或者刪除,這就要講到最後一個事務隔離級別-“序列化”。

4、序列化(serializable)

(1)開啟客戶端 A,設定當前事務級別為 serializable,查詢表 goods; 在這裡插入圖片描述 (2)開啟一個客戶端B,並設定當前事務模式為 serializable,插入一條記錄報錯,表被鎖了插入失敗。mysql中事務隔離級別為serializable時會鎖表,因此不會出現幻讀的情況,這種隔離級別併發性極低,開發中很少會用到。 在這裡插入圖片描述

補充:

1、mysql中預設的事務隔離級別是 repeatable read,並不會鎖住讀取到的行; 2、事務隔離級別為 read committed 時,寫資料只會鎖住相應的行。即寫操作鎖住了相應的行,導致讀操作讀不到,故只有寫操作提交了,其他事務才能讀到最新資料; 3、事務隔離級別為 repeatable read 時,如果有索引(包括主鍵索引)的時候,以索引列為條件更新資料,會存在間隙鎖、行鎖的問題,從而鎖住一些行;如果沒有索引,更新資料時會鎖住整張表(但是可讀)。 4、隔離級別越高,越能保證資料的完整性和一致性,但是對併發效能的影響也越大。對於多數應用程式,可以優先考慮隔離級別設為 Read Committed,它能夠避免髒讀,而且具有較好的併發效能。儘管它會導致不可重複讀、幻讀這些併發問題,在可能出現這類問題的個別場合,可以由應用程式採用悲觀鎖或者樂觀鎖來控制。

5、間隙鎖(Gap Lock):

定義:鎖加在不存在的空閒空間,可以是兩個索引記錄之間,也可能是第一個索引記錄之前或最後一個索引記錄之後的空間。

間隙鎖的出現主要集中在同一個事務中先 delete 後 insert 的情況下,當我們通過一個索引引數去刪除一條記錄的時候,如果引數在資料庫中存在,那麼這個時候產生的是普通行鎖,鎖住這條記錄,然後刪除,然後釋放鎖;如果這條記錄不存在,問題就來了,資料庫會掃描索引,發現這個記錄不存在,這個時候的 delete 語句獲取到的就是一個間隙鎖,然後資料庫會掃描到第一個比給定引數小的值,向右掃描掃描到第一個比給定引數大的值,然後以此為界,構建一個區別,鎖住整個區間的資料,一個特別容易出現死鎖的間隙誕生了。

(1)在預設的 repeatable read 隔離級別下,並且mysql 儲存引擎是 InnoDB(支援事務) 時,客戶端 A 開啟事務,執行刪除語句 delete from goods where id = 7; 在這裡插入圖片描述 (2)客戶端 B 執行 insert into goods (id,goods_name,goods_stock) values (9,“aaaaaa”, 80),發現產生了間隙鎖。當執行刪除語句是,由於沒有 id = 7 的記錄,於是會在id(4 - 10)區間內生成間隙鎖,不允許此區間內的 insert 操作。 在這裡插入圖片描述 所以,為了避免間隙鎖,需要遵循存在才刪除原則,儘量避免刪除不存在的記錄。

6、MySQL 儲存引擎和索引優化:

資料庫儲存引擎的選擇: MyISAM:不支援事務,表級鎖(讀操作是共享鎖)。適用場景:無事務場景,只讀場景。 InnoDB:事務級儲存引擎,完美支援行級鎖、事務ACID特性。MySQL 5.7以上的預設選擇。

大事務:在一個事務中,處理了太多的資料或者太多的SQL,導致處理時間很長而產生鎖表。

如何避免大事務? 1、避免一次處理太多的資料,可以採用分批處理。 2、移出不必要在事務中的select操作。

索引的選擇:

1、BTREE索引

BTREE索引,底層是B+樹的實現方式,B+樹是根據主鍵ID的順序進行分配的,其包含類似於二分搜尋樹的特性,B+樹也是一種平衡樹。B+樹非葉節點中存放的關鍵碼(主鍵ID)並不只是資料物件的地址指標,非葉節點只是索引部分。所有的葉節點在同一層上,包含了全部關鍵碼和相應資料物件的存放地址指標,且葉節點按關鍵碼(主鍵ID)從小到大順序連結。 B+樹

(1)BTREE索引的使用場景:

場景1: 場景1 場景2: 在這裡插入圖片描述

使用場景: 1、全值匹配的查詢,如oder_sn=‘9876432119900’; 2、匹配最左字首的查詢,在聯合索引中,只要最左的索引列用到了,該索引就會生效。但是如果是後面的索引列用到了,最左列未使用,是不會生效的; 3、匹配列字首查詢,即模糊查詢最前面不要使用‘%’,如order_sn=‘9876%’; 4、匹配範圍的查詢,如oder_sn>‘9876432119900’,order_sn<‘9876432119999’; 5、精確匹配左前列並範圍匹配另外一列。

(2)BTREE索引的使用限制:

使用限制

使用限制: 1、如果不是按照索引最左列開始查詢的,則無法使用索引; 2、使用索引時不能跳過索引的列,假如3個列的聯合索引,用到了1、3,未用的2,此時只有1是生效的,3是不生效的; 3、Not in 和 <> 操作無法使用索引; 4、如果查詢中有某個列的範圍查詢,則其右邊的所有列都無法使用索引。

2、Hash索引

(1)Hash索引的特點:

Hash 索引的特點 只有查詢條件精確匹配Hash索引中的所有列時,才能夠使用到Hash索引。 Hash索引的限制

Hash索引的限制: 1、Hash索引必須進行二次查詢,因為Hash索引儲存的是Hash碼,並不是直接儲存資料的指標地址,所以需要二次查詢,但是由於資料庫都是有快取的,這樣的效能損耗可以忽略; 2、Hash索引無法用於排序,因為它不像BTREE那樣是順序儲存元素的; 3、Hash索引不支援部分索引查詢也不支援範圍查詢,只支援精確查詢; 4、Hash索引中的Hash碼的計算可能存在Hash衝突,從而影響效能。

3、索引優化:

聯合索引優化

建立聯合索引時,如何選擇索引列的順序? 1、經常會被使用到的列優先; 2、選擇性高的列優先,即值的可能性比較多的列; 3、寬度小的列優選。

order by 優化

使用索引掃描來優化排序: 1、索引的列排序和Order by 子句的順序完全一致; 2、索引中的所有列的方向(升序,降序)和Order by 子句完全一致; 3、Order by 中的欄位全部在關聯表的第一張表中。