(十三)事務處理


1、概述

??事務是數據庫區別于文件系統的重要特征之一,其用于保證數據庫的完整性,事務能使批量的SQL語句要么完全執行,要么完全不執行。

??例如最簡單的轉賬示例:假設用戶A要給用戶B轉賬100元,用戶A的轉賬指令已經成功發出,而用戶B由于未知的原因接收失敗,如果兩個命令單獨執行,那么用戶A的賬戶少了100元,但是用戶B又沒收到這100元,顯然是不合理的;此時將用戶A向用戶B轉賬100元的這個過程當成一個事務處理,那么由于用戶B接收的失敗,整個轉賬事務都將失敗,用戶A不會減少100元,用戶B更不會增加100元,這才是合理的情況。

??事務的特性有以下幾點:

  • 原子性(Atomicity):事務對數據的修改,要么全部執行,要么全部不執行;
  • 一致性(Consistency):在事務開始和完成時,數據都必須保持一致狀態。這意味著所有相關的數據規則都必須應用于事務的修改,以保持數據的完整性;
  • 隔離性(Isolation):數據庫系統提供一定的隔離機制,保證事務在不受外部并發操作影響的“獨立”環境執行;
  • 持久性(Durability):事務完成之后,它對于數據的修改是永久性的,即使出現系統故障也能夠保持。

2、控制事務處理

??事務處理的關鍵在于將SQL語句組分解為邏輯塊,并明確規定在數據何時應該回退,何時不應該回退。

顯式的開啟一個事務

??START TRANSACTION;
??或
??BEGIN;

回滾

??回滾會結束用戶的事務,并撤銷正在進行的所有未提交的修改,分為直接回滾和部分回滾:

  • 直接回滾:會回滾到設置BEGIN之前的狀態。
    ROLLBACK;

  • 部分回滾:通過設置保存點,回滾到保存點設置前的狀態。
    設置一個事務保存點及標識符名稱:
    SAVEPOINT identifier;
    回滾到設置保存點之前的狀態:
    ROLLBACK TO SAVEPOINT identifier;
    從當前事務的一組保存點中刪除指定的保存點:
    RELEASE SAVEPOINT identifier;
    使用COMMIT或ROLLBACK結束事務后,所有保存點會被刪除。

提交事務

??使已對數據庫進行的所有修改成為永久性的,分為自動提交和手動提交:

  • 自動提交:MySQL默認是自動提交的,即執行SQL語句后就會馬上執行COMMIT操作。
  • 手動提交:如果正在使用的是一個事務安全型的存儲引擎,如“InnoDB”,那么當顯式的開啟事務后,MySQL的自動提交會被禁用,直到使用COMMIT或ROLLBACK結束事務后,MySQL會恢復為自動提交。
    查詢當前自動提交功能狀態:
    SELECT @@AUTOCOMMIT;
    改變MySQL的自動提交模式:
    SET AUTOCOMMIT=0; 禁用
    SET AUTOCOMMIT=1; 啟用
    僅對當前連接生效。

3、案例演示

??創建數據表“users”,并查看其存儲引擎為“InnoDB”:


??查看此時的自動提交狀態,并修改為禁用自動提交:


??禁用自動提交后,查看數據表記錄,此時為空,添加兩條記錄,顯示成功,但注意目前并未提交,即這兩條記錄沒有被存儲到磁盤中,因此調用另一個MySQL窗口查看該表時,其數據仍然為空:


??此時執行COMMIT語句,記錄被寫入磁盤,調用其他的MySQL窗口也可以查詢到數據:


??若此時執行ROLLBACK語句,則會回滾到添加記錄之前,查詢數據,顯示為空:


注意:
??之所以沒有回到創建數據表之前的狀態,而僅僅是記錄消失,是因為有些SQL語句會隱式的結束一個事務,即使用了COMMIT語句。
??例如:
ALTER FUNCTION、ALTER PROCEDURE、ALTER TABLE
BEGIN
CREATE DATABASE、CREATE FUNCTION、CREATE INDEX、CREATE PROCEDURE、CREATE TABLE
DROP DATABASE、DROP FUNCTION、DROP INDEX、DROP PROCEDURE、DROP TABLE
LOAD MASTER DATA、LOCK TABLES
RENAME TABLE
SET AUTOCOMMIT=1
START TRANSACTION
TRUNCATE TABLE
UNLOCK TABLES

??之后修改為自動提交,添加兩條記錄后,數據會被直接寫入磁盤,調用其他的MySQL窗口也可以查詢到數據:


??此時顯示的創建一個事務,會默認禁用自動提交,因此添加記錄后,在其他的MySQL窗口無法查詢到數據:


