什么是事務逮壁?
事務就是一組應該一起成功或一起失敗的SQL語句。事務應該具備原子性粮宛、一致性窥淆、隔離性和持久性(ACID)的屬性。
- 原子性:所有的SQL語句要么全部成功巍杈,要么全部失敗忧饭,不會存在部分更新。
- 一致性:事務只能以允許的方式改變受其影響的數(shù)據(jù)筷畦。
- 隔離性:同時發(fā)生的事務(并發(fā)事務)不應該導致數(shù)據(jù)庫處于不一致的狀態(tài)中词裤。系統(tǒng)中的每個事務都應該像唯一事務一樣執(zhí)行。任何事務都不應該影響其他事務的存在鳖宾。
- 持久性:無論數(shù)據(jù)庫或系統(tǒng)是否發(fā)生故障吼砂,數(shù)據(jù)都會永久保存在磁盤上,并且不會丟失鼎文。
注意:InnoDB支持事務處理渔肩,而MyISAM不支持事務處理。
如何啟動一個事務(一組SQL)拇惋?
通過執(zhí)行START TRANSACTION或BEGIN語句:
mysql> START TRANSACTION;
或
mysql> BEGIN;
接著周偎,執(zhí)行你希望在事務中包含的所有語句抹剩。確保所有語句執(zhí)行成功后,執(zhí)行COMMIT語句蓉坎,該語句將完成事務并提交數(shù)據(jù):
mysql> COMMIT;
如果遇到錯誤并希望中止事務吧兔,可以發(fā)送ROLLBACK語句而非COMMIT語句:
mysql> ROLLBACK;
autocommit
默認情況下,autocommit的狀態(tài)時ON袍嬉,這意味著所有單獨的語句一旦被執(zhí)行就會被提交,除非該語句在BEGIN……COMMIT塊中灶平。如果autocommit的狀態(tài)為OFF伺通,則需要明確發(fā)出COMMIT語句來提交事務。要禁用autocommit逢享,請執(zhí)行:
mysql> SET autocommit=0;
DDL語句罐监,如數(shù)據(jù)庫的CREATE或DROP語句,以及表或存儲例程的CREATE瞒爬,DROP或ALERT語句弓柱,都是無法回滾的。
部分語句侧但,包括DDL語句矢空、LOAD DATA INFILE、ANALYZE TABLE以及與replication相關的語句禀横,會導致隱式COMMIT屁药。
保存點
使用保存點可以回滾到事務中的某些點,而且無須中止事務柏锄∧鸺可以使用SAVEPOINT標識符為事務設置名稱,并使用ROLLBACK TO標識語句將事務回滾到指定的保存點而不中止事務趾娃。
mysql> BEGIN;
...
mysql> SAVEPOINT transfer_to_b;
...
mysql> ROLLBACK TO transfer_to_b;
...
mysql> COMMIT;
隔離級別
當兩個或多個事務同時發(fā)生時缭嫡,隔離級別定義了一個事務與其他事務在資源或者數(shù)據(jù)修改方面的隔離級別。有四種類型的隔離級別抬闷,要更改隔離級別妇蛀,需要設置tx_isolation變量,該變量是動態(tài)的并具有會話級別的作用范圍饶氏。
如何修改
mysql> SET @@ transaction_isolation='READ-COMMITTED';
四種類型
- 讀未提交
當前事務可以讀取由另一個未提交的事務寫入的數(shù)據(jù)讥耗,這也稱為臟讀。臟讀是不安全的疹启。 - 讀提交
當前事務只能讀取另一個事務提交的數(shù)據(jù)古程,這也稱為不可重復讀取。 - 可重復讀
一個事務通過第一條語句只能看到相同的數(shù)據(jù)喊崖,即使另一個事務已提交數(shù)據(jù)挣磨。在同一個事務中雇逞,讀取通過第一次讀取建立快照是一致的。一個例外是茁裙,一個事務可以讀取在同一事務中更改的數(shù)據(jù)塘砸。即幻讀。 - 序列化
通過把選定的所有行鎖起來晤锥,序列化可以提供最高級別的隔離掉蔬。此級別與REPEATABLE READ類似,但如果禁用autocommit矾瘾,則InnoDB會將所有普通SELECT語句隱式轉換為SELECT ... LOCK IN SHARE MODE女轿;如果啟用autocommit,則SELECT就是它自己的事務壕翩。所以蛉迹,序列化會等待被鎖的行,并且總是讀取最新提交的數(shù)據(jù)放妈。
可重復度需要重點了解一下北救。這種可重復讀僅僅適用于SELECT語句,如果插入或修改某些行并提交該事務芜抒,那么從另一個并發(fā)REPEATABLE READ事務發(fā)出的DELETE或UPDATE語句珍策,可能會影響那些剛剛提交的行,即使會話無法查詢這些語句挽绩。如果事務更新或刪除由不同事務提交的行膛壹,則這些更改對當前事務變?yōu)榭梢姟?/p>
兩種鎖
- 內部鎖:MySQL在自身服務器內部執(zhí)行內部鎖,以管理多個會話對表內容的爭用唉堪。
- 外部鎖:MySQL為客戶會話提供選項來顯式地獲取表鎖模聋,以阻止其他會話訪問表。
其中唠亚,內部鎖可以分為行級鎖與表級鎖链方,外部鎖包括了READ與WRITE。
行級鎖:行級鎖是細粒度的灶搜。只有被訪問的行會被鎖定祟蚀。這允許通過多個會話同時進行寫訪問,使其適用于多用戶割卖、高度并發(fā)和OLTP的應用程序前酿。只有InnoDB支持行級鎖。
表級鎖:MySQL對MyISAM鹏溯、MEMORY和MERGE表使用表級鎖罢维,一次只允許一個會話更新這些表。這種鎖定級別使得這些存儲引擎更適用于只讀的或以讀取操作為主的或單用戶的應用程序丙挽。
共享鎖(READ):當一個表被鎖定為READ時肺孵,多個會話可以從表中讀取數(shù)據(jù)而不需要獲取鎖匀借。此外,多個會話可以在同一個表上獲得鎖平窘。當READ鎖被保持時吓肋,沒有會話可以將數(shù)據(jù)寫入表中。如果有任何寫入嘗試瑰艘,該操作將處于等待狀態(tài)是鬼,直到READ鎖被釋放。
排他鎖(WRITE):當一個表被鎖定為WRITE時紫新,除持有該鎖的會話之外屑咳,其他任何會話都不能讀取或向表中寫入數(shù)據(jù)。除非現(xiàn)有鎖被釋放弊琴,否則其他任何會話都不能獲得任何鎖。如果有任何讀取/寫入嘗試杖爽,該操作將處于等待狀態(tài)敲董,直到WRITE鎖被釋放。
當會話中止慰安,或執(zhí)行UNLOCK TABLES時腋寨,所有鎖都會被釋放
如何使用
鎖定表的語法
mysql> LOCK TABLES table_name [READ | WRITE]
要解鎖表
mysql> UNLOCK TABLES;
要鎖定所有數(shù)據(jù)庫中的所有表,請執(zhí)行以下語句化焕。在獲取數(shù)據(jù)庫的一致快照時需要使用該語句萄窜,它會凍結對數(shù)據(jù)庫的所有寫入操作:
mysql> FLUSH TABLES WITH READ LOCK;
鎖隊列
除共享鎖之外,沒有兩個鎖可以一起加在一個表上撒桨。如果一個表已經(jīng)有一個共享鎖查刻,此時有一個排他鎖要進來,那么它將被保留在隊列中凤类,直到共享鎖被釋放穗泵。當排他鎖在隊列中時,所有后續(xù)的共享鎖也會被阻塞并保留在隊列中谜疤。
當InnoDB從表中讀取/寫入數(shù)據(jù)時會獲取元數(shù)據(jù)鎖佃延。如果第二個事務請求WRITE LOCK,該事務將被保留在隊列中夷磕,直到第一個事務完成履肃。如果第三個事務想要讀取數(shù)據(jù),就必須等到第二個事務完成坐桩。
One More Thing
- 事務的隔離級別越高尺棋,越能保證數(shù)據(jù)的完整性和一致性,但是對并發(fā)性能的影響也越大撕攒。
2018-01-23