一、(了解)定義
全稱(Transaction Control Language)翻譯成中文 事務控制語言,事務是訪問并可能更新數據庫各種數據項的一個程序執行單元,
是并發控制的單元,是用戶定義的一個操作序列。這些操作要么都做,要么都不做,是一個不可分割的工作單位。通過事務,sql 能將邏輯相關的一組操作綁定在一起,以便服務器 保持數據的完整性。
二、 (了解)為什么要事務
設想網上購物的一次交易,其付款過程至少包括以下幾步數據庫操作:
- 更新客戶所購商品的庫存信息
- 保存客戶付款信息--可能包括與銀行系統的交互
- 生成訂單并且保存到數據庫中
- 更新用戶相關信息,例如購物數量等等
正常的情況下,這些操作將順利進行,最終交易成功,與交易相關的所有數據庫信息也成功地更新。但是,如果在這一系列過程中任何一個環節出了差錯,例如在更新商品庫存信息時發生異常、該顧客銀行帳戶存款不足等,都將導致交易失敗。一旦交易失敗,數據庫中所有信息都必須保持交易前的狀態不變,比如最后一步更新用戶信息時失敗而導致交易失敗,那么必須保證這筆失敗的交易不影響數據庫的狀態--庫存信息沒有被更新、用戶也沒有付款,訂單也沒有生成。否則,數據庫的信息將會一片混亂而不可預測。
數據庫事務正是用來保證這種情況下交易的平穩性和可預測性的技術
默認情況下會自動提交,也就是說每個SQL語句都是在其完成時提交到數據庫。
事務只針對DDL操作
三、(熟練掌握)事務的特性(ACID)
1、案例
A賬戶向B賬號匯錢的例子來說明如何通過數據庫事務保證數據的準確性和完整性
1、從A賬號中把余額讀出來(500)
2、對A賬號做減法操作(500-100)
3、把結果寫回A賬號中(400)
4、從B賬號中把余額讀出來(500
5、對B賬號做加法操作(500+100)
6、把結果寫回B賬號中(600)
2、原子性(Atomicity)
- 概念
事務是數據庫的邏輯工作單位,而且是必須是原子工作單位,對于其數據修改,要么全部執行,要么全部不執行。 - 說明
保證1-6所有過程要么都執行,要么都不執行。一旦在執行某一步驟的過程中發生問題,就需要執行回滾操作。 假如執行到第五步的時候,B賬戶突然不可用(比如被注銷),那么之前的所有操作都應該回滾到執行事務之前的狀態
3、一致性(Consistency)
-
概念
指在事務開始之前和事務結束以后,數據庫的完整性約束沒有被破壞。也就是說數據庫事務不能破壞關系數據的完整性以及業務邏輯上的一致性。
**注: **
業務邏輯上的一致性 由開發人員進行保證。
數據庫層面 在一個事務執行之前和之后,數據會符合你設置的約束(唯一約束,外鍵約束,check約束等)和觸發器設置,并且同一個事務內部的一組操作必須全部執行成功(原子操作)
但是,原子性并不能完全保證一致性。在多個事務并行進行的情況下,即使保證了每一個事務的原子性,仍然可能導致數據不一致的結果。為了保證并發情況下的一致性,引入了隔離性
-
說明
在轉賬之前,A和B的賬戶中共有500+500=1000元錢。在轉賬之后,A和B的賬戶中共有400+600=1000元。也就是說,數據的狀態在執行該事務操作之后從一個狀態改變到了另外一個狀態。同時一致性還能保證賬戶余額不會變成負數等
4、隔離性(Isolation)
-
概念
也稱為獨立性,是指并行事務的修改必須與其他并行事務的修改相互獨立。一個事務處理數據,要么是其他事務執行之前的狀態,要么是其他事務執行之后的狀態,但不能處理其他正在處理的數據。
企業級的數據庫每一秒鐘都可能應付成千上萬的并發訪問,因而帶來了并發控制的問題。 -
說明
在A向B轉賬的整個過程中,只要事務還沒有提交(commit),查詢A賬戶和B賬戶的時候,兩個賬戶里面的錢的數量都不會有變化。
如果在A給B轉賬的同時,有另外一個事務執行了C給B轉賬的操作,那么當兩個事務都結束的時候,B賬戶里面的錢應該是A轉給B的錢加上C轉給B的錢再加上自己原有的錢
5、持久性(Durability)
-
概念
一個事務一旦提交,事物的操作便永久性的保存在DB中。即使此時再執行回滾操作也不能撤消所做的更改
-
說明
一旦轉賬成功(事務提交),兩個賬戶的里面的錢就會真的發生變化(會把數據寫入數據庫做持久化保存)
四、(熟練掌握)基本操作
1、事務常用的語句
語句 | |
---|---|
BEGIN或START TRANSACTION | 顯式地開啟一個事務; |
COMMIT | 也可以使用COMMIT WORK,不過二者是等價的。COMMIT會提交事務,并使已對數據庫進行的所有修改成為永久性的; |
ROLLBACK | 有可以使用ROLLBACK WORK,回滾會結束用戶的事務,并撤銷正在進行的所有未提交的修改; |
SAVEPOINT identifier | SAVEPOINT允許在事務中創建一個保存點,一個事務中可以有多個SAVEPOINT; |
RELEASE SAVEPOINT identifier | 刪除一個事務的保存點,當沒有指定的保存點時,執行該語句會拋出一個異常; |
ROLLBACK TO identifier; | 把事務回滾到標記點; |
SET TRANSACTION | 用來設置事務的隔離級別。InnoDB存儲引擎提供事務的隔離級別有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。 |
2、MYSQL 事務處理主要有兩種方法:
- 用 BEGIN, ROLLBACK, COMMIT來實現
- BEGIN 開始一個事務
- ROLLBACK 事務回滾
- COMMIT事務提交
- 直接用 SET 來改變 MySQL 的自動提交模式
- SET AUTOCOMMIT=0 | off 禁止自動提交
- **SET AUTOCOMMIT=1 | on ** 開啟自動提交
3 、案例
- 前期準備工作
-- 創建銀行賬戶表 create table account( aid int primary key auto_increment comment '主鍵', card_no varchar(16) not null unique comment '銀行卡號', name varchar(20) not null comment '姓名', money decimal(10,2) default 0.0 comment '金額' ) -- 插入數據 insert into account (card_no,name,money) values ('1', '小明', 1000), ('2', '嬌嬌', 1000);
- 第一步 開始事務
-- 告訴系統以下所有操作,不要直接寫入數據庫,先存到事務日志。 BEGIN
- 第二步 減少賬戶的余額
update account set money = money - 1000 where cardno =1
- 第三步 增加賬戶的余額
update account set money = money + 1000 where cardno = 2;
- 第四步 提交事務
commit;
- 第五步 或者回滾
rollback
五、(掌握)事務隔離 - 并發控制
數據庫事務處理相關命令
操作命令 | 說明 |
---|---|
SHOW CREATE TABLE 表名; | 查看存儲引擎 |
SET AUTOCOMMIT=0或1;或者 SET AUTOCOMMIT=off 或 on | 設置是否自動提交 |
SELECT @@AUTOCOMMIT; 或者 show variables like '%commit%'; | 查詢自動提交功能狀態 |
SELECT @@tx_isolation; | 查看事務隔離級別 |
SET tx_isolation='READ-UNCOMMITTED'; | 設置事務的隔離級別 |
1、并發控制
數據庫管理系統(DBMS)中的并發控制的任務是確保在多個事務同時存取數據庫中同一數據時不破壞事務的隔離性和統一性以及數據庫的統一性
2、不考慮事務的隔離性,會出現什么問題?
- 臟讀:一個事務讀取到另一個事務的未提交數據
- 不可重復讀:兩次讀取的數據不一致(強調update 針對單行)
- 虛讀(幻讀):兩次讀取的數據不一致(強調insert或delete,范圍查詢)
- 丟失更新:撤銷一個事務時,把其他事務已提交的更新數據覆蓋(事務A和B并發執行,A事務執行更新后,提交;B事務在A事務更新后,B事務結束前也做了對該行數據的更新操作,然后回滾,則兩次更新操作都丟失了)
3、四種隔離級別
- Read-uncommitted:最低級別,以上情況均無法保證。(讀未提交)
- Read-committed:可避免臟讀情況發生(讀已提交)
- Repeatable-read:可避免臟讀、不可重復讀情況的發生。(可重復讀)不可以避免虛讀
- Serializable:可避免臟讀、不可重復讀、虛讀情況的發生。(序列化,不僅有read、write鎖還有range lock范圍鎖(沒有where鎖全表,有where鎖where范圍);對一張表的所有增刪改操作必須順序執行,性能最差)
| 隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
| ---------------- | ---- | ---------- | ---- |
| Read uncommitted | √ | √ | √ |
| Read committed | × | √ | √ |
| Repeatable read | × | × | √ |
| Serializable | × | × | × |
六、(掌握)隔離級別詳解
1、Read uncommitted
- 作用
所有事務都可以看到其他未提交事務的執行結果 - 舉個栗子
又到月底了,小明的老婆要準備給小明發生活費了,小明的老婆給小明打了500塊,但該事務并沒有提交,而此時小明正好在查余額,發現是550塊,高興的差點蹦了起來.天有不測風云,突然小明的老婆發現多打了50塊,于是回滾事務,修改金額,然后將事務提交,最后小明空歡喜異常。 -
示例圖
image - 示例代碼
-- ****打開兩個窗口 事務A窗口**** -- 1.查看事務隔離級別 SELECT @@TX_ISOLATION; +------------------+ | @@tx_isolation | +------------------+ | READ-UNCOMMITTED | +------------------+ -- 2.設置事務的隔離級別為讀取未提交 SET tx_isolation='READ-UNCOMMITTED'; -- 3.查看自動提交狀態 SELECT @@AUTOCOMMIT; -- 4.如果自動提交開啟,關閉自動提交 SET AUTOCOMMIT = 0; -- ****在事務A中執行更新語句,且不提交**** -- 5. 開啟事務 START TRANSACTION; -- 6. 將賬戶小明的賬號的錢減100 UPDATE account SET money = money - 100 WHERE aid = 1; -- 不要提交 切換到事務B的窗口 -- 7. 事務回滾 ROLLBACK
-- 事務B窗口 -- 1. 查看事務的隔離級別 SELECT @@TX_ISOLATION; -- 2.設置事務的隔離級別 SET TX_ISOLATION = 'READ-UNCOMMITTED'; -- 3.查詢小明的賬號 SELECT * from account WHERE aid = 1 -- ****顯示的信息**** +-----+---------+--------+--------+ | aid | card_no | name | money | +-----+---------+--------+--------+ | 1 | 1 | 小明 | 700.00 | +-----+---------+--------+--------+ -- 4. 將事務隔離級別設置成讀取已提交或者其他 SET TX_ISOLATION = 'READ-COMMITTED'; -- 5. 在次查詢發現賬號余額是800 SELECT * from account WHERE aid = 1; -- ****顯示的信息**** +-----+---------+--------+--------+ | aid | card_no | name | money | +-----+---------+--------+--------+ | 1 | 1 | 小明 | 800.00 | +-----+---------+--------+--------+ -- 切回事務B 使用回滾 rollback
2、Read committed
-
作用
一個事務只能看見已經提交事務所做的改變
-
舉個栗子
某個夜黑風高的夜晚,小明豐富的夜生活開始了,小明拿著工資卡去消費,pos機讀取卡的信息的時候有500,
而此時小紅也正好在網上轉賬,把小明工資卡的500元轉到另一賬戶,并小明之前提交了事務,當小明扣款時,
系統檢查到小明的工資卡已經沒有錢,扣款失敗,小明十分納悶,明明卡里有錢,為什么會說余額不足,
出現上述情況,即我們所說的不可重復讀,兩個并發的事務,“事務1:小明消費”、“事務2:小紅網上轉賬”,事務1事先讀取了數據,
事務2緊接了更新了數據,并提交了事務,而事務1再次讀取該數據時,數據已經發生了改變,
當隔離級別設置為Read committed時,避免了臟讀,但是可能會造成不可重復讀 -
示意圖
image -
備注
Sql Server ,Oracle的默認級別
-
示例代碼
-- 事務1 -- 1. 設置隔離級別為讀取已提交 SET TX_ISOLATION = 'READ-COMMITTED'; -- 2. 查看當前連接的事務級別 SELECT @@TX_ISOLATION; +----------------+ | @@TX_ISOLATION | +----------------+ | READ-COMMITTED | -- 3. 關閉自動提交 SET AUTOCOMMIT = 0; -- 4. 關閉自動提交 SELECT @@AUTOCOMMIT; -- 5.開啟事務 START TRANSACTION; -- 6.賬號余額-100 UPDATE account SET account.money= money - 100 WHERE aid = 1; -- 7. 提交事務 COMMIT
-- 事務1 -- 1. 設置隔離級別為讀取已提交 SET TX_ISOLATION = 'READ-COMMITTED'; -- 2. 查看當前連接的事務級別 SELECT @@TX_ISOLATION; -- 3. 關閉自動提交 SET AUTOCOMMIT = 0; -- 4. 關閉自動提交 SELECT @@AUTOCOMMIT; -- 5. 事務1 ******還沒有提交事務**** -- 查詢賬戶信息 SELECT * FROM account WHERE aid = 1 +-----+---------+--------+---------+ | aid | card_no | name | money | +-----+---------+--------+---------+ | 1 | 1 | 小明 | 1000.00 | +-----+---------+--------+---------+ -- 切換到事務1 提交事務 -- 5. 此時事務已經提交 兩次查詢的結果不一致 SELECT * FROM account WHERE aid = 1 +-----+---------+--------+--------+ | aid | card_no | name | money | +-----+---------+--------+--------+ | 1 | 1 | 小明 | 900.00 | +-----+---------+--------+--------+ -- 相同的select語句,結果卻不一樣
3、Repeatable read
-
說明
當用戶讀取某一范圍的數據行時,另一個事務又在該范圍內插入了新行,當用戶再讀取該范圍的數據行時
-
舉個栗子
小紅最近發現小明總是很晚回家并且經常不接電話,于是小紅開始查小明當月信用卡的總消費金額,
消費金額為50,而小明此時正好在收銀臺買單,消費1000元,即新增了一條1000元的消費記錄,并提交了事務,
隨后小紅將小明當月信用卡消費的明細打印了出來,卻發現消費總額為1050元,小紅很詫異,以為出現了幻覺 -
示例圖
image -
備注
MySQL的默認隔離級別
-
區別
- 不可重復讀的重點是修改比如多次讀取一條記錄發現其中某些列的值被修改,
- 幻讀的重點在于新增或者刪除比如多次范圍讀取發現記錄增多或減少了。
-
示例代碼
-- 事務1 -- 設置隔離級別 SET TX_ISOLATION = 'read-committed'; -- 關閉自動提交 SET AUTOCOMMIT = 0; -- 開啟事務 BEGIN; -- 插入數據 INSERT INTO account(card_no, name, money) VALUES ('9527', '老李', 0.00); -- 切換到事務 2
-- 事務 2 SET TX_ISOLATION = 'read-committed'; SET AUTOCOMMIT = 0; -- 事務未提交之前 SELECT * from account -- -- SELECT * from account
image
4、Serializable(禁止使用)
-
說明
最高級別:防止上述3種情況,事務串行執行,慎用
這是最高的隔離級別,它通過強制事務排序,使之不可能相互沖突,從而解決不讀臟,可重復讀,不可幻讀。
簡言之,它是在每個讀的數據行上加上共享鎖。在這個級別,可能導致大量的超時現象和鎖競爭,并發性能最差,在分布式事務中可能會被用到
七、(了解)更新丟失問題
1、概要
由于RDBMS都有鎖機制,所以在并發事務中不存在更新丟失問題
1>加鎖階段:在該階段可以進行加鎖操作。在對任何數據進行讀操作之前要申請并獲得S鎖,在進行寫操作之前要申請
2>并獲得X鎖。
加鎖不成功,則事務進入等待狀態,直到加鎖成功才繼續執行。
解鎖階段:當事務釋放了一個封鎖以后,事務進入解鎖階段,在該階段只能進行解鎖操作不能再進行加鎖操作
2、分類
- 第一種情況丟失更新:A事務提交時,把已提交的B事務的數據覆蓋掉。
- 第二鐘情丟失更新:A事務回滾時,把已提交的B事務的數據覆蓋掉。