目錄
- 事務的必要性
- MySQL中如何控制事務
- 手動開啟事務
- 事務的四大特征
- 事務的四大特征
- 事務開啟方式
- 事務手動提交與手動回滾
- 事務的隔離性
- 臟讀現象
- 不可重復讀現象
- 幻讀現象
- 串行化
- 一些補充
- 使用長事務的弊病
- `commit work and chain`的語法是做什么用的?
- 怎么查詢各個表中的長事務?
- 如何避免長事務的出現?
- 事務隔離是怎么通過read-view(讀視圖)實現的?
- 參考
事務的必要性
mysql中,事務是一個最小的不可分割的工作單元。事務能夠保證一個業務的完整性。
比如我們的銀行轉賬:
-- a -> -100
UPDATE user set money = money - 100 WHERE name = 'a';-- b -> +100
UPDATE user set money = money + 100 WHERE name = 'b';
如果程序中,只有一條語句執行成功了,而另外一條沒有執行成功,就會出現前后不一致。就會有人白嫖。
因此,在執行多條有關聯 SQL 語句時,事務可能會要求這些 SQL 語句要么同時執行成功,要么就都執行失敗。
也就是說事務具有原子性。
MySQL中如何控制事務
1、mysql是默認開啟事務的(自動提交)
默認事務開啟的作用:
-- 查詢事務的自動提交狀態
SELECT @@AUTOCOMMIT;
+--------------+
| @@AUTOCOMMIT |
+--------------+
| 1 |
+--------------+
當我們執行一個sql語句時候,效果會立即體現出來,且不能回滾。
回滾舉例
CREATE DATABASE bank;USE bank;CREATE TABLE user (id INT PRIMARY KEY,name VARCHAR(20),money INT
);INSERT INTO user VALUES (1, 'a', 1000);SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
執行插入語句后數據立刻生效,原因是 MySQL 中的事務自動將它提交到了數據庫中。那么所謂回滾的意思就是,撤銷執行過的所有 SQL 語句,使其回滾到最后一次提交數據時的狀態。
在 MySQL 中使用 ROLLBACK 執行回滾:
由于所有執行過的 SQL 語句都已經被提交過了,所以數據并沒有發生回滾。
-- 回滾到最后一次提交
ROLLBACK;SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
將自動提交關閉后,可以數據回滾:
-- 關閉自動提交
SET AUTOCOMMIT = 0;-- 查詢自動提交狀態
SELECT @@AUTOCOMMIT;
+--------------+
| @@AUTOCOMMIT |
+--------------+
| 0 |
+--------------+
現在我們測試一下:
INSERT INTO user VALUES (2, 'b', 1000);-- 關閉 AUTOCOMMIT 后,數據的變化是在一張虛擬的臨時數據表中展示,
-- 發生變化的數據并沒有真正插入到數據表中。
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+-- 數據表中的真實數據其實還是:
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+-- 由于數據還沒有真正提交,可以使用回滾
ROLLBACK;-- 再次查詢
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
可以使用COMMIT
將虛擬的數據真正提交到數據庫中:
INSERT INTO user VALUES (2, 'b', 1000);
-- 手動提交數據(持久性),
-- 將數據真正提交到數據庫中,執行后不能再回滾提交過的數據。
COMMIT;-- 提交后測試回滾
ROLLBACK;-- 再次查詢(回滾無效了)
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
總結
1、查看自動提交狀態:
select @@AUTOCOMMIT;
2、設置自動提交狀態:set AUTOCOMMIT = 0;
3、手動提交: 在 @@AUTOCOMMIT = 0 時,可以使用commit
命令提交事務
4、事務回滾: 在 @@AUTOCOMMIT = 0 時,可以使用rollback
命令回滾事務
事務給我們提供了一個可以反悔的機會,假設在轉賬時發生了意外,就可以使用 ROLLBACK
回滾到最后一次提交的狀態。假設數據沒有發生意外,這時可以手動將數據COMMIT
到數據表中。
手動開啟事務
可以使用BEGIN
或者 START TRANSACTION
手動開啟一個事務。
-- 使用 BEGIN 或者 START TRANSACTION 手動開啟一個事務
-- START TRANSACTION;
BEGIN;
UPDATE user set money = money - 100 WHERE name = 'a';
UPDATE user set money = money + 100 WHERE name = 'b';-- 由于手動開啟的事務沒有開啟自動提交,
-- 此時發生變化的數據仍然是被保存在一張臨時表中。
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
+----+------+-------+-- 測試回滾
ROLLBACK;SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
當然事務開啟之后,使用commit提交后就不能回滾了。
事務的四大特征
事務的四大特征
A 原子性:事務是最小的單位,不可以分割
C 一致性:事務要求同一事務中的sql語句,必須要保證同時成功或者同時失敗
I 隔離性:事務1 和事務2 之間是具有隔離性的
D 持久性:事務一旦結束(commit or rollback),就不可以返回
事務開啟方式
1、修改默認提交 set autocommit = 0;
2、begin
3、start transaction
事務手動提交與手動回滾
手動提交:commit
手動回滾:rollback
事務的隔離性
事務的隔離性:
1、read uncommitted; 讀未提交的
2、read committed; 讀已經提交的
3、repeatable read; 可以重復讀
4、serializable; 串行化
臟讀現象
在read uncommitted
的隔離級別下:
臟讀:一個事務讀到了另外有一個事務沒有提交的數據
實際開發不允許臟讀出現。
如果有兩個事務 a、b
a事務對數據進行操作,在操作的過程中,事務并沒有被提交,但是b可以看見a操作的結果。b看到轉賬到了,然后就不管了。后面a進行rollback操作,錢又回去了,完成白嫖。
不可重復讀現象
在read committed
的隔離級別下:
小王一開始開啟了一個事務,然后提交了幾個數據,然后出去抽煙。
在他抽煙的時候,小明在其他電腦上開啟了一個事務,然后對那個表提交了一個數據。
小王煙抽完了,然后統計表中數據,發現不對勁。前后不一致了。
幻讀現象
在repeatable read;
的隔離級別下:
事務a和事務b同時操作一張表,事務a提交的數據也不能被事務b讀到,就可以造成幻讀。
可以觀察如下步驟:
小明 在杭州 開啟一個事務;
小王 在北京 開啟一個事務;
小明 對table進行插入數據操作,然后commit;然后查看表,發現操作成功
小王在對table進行插入之前也查看表,然而并沒有小明插入的數據,于是乎他插入了同樣的一條數據,數據庫報錯。
小王很是疑惑,這就是幻讀現象。
串行化
在serializable
的隔離級別下:
當user表被事務a操作的時候,事務b里面的寫操作是不可以進行的,會進入排隊狀態(串行化)。
“讀-讀”在串行化隔離級別允許并發。
直到事務a結束之后,事務b的寫入操作才會執行。
串行化的問題是性能特差。
一般來說,隔離級別越高,性能越差。
MySQL默認隔離級別是:repeatable read;
一些補充
使用長事務的弊病
從存儲空間上來說:
長事務意味著系統里面會存在很老的事務視圖。由于這些事務隨時可能訪問數據庫里面的任何數據,所以這個事務提交之前,數據庫里面它可能用到的回滾記錄都必須保留,這就會導致大量占用存儲空間。
長事務還占用鎖資源,也可能拖垮整個庫。
commit work and chain
的語法是做什么用的?
提交上一個事務,并且再開啟一個新的事務。它的功能等效于:commit + begin
。
怎么查詢各個表中的長事務?
這個表中記錄了所有正在運行的事務信息,里面有事務的開始時間。可以從這里看出哪些事務運行的時間比較長。
select * from information_schema.innodb_trx;
如何避免長事務的出現?
從數據庫方面:
a.設置autocommit=1,不要設置為0。 b.寫腳本監控information_schemal.innodb_trx表中數據內容,發現長事務,kill掉它。 c.配置SQL語句所能執行的最大運行時間,如果查過最大運行時間后,中斷這個事務
從SQL語句方面:
設置回滾表空單獨存放,便于回收表空間
從業務代碼方面:
1、檢查業務邏輯代碼,能拆分為小事務的不要用大事務。
2、檢查代碼,把沒有必要的select語句被事務包裹的情況去掉
事務隔離是怎么通過read-view(讀視圖)實現的?
每一行數有多個版本,當我們要去讀取數據的時候,要判斷這個數據的版本號,對當前事務而言,是否可見,如果不可見,則要根據回滾日志計算得到上一個版本。如果上一個版本也不符合要求,則要找到再上一個版本,
直到找到對應正確的數據版本。
參考
一天學會MySQL
https://time.geekbang.org/column/article/68963