Innodb事務的一些概念

  • 2019 年 11 月 6 日
  • 筆記

innodb之事務
Innodb事務的一些概念
1

事務簡介

事務是由一系列SQL組成的邏輯處理單元,要麼全'部'執行,要麼全'不'執行。事務具有四個屬性,也就是常說的ACID屬性,分別代表原子性,一致性,隔離性和持久性,再重新鞏固一下吧:

原子性(Atomicity):事務是一個原子操作單元,其對數據的修改,要麼全都執行,要麼全都不執行。

一致性(Consistent):在事務開始和完成時,數據都必須保持一致狀態。這意味著所有相關的數據規則都必須應用於事務的修改,以保持數據的完整性;事務結束時,所有的內部數據結構(如B樹索引或雙向鏈表)也都必須是正確的。

隔離性(Isolation):資料庫系統提供一定的隔離機制,保證事務在不受外部並發操作影響的「獨立」環境執行。這意味著事務處理過程中的中間狀態對外部是不可見的,反之亦然。

持久性(Durable):事務完成之後,它對於數據的修改是永久性的,即使出現系統故障也能夠保持。

2

並發事務帶來的問題

事務的運行經常是並發的,因為並發的事務執行效率高於串列執行,然而,並發事務處理也帶來了一些其他的問題。

更新丟失(Lost Update):當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,就會發生丟失更新問題--最後的更 新覆蓋了由其他事務所做的更新。例如,兩個編輯人員製作了同一文檔的電子副本。每個編輯人員獨立地更改其副本,然後保存更改後的副本,這樣就覆蓋了原始文 檔。最後保存其更改副本的編輯人員覆蓋另一個編輯人員所做的更改。如果在一個編輯人員完成並提交事務之前,另一個編輯人員不能訪問同一文件,則可避免此問 題。

臟讀(Dirty Reads):一個事務正在對一條記錄做修改,在這個事務完成並提交前,這條記錄的數據就處於不一致狀態;這時,另一個事務也來讀取同一條記錄,如果不加 控制,第二個事務讀取了這些「臟」數據,並據此做進一步的處理,就會產生未提交的數據依賴關係。這種現象被形象地叫做"臟讀"。

不可重複讀(Non-Repeatable Reads):一個事務在讀取某些數據後的某個時間,再次讀取以前讀過的數據,卻發現其讀出的數據已經發生了改變、或某些記錄已經被刪除了!這種現象就叫做「不可重複讀」。

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

3

事務的隔離級別

在上面講到的並發事務處理帶來的問題中,「更新丟失」通常是應該完全避免的。但防止更新丟失,並不能單靠資料庫事務控制器來解決,需要應用程式對要更新的數據加必要的鎖來解決,因此,防止更新丟失應該是應用的責任。

「臟讀」、「不可重複讀」和「幻讀」,其實都是資料庫讀一致性問題,必須由資料庫提供一定的事務隔離機制來解決。資料庫實現事務隔離的方式,基本上可分為以下兩種。

  一種是在讀取數據前,對其加鎖,阻止其他事務對數據進行修改。

   另一種是不用加任何鎖,通過一定機制生成一個數據請求時間點的一致性數據快照(Snapshot),並用這個快照來提供一定級別(語句級或事務級)的一 致 性讀取。從用戶的角度來看,好像是資料庫可以提供同一數據的多個版本,因此,這種技術叫做數據多版本並發控制(MultiVersion Concurrency Control,簡稱MVCC或MCC),也經常稱為多版本資料庫。

  資料庫的事務隔離越嚴格,並發副作用越 小,但付出的代價也就越大,因為事務隔離實質上就是使事務在一定程度上 「串列化」進行,這顯然與「並發」是矛盾的。同時,不同的應用對讀一致性和事務隔離程度的要求也是不同的,比如許多應用對「不可重複讀」和「幻讀」並不敏 感,可能更關心數據並發訪問的能力。

為了解決「隔離」與「並發」的矛盾,ISO/ANSI SQL92定義了4個事務隔離級別,每個級別的隔離程度不同,允許出現的副作用也不同,應用可以根據自己的業務邏輯要求,通過選擇不同的隔離級別來平衡 「隔離」與「並發」的矛盾。下面的表很好地概括了這4個隔離級別的特性。

需要注意的是,在讀數據的一致性方面,讀未提交是最低級別,只能保證不讀取物理上損壞的數據,不可重複讀是語句級別,可重複度是事務級別,而序列化、也叫串列化是最高級別。mysql默認的事務隔離級別為repeatable-read,可以通過下面的方法查看:

mysql> select @@tx_isolation;  +-----------------+  | @@tx_isolation  |  +-----------------+  | REPEATABLE-READ |  +-----------------+  1 row in set, 1 warning (0.03 sec)  

關於事務概念的介紹大概就這麼多,最後補充幾個需要關注的點:

1、事務隔離級別為讀提交時,寫數據只會鎖住相應的行

2、事務隔離級別為可重複讀時,如果檢索條件有索引(包括主鍵索引)的時候,默認加鎖方式是next-key 鎖;如果檢索條件沒有索引,更新數據時會鎖住整張表。一個間隙被事務加了鎖,其他事務是不能在這個間隙插入記錄的,這樣可以防止幻讀。

3、事務隔離級別為串列化時,讀寫數據都會鎖住整張表

4、隔離級別越高,越能保證數據的完整性和一致性,但是對並發性能的影響也越大。

4

MySQL中事務的處理語法

事務控制語句:

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。

MYSQL 事務處理主要有兩種方法:

1、用 BEGIN, ROLLBACK, COMMIT來實現

BEGIN 開始一個事務

ROLLBACK 事務回滾

COMMIT 事務確認

2、直接用 SET 來改變 MySQL 的自動提交模式:

SET AUTOCOMMIT=0 禁止自動提交

SET AUTOCOMMIT=1 開啟自動提交

最後給出一個測試用例用來方便理解:

mysql> use test_db;  Database changed  mysql> CREATE TABLE tbl_test( id int(5)) engine=innodb;  # 創建數據表  Query OK, 0 rows affected (0.04 sec)    mysql> select * from tbl_test;  Empty set (0.01 sec)    mysql> begin;  # 開始事務  Query OK, 0 rows affected (0.00 sec)    mysql> insert into tbl_test values (5);  Query OK, 1 rows affected (0.01 sec)    mysql> insert into tbl_test values (6);  Query OK, 1 rows affected (0.00 sec)    mysql> commit; # 提交事務  Query OK, 0 rows affected (0.01 sec)    mysql>  select * from tbl_test;  +------+  | id   |  +------+  | 5    |  | 6    |  +------+  2 rows in set (0.01 sec)    mysql> begin;    # 開始事務  Query OK, 0 rows affected (0.00 sec)    mysql>  insert into tbl_test values(7);  Query OK, 1 rows affected (0.00 sec)    mysql> rollback;   # 回滾  Query OK, 0 rows affected (0.00 sec)    mysql>   select * from tbl_test;   # 因為回滾所以數據沒有插入  +------+  | id   |  +------+  | 5    |  | 6    |  +------+  2 rows in set (0.01 sec)    

後續還將用一篇文章對事務的四種隔離級別做相應的示例說明。