MySQL事務 先來看一個例子 有一張balance表: 需求:將tom的100塊錢轉到King賬戶中 執行的操作是: update balance set money = money -100 where id = 100 update balance set money = money +100 ...
MySQL事務
先來看一個例子
有一張balance表:
需求:將tom的100塊錢轉到King賬戶中
執行的操作是:
update balance set money = money -100 where id = 100
update balance set money = money +100 where id = 200
這時,如果第一條語句執行成功,但第二條語句執行失敗,就會出現問題。
這裡引出一個需求,將多個dml語句(update,insert,delete)當做一個整體,要麼全部成功,要麼全部失敗
--->使用事務來解決
1.什麼是事務
- 什麼是事務
事務用於保證數據的一致性,它由一組相關的dml語句(update,insert,delete)組成,該組的dml語句要麼全部成功,要麼全部失敗。如:轉賬就要用事務來處理,用以保證數據的一致性。
- 事務和鎖
當執行事務操作時(dml語句),mysql會在表上加鎖,防止其他用戶修改表的數據。這對用戶來講是非常重要的。
- mysql資料庫控制台事務的幾個重要操作(基本操作)
註意:當直接回退到保存點a時,會刪除中間的保存點b
1.start transaction --開始一個事務
2.savepoint 保存點名 -- 設置保存點
3.rollback to 保存點名 -- 回退事務
4.rollback -- 回退全部事務
5.commit -- 提交事務,所有的操作生效,不能回退
細節:
- 沒有設置保存點
- 多個保存點
- 存儲引擎
- 開始事務方式
- 回退事務
在介紹回退事務前,先介紹一下保存點(savepoint)。保存點是事務中的點,用於取消部分事務,當結束事務時(commit),會自動地刪除該事務所定義的所有保存點。
當執行回退事務時,通過指定保存點可以回退到指定的點
- 提交事務
使用commit語句可以提交事務。當執行了commit語句之後,會確認事務的變化、結束事務、刪除保存點、釋放鎖,數據生效。
當使用了commit語句結束事務之後,其它會話[其他連接] 可以查看到事務變化後的新數據 [所有的數據正式生效]
例子
-- 事務的演示操作
-- 1.創建一張測試表
CREATE TABLE t27(
id INT ,
`name` VARCHAR(32)
);
SELECT * FROM t27;
-- 2.開始事務
START TRANSACTION;
-- 3.設置保存點
SAVEPOINT a;
-- 4.執行dml操作1
INSERT INTO t27 VALUES(100,'tom');
-- 設置保存點b
SAVEPOINT b;
-- 執行dml操作2
INSERT INTO t27 VALUES(200,'jack');
-- 回退到b
ROLLBACK TO b
-- 繼續回退a
ROLLBACK TO a
-- 如果是rollback,表示直接回退到事務開始的狀態
ROLLBACK
COMMIT
2.事務註意事項
- 如果不開始事務,預設情況下,dml操作是自動提交的,不能回滾
- 如果開始一個事務,你沒有創建保存點,也可以執行rollback,預設就是回到事務開始的狀態
- 可以在事務中(還沒有提交時),創建多個保存點。比如:savepoint aaa;執行dml,savepoint bbb;
- 可以在事務沒有提交前,選擇回退到哪個保存點
- innodb的存儲引擎支持事務,myisam不支持
- 開始一個事務的方式 start transaction或者set autocommit = off;
例子
-- 討論事務細節
-- 1. 如果不開始事務,預設情況下,dml操作是自動提交的,不能回滾
INSERT INTO t27 VALUES(300,'milan'); -- 自動提交 commit
SELECT * FROM t27;
-- 2. 如果開始一個事務,你沒有創建保存點,也可以執行rollback,
-- 預設就是回到事務開始的狀態
START TRANSACTION
INSERT INTO t27 VALUES(400,'king');
INSERT INTO t27 VALUES(500,'scott');
ROLLBACK -- 表示直接回退到事務開始的狀態
COMMIT
-- 3. 可以在事務中(還沒有提交時),創建多個保存點。
-- 比如:savepoint aaa;執行dml,savepoint bbb;
-- 4. 可以在事務沒有提交前,選擇回退到哪個保存點
-- 5. innodb的存儲引擎支持事務,myisam不支持
-- 6. 開始一個事務的方式 start transaction或者set autocommit = off;
SET autocommit = off
3.事務的四種隔離級別
- 事務隔離級別介紹
- 多個連接開啟各自的事務,操作資料庫中的數據時,資料庫系統要負責隔離操作,以保證各個連接在獲取數據時的準確性。
- 如果不考慮隔離性,可能會引發如下問題:
- 臟讀(dirty read):當一個事務讀取另一個事務尚未提交的改變(delete,insert,update)時,產生臟讀
- 不可重覆讀(nonrepeatable read):同一個查詢在同一事務中多次進行,由於其他已提交事務所做的修改或刪除,每次返回不同的結果集,此時發生不可重覆讀
- 幻讀(phantom read):虛讀,同一查詢在同一事務中多次進行,由於其他已提交事務所做的插入操作,每次返回不同的結果集,此時發生幻讀
- 事務隔離級別
概念:MySQL隔離級別定義了事務與事務之間的隔離程度
MySQL隔離級別(4種) | 臟讀 | 不可重覆讀 | 幻讀 | 加鎖讀 |
---|---|---|---|---|
讀未提交(read uncommitted) | 會出現 | 會出現 | 會出現 | 不加鎖 |
讀已提交(read committed) | 不會出現 | 會出現 | 會出現 | 不加鎖 |
可重覆讀(repeatable read) | 不會出現 | 不會出現 | 不會出現 | 不加鎖 |
可串列化(serializable) | 不會出現 | 不會出現 | 不會出現 | 加鎖 |
可重覆讀實際上會發生幻讀?
3.1讀未提交(read uncommitted)
MySQL的事務隔離級別--案例
我們舉例一個案例來說明mysql的事務隔離級別。以account表進行操作為例。(id、name、money)
-
開啟兩個mysql的控制台
-
查看當前mysql的隔離級別,均為可重覆讀
mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set (0.00 sec)
-
將其中一個連接的隔離級別設置為 read uncommitted(讀未提交)
-- 把其中一個控制台的隔離級別設置為read uncommitted SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
此時的情況:左邊的隔離級別為讀未提交;右邊的隔離級別為可重覆讀
-
然後兩邊都開啟事務
-
在兩個連接控制臺中選擇資料庫,在隔離級別為 可重覆讀 的連接中 創建表account
-
再在隔離級別為 可重覆讀 的連接中插入一條數據(但未提交)
在另一個連接(隔離級別為 讀未提交 READ-UNCOMMITTED)中查詢該表,發現可以查詢到另一事務尚未提交的插入的數據,這時就發生了臟讀
臟讀:當一個事務讀取另一個事務尚未提交的改變(delete,insert,update)時,產生臟讀
-
在隔離級別為 可重覆讀 的連接中更新一條數據,同時插入一條數據,然後提交commit
此時在另一個連接(隔離級別為 讀未提交)中,查詢同一張表,可以看到在這個(隔離級別為 讀未提交的)連接中,已經可以看到另一個事務中提交的數據
即一個事務的操作影響了另一個事務的查詢,這時候就發生了不可重覆讀和幻讀
這將會導致,當有多個連接開啟事務時,某一連接的事務的查詢會受到其他所有連接的事務的影響,這無疑將會導致混亂
最佳情況應該是:一個連接 連接到資料庫,操作account表的時候,希望看到的數據應該是,開啟事務的這一時刻的數據
-
在連接(隔離級別為 讀未提交)中提交commit結束一個事務,此時兩個連接中的事務均已結束
3.2讀已提交(read committed)
例子
-
在上個例子開啟的兩個連接中,將其中一個連接的隔離級別修改為 讀已提交,
另一個保持隔離級別為可重覆讀
-
兩邊都開啟事務
-
在隔離級別為可重覆讀的連接中插入一條數據
然後在隔離級別為讀已提交的連接中 插詢表account,可以看到查詢到的數據還是本連接開啟事務時的數據
即,讀已提交的隔離級別不會出現臟讀現象
-
在隔離級別為可重覆讀的連接中更新一條數據
然後在隔離級別為讀已提交的連接中 插詢表account,可以看到查詢到的數據變成了其他連接的事務提交的 數據,說明,在隔離級別為讀已提交下,出現了不可重覆讀和幻讀
3.3可重覆讀(repeatable read)
-
重新開啟兩個連接,兩個連接的隔離級別均為可重覆讀
-
然後兩邊均開啟事務
-
在一個連接中選擇資料庫,然後在account表中插入一條數據,再更新一條數據(未提交)
此時該連接中的表情況為:
在另一個連接中選擇資料庫,查詢表account,可以看到查詢到的表數據依舊是開啟事務時的樣子,沒有受到其他事務的影響,即沒有產生臟讀
-
在原先修改數據的連接中輸入commit提交
在另一個連接中再查詢表account,可以看到數據依舊是開啟事務的時刻的樣子
即,沒有產生不可重覆讀和幻讀
綜上,隔離級別為可重覆讀的情況下 既不會出現臟讀,也不會出現不可重覆讀和幻讀
3.4可串列化(serializable)
-
將上面兩個連接其中一個重新啟動,將新連接設置隔離級別為可串列化(serializable)
此時兩個連接的隔離級別分別為 可重覆讀 和可串列化(serializable)
-
這時分別在兩個連接中均開啟事務
-
在隔離級別為可重覆讀的連接中分別插入、更新數據(未提交)
在另一個隔離級別為可串列化的連接中選擇資料庫。然後查詢表account,回車時會發現卡住了,這是因為 可串列化會加鎖
A連接在操作表的時候,事務還沒有結束,這時B連接也嘗試操作該表,此時將會檢查A的事務有沒有結束,如果沒有結束,B連接的操作就會進行等待,直到A連接的事務提交
這時,在隔離級別為可重覆讀的連接中提交事務
可以看到可串列化級別的連接中可以成功操作表了
綜上說明,可串列化級別下,不僅不會出現臟讀、不可重覆讀、幻讀,還會加鎖讀