事務的含義
事務:一條或多條 sql 語句組成的一個執行單元,這組 sql 語句是一個整體,要麼都順利執行,要麼都不執行,只要在執行的過程中,有一條 sql 語句執行錯誤就全部回滾或部分回滾。
事務的特性
事務總共有 4 大特性:ACID。
A(Atomicity)原子性
:一個事務是不可再分割的整體,要麼都執行要麼都不執行
C(Consistency)一致性
:一個事務可以使數據從一個一致狀態切換到另外一個一致的狀態。比如 A 和 B 的賬戶上都各有 1000 元,在 A 給 B 轉了 500 元之後,A 的賬戶上 是 500 元,B 的賬戶上是 1500 元,兩人的總額還是 2000 元。
I(Isolation)隔離性
:一個事務不受其他事務的干擾,多個事務互相隔離
D(Durability)持久性
:一個事務一旦提交了,則它對數據庫中數據的改變就是永久性的
事務的使用步驟
首先事務分爲兩類:一種是隱式事務,一種是顯式事務。
像單條的 insert、update、delete 語句,本身都是一條事務,只不過由 MySQL 自動提交了。
而顯式事務則需要我們自己手動的提交或回滾。
使用顯式事務的步驟:
① 首先要開啓事務,將自動提交關閉。
set autocommit = 0;
start transaction; #可以省略
② 編寫一組 sql 語句,在其間可以設置會滾點。
savepoint 回滾點名; #設置回滾點`
③ 結束事務
提交:commit;
回滾:rollback;
回滾到指定的地方:rollback to 回滾點名;
演示 savepoint 的使用:
SET autocommit = 0;
START TRANSACTION;
DELETE FROM account WHERE id=25;
SAVEPOINT a;#設置保存點
DELETE FROM account WHERE id=28;
ROLLBACK TO a;#回滾到保存點
事務的併發問題
當多個事務同時操作同一個數據庫的相同數據時,就會出現事務的併發問題。
那事務的併發問題都有哪些呢?
- 髒讀: 對於兩個事務 T1, T2。T1 讀取了已經被 T2 更新但還沒有被提交的字段。之後,若 T2 回滾,則 T1 讀取的內容就是臨時且無效的。
- 不可重複讀:對於兩個事務T1, T2。T1 讀取了一個字段,然後 T2 更新了該字段。之後,T1再次讀取同一個字段,值就不同了。
- 幻讀:對於兩個事務T1, T2。T1 從一個表中讀取了一個字段, 然後 T2 在該表中插
入了一些新的行。之後, 如果 T1 再次讀取同一個表, 就會多出幾行
事務的隔離級別
對於事務的併發問題,我們可以通過設置不同的隔離級別來解決這些併發問題。
MySQL 支持 4 種隔離級別:
隔離級別 | 描述 |
---|---|
READ UNCOMMITTED(讀未提交數據) | 允許事務讀取未被其他事務提交的變更髒讀,不可重複讀和幻讀的問題都會出現 |
READ COMMITTED(讀已提交數據) | 只允許事務讀取已經被其它事務提交的變更。可以避免髒讀,但不可重複讀和幻讀問題仍然可能出現 |
REPEATABLE READ(可重複讀) | 確保事務可以多次從一個字段中讀取相同的值。可以避免髒讀和不可重複讀,但幻讀的問題仍然存在。 |
SERIALIZABLE(串行化) | 確保事務可以從一個表中讀取相同的行,在這個事務持續期間,禁止其他事務對該表執行插入,更新和刪除操作,所有併發問題都可以避免,但性能十分低下 |
隔離級別越高, 數據一致性就越好, 但併發性越弱。
補充:
Oracle 支持 2 種事務隔離級別:READ COMMITED 和 SERIALIZABLE。Oracle 默認的事務隔離級別爲: READCOMMITED。
Mysql 默認的事務隔離級別爲: REPEATABLE READ。
事務併發問題的演示
每啓動一個 mysql 程序, 就會獲得一個單獨的數據庫連接。每個數據庫連接都有一個全局變量 @@tx_isolation, 表示當前的事務隔離級別。
查看當前的隔離級別: SELECT @@tx_isolation;
設置當前 MySQL 連接的隔離級別:set session transaction isolation level read committed;
設置數據庫系統的全局的隔離級別:set global transaction isolation level read committed;
1、先創建一個表 account,往裏面插入 2 條數據
CREATE TABLE `account` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`username` varchar(20) ,
`balance` int ,
);
INSERT INTO account(username,balance) VALUES('劉備',1000),('關羽',1000);
2、開啓兩個 MySQL 連接,首先設置數據庫的隔離級別爲:read uncommitted;
,演示出現的問題。
3、修改數據庫的隔離級別爲:read committed
,解決了髒讀的問題,但是會出現不可重複讀的問題。
4、修改數據庫的隔離級別爲:repeatable read
,解決了髒讀和不可重複讀的問題,但是會出現幻讀。
5、修改數據庫的隔離級別爲:serializable
,髒讀、不可重複讀、幻讀的問題都解決了。
最後,總結一下,不同的隔離級別都能解決什麼問題:
髒讀 | 不可重複讀 | 幻讀 | |
---|---|---|---|
READ UNCOMMITTED | × | × | × |
READ COMMITTED | √ | × | × |
REPEATABLE READ | √ | √ | × |
SERIALIZABLE | √ | √ | √ |