Mysql事務
事務定義
一個事務會涉及大量的cpu操作和IO操作,這些操作會被打包成一個執行單元,要麼同時完成,要麼同時都不完成。
事務是一組原子性的sql命令或者說是一個獨立的工作單元,如果其中任何一條sql語句因為崩潰或者其他原因執行失敗,那麼該組所有的sql語句都不會執行。如果沒有顯示啟動事務,資料庫會根據autocommit的值,預設每條sql操作都會自動提交。
事務的特性ACID
原子性(A)
一個事務中的所有操作,要麼都完成,要麼都不執行,對於一個事務來說,不可能只執行其中的一部分。
一致性(C)
資料庫總是從一個一致性的狀態轉換到另一個一致性的狀態
隔離性(I)
一個事務所做的修改在最終提交之前,對其他事務是不可見的。多個事務之間的操作相互不影響。每降低一個事務的隔離級別都能提高資料庫的併發,但同時不安全性就增加了。關於事務的隔離級別後續還要詳細討論。這裡簡單介紹一下:
- 讀未提交:其他事務未提交就可以讀。
- 讀已提交:其他事務只有提交了才能讀。
- 可重複讀:只管自己啟動事務時候的狀態,不接受其他事務的影響。
- 序列化:按照順序提交事務保證了資料的安全性,但無法實現併發。
永續性(D)
事務一旦提交,就要更新到資料庫中,不能回滾。就算伺服器宕機,仍然需要在下次啟動的時候自動執行事務中的sql命令,體現到資料庫中。
資料庫事務隔離界別
介紹
隔離界別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
未提交讀(Read uncommited) | 可能 | 可能 | 可能 |
已提交讀(Read commited) | 不可能 | 可能 | 可能 |
可重複讀(Repeatable read) | 不可能 | 不可能 | 可能 |
序列化(Serializable) | 不可能 | 不可能 | 不可能 |
- 未提交讀(Read uncommited):允許髒讀,也就是可能讀取到其他會話中未提交事務修改的資料。
- 已提交讀(Read commited):只能讀取到已經提交的資料。Oracle等多數資料庫預設都是該級別。
- 可重複讀(Repeatable read):可重複讀。在同一事務內的查詢都是事務開始時刻一致的,InnoDB預設級別。在SQL標準中,改級別消除了不可重複讀,但還是存在幻象讀,但InnoDB解決了幻讀。
- 序列化:完全序列化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞。
準備資料
準備一個賬戶表,表裡三個欄位,分別是主鍵id,賬戶名和金額
create table account(
id int(11) not null auto_increment,customer_name varchar(255) not null,money decimal(10,2) not null,primary key(id),unique uni_name using btree (customer_name)
) ENGINE = 'InnoDB' AUTO_INCREMENT=10 COMMENT = '賬戶表';
複製程式碼
驗證RU(未提交讀)
1、開啟兩個會話,設定隔離級別為read uncommited
set session transaction isolation level read uncommitted;
select @@session.tx_isolation;
複製程式碼
2、會話1開啟事務 3、會話2開啟事務並插入一條資料 4、會話1開始查詢資料庫,查到了會話2事務未提交的資料 5、會話2如果此時回滾了,會話1就查不到了 小結:在RU模式下,一個事務可以讀取到另一個未提交事務的資料,導致了髒讀。如果另一個事務回滾了,就會造成資料的不一致性。RU是事務隔離級別中最低的。
RC(讀提交)
1、將會話1和會話2的隔離級別設定成讀提交模式
set session transaction isolation level read committed;
複製程式碼
2、會話1和會話2都開啟事務,會話1查詢當前資料
3、會話2更新資料但未提交,會話1查詢當前資料還是沒改
4、但是會話2此時事務提交,會話1查詢當前資料變了
小結:在RC模式下,我們發現,當另一個事務沒有提交資料修改時,當前事務時讀不到修改後的資料的,這就避免了讀未提交模式的髒讀。但有一個問題,在當前事務中,兩次select的資料不一樣,這就存在了不可重複讀的問題。PS:RC隔離級別是Oracle資料庫預設的隔離級別。
RR(可重複讀)
1、將會話1和會話2的隔離級別都設定成可重複讀
set session transaction isolation level repeatable read;
複製程式碼
2、會話1開啟事務並查詢當前資料,會話2開啟事務並更新資料
3、此時會話1查詢資料還是之前的資料
4、然後會話2提交事務,會話1查詢資料還是之前的資料,這表示會話2的更改並沒有影響當前的事務,可以重複讀取。
5、此時會話1提交當前事務,並再次讀取資料,發現其他事務改了資料
小結:在RR模式下,我們解決了不可重複讀的問題,即在這種隔離級別下,一個事務中我們能夠保證獲取一樣的資料(即使有其他事務正在改當前的資料行)。但是無法避免幻讀,幻讀簡單的解釋就是在資料有新增的時候,也無法保證兩次得到的資料不一致但是不同資料庫對不同的RR級別有不同的實現,有時候加上間隙鎖來避免幻讀。
InnoDB解決了幻讀
結論
首先說結論:在RR的隔離級別下,InnoDB使用MVCC和next-key locks(間隙鎖)解決幻讀。MVCC解決的是普通讀(快照讀)的幻讀,間隙鎖解決的是當前讀情況下的幻讀。
幻讀是什麼
(首先宣告:在RR模式下,引擎是InnoDB,無法產生以下效果。)
1、事務1先執行,但未提交
start transaction;
update account set account=1000 where id>10;
複製程式碼
結果為:OK row xx表名成功影響多少行 2、事務2後執行,並且提交
start transaction;
insert into account(customer_name,money) values('李雲龍',10000);
commit;
複製程式碼
3、事務1再select一下,結果集為:
select * from account;
複製程式碼
···
李雲龍,10000
這時,事務1蒙了,不是已經將id>10的所有人的金額都更新成1000了嗎,怎麼多出一個人金額是10000?這是已提交事務2對事務1產生的影響,這個影響就叫做幻讀。
幻讀和不可重複讀的區別
不可重複讀:多次讀取一條記錄,發現該記錄中某些列值被修改過。
幻讀:只要是說多次讀取一個範圍內的記錄(包括直接查詢所有記錄結果或者做聚合統計),發現結果不一致(一般指的是記錄增多,記錄的減少應該也算是幻讀)。
怎麼解決幻讀
當前讀
所謂當前讀,指的是加鎖的select(S鎖或者X鎖),update或者delete語句。在RR的事務隔離級別下,資料庫會使用間隙鎖來鎖住本條記錄以及索引區間。例如:
select * from account where id>10 for update;鎖住的就是id=10的記錄以及id>10的這個區間範圍,避免範圍間插入記錄,以避免產生幻影行記錄。
普通讀
因為普通讀是不會加鎖的讀,所以解決幻讀的手段是MVCC。MVCC會給每行元祖加一些輔助欄位,記錄建立版本號和刪除版本號。在每一個事務啟動的時候,都有一個唯一的遞增的版本號。每開啟一個新事務,事務的版本號就會遞增。MYSQL預設的隔離級別(可重複讀Repeatable Read)下,增刪改查就變成下面這樣:
- select:讀取建立版本小於或等於當前事務版本號,並且刪除版本為空或大於當前事務版本號的記錄。這樣可以保證在讀取之前記錄是存在的。
- insert:將當前事務的版本號儲存至行的建立版本號。
- update:新插入一行,並以當前事務版本號作為新行的建立版本號,同時將原紀錄行的刪除版本號設定為當前事務版本號。
- delete:將當前事務版本號儲存至行的刪除版本號。 有點繞,總結下來就是每行多了兩個版本號,一個建立版本號和一個刪除版本號,都是儲存事務的版本號。下面舉個例子理解一下: 例如我插入一條記錄,事務id=1,那麼記錄如下:
id | name | createversion | deleteversion |
---|---|---|---|
1 | 馬雲 | 1 |
如果我把name更新成劉強東,事務id=2,那麼記錄就變成
id | name | createversion | deleteversion |
---|---|---|---|
1 | 馬雲 | 1 | 2 |
2 | 劉強東 | 2 |
=> 可以看出,原來的元祖的deleteversion為更新的這個事務的id,並且新增一條
如果刪除的話,假設事務id=3,刪除id=2的記錄
id | name | createversion | deleteversion |
---|---|---|---|
1 | 馬雲 | 1 | 2 |
2 | 劉強東 | 2 | 3 |
關鍵點來了,如果我現在讀取的話,需要同時滿足兩個條件:
1、讀取建立版本小於或等於當前事務的版本號,這意味著資料在這個事務之前被建立
2、刪除版本為空或者大於當前事務版本號的記錄。這意味著刪除操作在這個事務之後發生
所以當事務1插入一條記錄,事物2更新這條記錄並刪除這條記錄之後,事務1再次查詢這條記錄,只有id=1的記錄滿足這兩個條件,所以結果為:(避免了幻讀)
id | name | createversion | deleteversion |
---|---|---|---|
1 | 馬雲 | 1 | 2 |
序列化
所有事務序列執行,是最高的隔離級別,效能最差
總結
這篇文章就到這了,總的來說資料庫事務的四個特性ACID和四種隔離級別(RU,RC,RR,序列化)以及會產生的問題(髒讀、不可重複讀、幻讀)是面試的高頻問點,什麼東西都需要積累,然後反覆的溫習,這樣才能記憶和理解的更深刻。