??在添加用戶“Alice”后,創建保存點“sp1”;之后清空數據表記錄,查詢后顯示該表記錄為空,回滾到保存點“sp1”的位置,再次查詢數據后,顯示用戶“Alice”也在數據表中;此時執行ROLLBACK語句,會直接回滾到創建事務時的狀態,即添加用戶“Alice”之前,因此查詢數據顯示只有4條記錄:


注意:
??有些語句不能被回滾,通常包括數據定義語言語句(DDL),例如創建或取消數據庫的語句,和創建、取消或更改表或存儲的子程序的語句。
??因此在設計事務時,不應包含這類語句。如果在事務的前部調用了一個不能被回滾的語句,則后部的其它語句會發生錯誤,在這些情況下,通過使用ROLLBACK語句不能回滾事務的全部效果。


4、鎖定機制

??上述操作都是針對單個連接進行的,但實際會存在多個連接同時操作數據的情況,舉例來說,如果用戶A和用戶B幾乎同時登錄到訂票APP,發現某場次電影的最佳觀影位置只剩最后一張票,于是用戶都紛紛下單,此時沖突就產生了。對于這種當多個連接對記錄進行修改的操作,為保證數據的一致性和完整性,需要用到鎖系統:

  • 共享鎖(讀鎖):
    在同一時間段內,多個用戶可以讀取同一個資源,讀取過程中數據不會有任何變化;
  • 排他鎖(寫鎖):
    在任何時候只能有一個用戶寫入資源,當進行寫鎖時會阻塞其他的讀鎖或寫鎖操作。

語法結構
LOCK TABLES
tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}
[, tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}] ...
UNLOCK TABLES

注意:
??寫鎖定通常比讀鎖定擁有更高的優先權,以確保更新被盡快地處理。這意味著,如果一個線程獲得了一個讀鎖定,則另一個線程會申請一個寫鎖定,后續的讀鎖定申請會等待,直到寫線程獲得鎖定并釋放鎖定。此時可以使用LOW_PRIORITY WRITE鎖定來允許其它線程在該線程正在等待寫鎖定時獲得讀鎖定。前提是當此時沒有線程擁有讀鎖定時,才應該使用LOW_PRIORITY WRITE鎖定。

??對于鎖系統還需要了解鎖的粒度,即鎖定時的單位。原則上只需要對待修改的數據精確加鎖即可,而不是所有資源都枷鎖,以減少系統的開銷:

  • 表鎖,是一種占有系統開銷最小的鎖策略;
  • 行鎖,是一種占有系統開銷最大的鎖策略。

5、隔離級別

??在SQL的標準中,定義了四種隔離級別。每一種級別都規定了,在一個事務中所做的修改,哪些在事務內和事務間是可見的,哪些是不可見的:

  1. READ UNCOMMITTED (未提交讀) :可以讀取未提交的記錄,會出現臟讀。
  2. READ COMMITTED (提交讀) :事務中只能看到已提交的修改。但不可重復讀,會出現幻讀。(在InnoDB中,會加行鎖,但是不會加間隙鎖)該隔離級別是大多數數據庫系統的默認隔離級別。
  3. REPEATABLE READ (可重復讀) :在InnoDB中是這樣的:RR隔離級別保證對讀取到的記錄加鎖 (行鎖),同時保證對讀取的范圍加鎖,新的滿足查詢條件的記錄不能夠插入 (間隙鎖),因此不存在幻讀現象,但是標準的RR只能保證在同一事務中多次讀取同樣記錄的結果是一致的,而無法解決幻讀問題。InnoDB的幻讀解決是依靠MVCC的實現機制做到的,該隔離級別是MySQL的默認隔離界別。
  4. SERIALIZABLE (可串行化):該隔離級別會在讀取的每一行數據上都加上鎖,退化為基于鎖的并發控制,即LBCC。
隔離級別 臟讀 不可重復讀 幻讀
READ UNCOMMITTED
READ COMMITTED ×
REPEATABLE READ × ×
SERIALIZABLE × × ×

查看隔離級別
全局隔離:SELECT @@global.tx_isolation;
會話隔離:SELECT @@tx_isolation;

修改隔離級別
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL
{READ UNCOMMITTED | READ COMMITTED
| REPEATABLE READ | SERIALIZABLE};

??低級別的隔離可以執行更高級別的并發,性能好,但是會出現臟讀、幻讀等錯誤的現象:

更新丟失

??當兩個或多個事務選擇同一條記錄,然后基于最初選定的值更新該行時,由于每個事務都不知道其他事務的存在,就會發生更新丟失的問題,即最后的更新覆蓋了之前由其他事務所做的更新。

