1. 程式人生 > 程式設計 >Mysql事務

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,序列化)以及會產生的問題(髒讀、不可重複讀、幻讀)是面試的高頻問點,什麼東西都需要積累,然後反覆的溫習,這樣才能記憶和理解的更深刻。