1. 程式人生 > >一步步走進Sql中的事務及事務隔離級別

一步步走進Sql中的事務及事務隔離級別

eight 16px set 隔離 等待 zhang code 數據庫初始 關閉

1.事務定義

事務是按照邏輯執行的一組任務序列,這一組任務序列要麽都執行,要麽都不執行。

事務是優先基於會話配置的的(session),其次是基於全局配置的(global)。

2.事務的提交和回滾

提交commited,即一組任務序列,正常執行後,提交給數據庫。

回滾rollback,即一組任務序列中,無論哪一個任務環節出現異常,都會把所有的任務序列全部恢復到原始狀態。

事務的提交可以分為手動提交和自動提交。

3.查看和設置提交方式

session和global中都有變量autocommit,

autocommit為1的時候自動提交,

autocommit為0的時候手動提交。

select
@@session.autocommit;

技術分享圖片

查看全局配置中的默認提交方式

select @@global.autocommit;

技術分享圖片

關閉當前會話的自動提交,改為手動提交。

set @@session.autocommit=0;

技術分享圖片

4.創建數據庫,測試提交方式。

設計一個簡單的數據庫,往數據庫中加一個表格。

表格中分別是,id(自增),用戶名,用戶余額。

USE transdemo;

DROP TABLE IF EXISTS users;

CREATE TABLE users (
  uid INT(11) AUTO_INCREMENT,
  uname VARCHAR
(10) DEFAULT NULL, money INT(11) DEFAULT NULL, PRIMARY KEY (uid) )

插入一條數據

INSERT INTO users VALUES(NULL,zhanghao,1000)

mysql自增字段(id)插入時,用null即可。

結果:

技術分享圖片

自動提交:

技術分享圖片

手動提交:

為了測試手動提交,打開另外一個命令窗口並連接mysql,即開啟了另外一個會話窗口B(Windows PowerShell)

這是原本的會話窗口A(Windows PowerShell),關閉自動提交,改為手動提交。

技術分享圖片

此時A中會話看似一切正常。

在B中,再次讀取users表。

技術分享圖片

問題出來了,zhanghao用戶的money還是900,表格數據並沒有被更新。

此時,我們在A會話(原本會話)中,提交commit;

技術分享圖片

此時,我們在B會話(新開的會話窗口)再次查詢,

技術分享圖片

數據被更新。

總結:自動提交時,無需commit即可自動更新改變數據;手動提交時,必須要commit,才能更新改變數據庫中的數據。

5.事務隔離級別

事務中的隔離級別共有四種:

1.讀未提交 read uncommited

2.讀已提交 read commited

3.可重復讀 repeatable read

4.串行化 serializable

首先查看當前會話的事務隔離級別

select @@session.transaction_isolation;


-- 8.0以下版本
-- select @@session.tx_isolation;

技術分享圖片

再看一下全局的

select @@global.transaction_isolation;


-- 8.0以下版本
-- select @@global.tx_isolation;

技術分享圖片

從上面測試可以看出,mysql的事務隔離級別,默認是repeatable-read,即可重復讀。

我們可以修改mysql的事務隔離級別,仍然基於當前會話(session)

-- 將當前會話的事務隔離級別設置為 : read uncommitted,即讀未提交
set session transaction isolation level read uncommitted;

-- 查看當前會話的事務隔離級別
select @@session.transaction_isolation;

技術分享圖片

此時,事務的隔離級別變為的read uncommitted即讀未提交。

5.1 讀未提交

兩個連接窗口AB(Windows PowerShell),測試讀未提交。

按上文方法,將A連接設置為讀未提交。B連接不設置隔離級別,采用默認。

首先在B連接中,開始事務,並將zhanghao用戶的money減100。

技術分享圖片

此時,在事務中,可以正常的顯示zhanghao的money為900,成功被減去100。

但是在數據庫中查看

技術分享圖片

zhanghao的money仍為1000。

這也不奇怪,因為B連接的事務並沒有被提交。

但是,在B連接中的事務沒有提交,數據庫users表中zhanghao的money為1000的時候,

在A會話窗口中(已被設置為讀未提交),開始事務並讀取zhanghao的money時,卻讀到了未提交的900。

技術分享圖片

這就是讀未提交。

導致的問題:臟讀。即另外一個事務尚未提交的時候(數據庫中的數據沒被改變的時候),在A窗口事務中,已經讀到了未提交的數據。

5.2 讀已提交

仍然開始兩個Windows PowerShell窗口分別連接mysql,實現兩個會話。

首先在A窗口中,設置事務隔離級別為讀已提交。

set session transaction isolation level read committed;

技術分享圖片

B窗口采用默認的隔離級別,不做任何改動即可。

數據庫初始值

技術分享圖片

在B窗口中,開啟事務,並把money減去100,不提交。

技術分享圖片

然後,在A窗口中,開始事務並查詢users表,不提交

技術分享圖片

然後,在B窗口中,提交。

技術分享圖片

然後,在A窗口中,再次查詢users表。

技術分享圖片

本次流程A窗口的完整運行結果如下

技術分享圖片

第一次查詢的時候,由於B窗口的update未提交,所以為money1000,

B窗口提交之後,A窗口再次查詢的時候,money變為了900。

這就是讀已提交。

導致的問題:不可重復讀。從上圖可以明顯看出,在一個事務中,兩次讀到的結果不相同。

5.3 可重復讀

仍然開始兩個Windows PowerShell窗口分別連接mysql,實現兩個會話。

首先在A窗口中,設置事務隔離級別為可重復讀。

技術分享圖片

B窗口采用默認的隔離級別,不做任何改動即可。

數據庫初始值

技術分享圖片

首先在B窗口中,開啟事務。並將zhanghao的money減去100,不提交。

技術分享圖片

然後,在A窗口中,開啟事務並查看users表,不提交。

技術分享圖片

然後B窗口提交。

技術分享圖片

然後再次在A窗口中查看users表。

技術分享圖片

發現,兩次查詢的結果,是一致的。即使B窗口已經將money改為900並提交,

但是在A窗口的一個事務中,兩次查詢結果是一致的

技術分享圖片

這就是可重復讀,即在一個事務中,重復讀數據時不會變的。

但是這就完美了嗎?

此時A窗口仍未提交,B窗口已提交。

在B窗口,再次開啟一個事務,並且添加一條用戶,lisi,500。

此時,A窗口再次查詢users表時,詭異的多了一條lisi的數據,

也就是像魅影一樣的出現了,這就是幻讀。

mysql8中,已經對幻讀增加了防範措施,低版本mysql仍存在幻讀問題。

可重復讀導致的問題:幻讀

5.4 串行化

同上,開啟A、B兩個Windows PowerShell窗口連接mysql,

在AB窗口中,都設置事務隔離方式為串行化

set session transaction isolation level seriable;

技術分享圖片

首先在A窗口中開啟事務,並查詢users表

技術分享圖片

然後再B窗口中開啟事務,並將zhanghao的money減去100,卻發現窗口卡住了。

技術分享圖片

回到A窗口,提交事務

技術分享圖片

此時B窗口,更新操作順利完成

技術分享圖片

這就是串行化,

即當A連接中的事務訪問一個表的時候,

B連接中的事務想要修改表中的數據,

B連接就會卡住,等待A連接中的事務提交或回滾後,才繼續執行B連接中的修改事務。

即誰先執行的事務,必須等這個事務執行完,才能執行另一個事務。

缺點:性能非常低。

一步步走進Sql中的事務及事務隔離級別