臟讀

??一個事務正在對一條記錄做修改,在這個事務完成并提交前,這條記錄的數據就處于不一致狀態;此時,另一個事務也來讀取同一條記錄,如果不加控制,第二個事務會讀取到尚未提交的臟數據,并據此做進一步的處理,這種現象被形象地叫做"臟讀"。

左側為A窗口,右側為B窗口
左側為A窗口,右側為B窗口

??如案例所示,將A窗口隔離級別修改為允許臟讀后,顯式創建事務,并修改用戶名,此時A窗口始終未執行提交指令,意味著數據其實并沒有寫入磁盤,因此在B窗口首次查詢時并沒有出現修改后的記錄,但是將B窗口的隔離級別也修改至允許臟讀后,再次查詢發現臟數據被讀出來了。

不可重復讀

??一個事務在讀取某些數據后,再次讀取之前讀過的數據,卻發現這些數據已經被另一個已提交的事務修改過,這種現象被稱為“不可重復讀”。

左側為A窗口,右側為B窗口
左側為A窗口,右側為B窗口

??如案例所示,將A窗口隔離級別修改為允許不可重復讀后,顯式創建事務,并修改用戶名,假設在A窗口執行提交指令前,B窗口進行了第一次查詢,顯示的是原始記錄,但在B窗口查詢不久后,A窗口執行了提交指令,因此當B窗口再次查詢相同的數據時,數據發生了變化,即為不可重復讀。

幻讀

??一個事務按相同的查詢條件重新讀取以前檢索過的數據,卻發現其他事務插入了滿足其查詢條件的新數據,這種現象就稱為“幻讀”。

左側為A窗口,右側為B窗口
左側為A窗口,右側為B窗口

??如案例所示,將A窗口隔離級別修改為允許幻讀后,顯式創建事務,并添加新用戶,假設在A窗口執行提交指令前,B窗口進行了第一次查詢,顯示的是原始記錄,但在B窗口查詢不久后,A窗口執行了提交指令,因此當B窗口再次執行相同的查詢指令時,出現了新的記錄,即為幻讀。


5、死鎖

??死鎖是指兩個或者多個事務在同一資源上相互作用,并請求鎖定對方占用的資源,從而導致惡性循環的現象。當多個事務試圖以不同的順序鎖定資源時,就可能產生死鎖。多個事務同時鎖定同一個資源時,也會產生死鎖。

左側為A窗口,右側為B窗口
左側為A窗口,右側為B窗口

??如案例所示,在A窗口顯式創建事務,并修改id為3的用戶名稱,之后在B窗口顯式創建事務并修改id為6的用戶名稱,此時在A窗口再次修改id為6的用戶名稱,A窗口會等待B窗口提交后釋放鎖定,在B窗口修改id為3的用戶名稱,B窗口也會等待A窗口提交后釋放鎖定,此時互相等待陷入死鎖,這時MySQL會使B窗口強制解除鎖定,使A窗口繼續執行。


6、事務處理的SQL語句匯總

  • 顯式的開啟一個事務
    START TRANSACTION;

    BEGIN;

  • 回滾
    直接回滾:
    ROLLBACK;
    部分回滾:
    設置一個事務保存點及標識符名稱:SAVEPOINT identifier;
    回滾到設置保存點之前的狀態:ROLLBACK TO SAVEPOINT identifier;
    從當前事務的一組保存點中刪除指定的保存點:RELEASE SAVEPOINT identifier;

  • 提交事務
    COMMIT;
    查詢當前自動提交功能狀態:
    SELECT @@AUTOCOMMIT;
    改變MySQL的自動提交模式:
    SET AUTOCOMMIT=0; 禁用
    SET AUTOCOMMIT=1; 啟用

  • 鎖定
    LOCK TABLES
    tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}
    [, tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}] ...
    UNLOCK TABLES

  • 隔離級別
    查看隔離級別:
    全局隔離:SELECT @@global.tx_isolation;
    會話隔離:SELECT @@tx_isolation;
    修改隔離級別:
    SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL
    {READ UNCOMMITTED | READ COMMITTED
    | REPEATABLE READ | SERIALIZABLE};


版權聲明:歡迎轉載,歡迎擴散,但轉載時請標明作者以及原文出處,謝謝合作!             ↓↓↓
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,362評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,013評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,346評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,421評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,146評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,534評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,585評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,767評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,318評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,074評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,258評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,828評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,486評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,916評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,156評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,993評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,234評論 2 375

推薦閱讀更多精彩內容