1. 程式人生 > >【MySql 04】事務

【MySql 04】事務

功能

當多個使用者訪問同一份資料時,一個使用者在更改資料的過程中可能有其他使用者同時發起更改請求,為保證資料的更新從一個一致性狀態變更為另一個一致性狀態,就需要用到事務。

特性

事務有四個特性:原子性、一致性、隔離性、永續性。下面一一介紹。

  • 原子性:事務中所有的操作視為一個原子單元,即對於事務所進行的資料修改操作等操作只能是完全執行或完全回滾。

  • 一致性:事務在完成時,必須使所有的資料從一種一致性狀態變更為另外一種一致性狀態。

  • 隔離性:一個事務中的操作語句所做的修改與其他事務所做的修改相隔離。

  • 永續性:事務完成之後,對資料的修改時永久的,即使系統故障也不會丟失。

控制語句

BEGIN:也可以使用START TRANSACTION,它用來顯式地開啟一個事務。

COMMIT:也可以使用COMMIT WORK,二者是等價的。它用來提交事務,對資料庫進行永久性的修改。

ROLLBACK:也可以使用ROLLBACK WORK,二者是等價的。它用來結束事務,並撤銷正在進行的所有未提交的修改。

SET AUTOCOMMIT = 0 :禁止自動提交

SET AUTOCOMMIT = 1 :開啟自動提交

示例

mysql> use test;
Database changed
mysql> CREATE TABLE shiwu( id int(5)) engine=innodb;
Query OK, 0 rows affected

mysql> begin;
Query OK, 0 rows affected

mysql> insert into shiwu(id) values(1);
Query OK, 1 row affected

mysql> insert into shiwu(id) values(2);
Query OK, 1 row affected

mysql> commit;
Query OK, 0 rows affected

mysql> select id from shiwu;
+----+
| id |
+----+
|  1 |
|  2 |
+----+
2 rows in set

mysql> begin;
Query OK, 0 rows affected

mysql> insert into shiwu(id) values(3);
Query OK, 1 row affected

mysql> rollback;
Query OK, 0 rows affected

mysql> select id from shiwu;
+----+
| id |
+----+
|  1 |
|  2 |
+----+
2 rows in set

隔離級別

SQL標準定義了4中隔離級別,指定了事務中哪些資料改變其他事務可見,哪些資料改變其他事務不可見。低隔離級別可以支援更高的併發處理,同時佔用的系統資源更少。事務隔離級別可以使用以下語句設定:

#未提交讀
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
#提交讀
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
#可重複讀
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
#可序列化
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;

READ-UNCOMMITTED 

在該隔離級別(未提交讀),所有事務都可以看到其他未提交事務的執行結果。讀取未提交的資料會產生髒讀(Dirty Read)的問題,比如:事務A將某個值改為1,但並未提交,此時事務B來讀取這個值,取到1,事務A發現修改的不對,進行回滾。而事務B獲取的是事務A回滾前的資料,這就是髒讀。

READ-COMMITTED

在該隔離級別(提交讀),一個事務從開始到提交前所做的任何改變都是不可見的,其他事務只能看見已經提交事務所做的改變。這樣就解決了髒讀問題。但存在不可重複讀的問題,如:事務A查詢某個值後,事務B對這個值進行更新並提交,事務A執行過程中第二次查詢這個值時,會得到跟第一次不同的結果。這就是不可重複讀。

REPEATTABLE-READ

它是MySQL資料庫的預設隔離級別(可重複讀),在該隔離級別,當事務開始讀取資料時,不再允許修改。但存在幻讀的問題,如:事務A查詢某張表的全部資料後,事務B插入了一條資料,當事務A再次查詢全部資料時,會發現多了一條資料,這就是幻讀。

SERIALIZABLE

這是最高的隔離級別(可序列化),在該級別下,通過強制事務排序,序列化順序執行,可以避免髒讀、不可重複讀與幻讀。但這種事務隔離級別效率低下,比較消耗資料庫效能,一般不使用。

大多數資料庫預設的隔離級別是READ-COMMITTED(提交讀),比如SQL Server、Oracle。

MySQL的預設隔離級別是REPEATTABLE-READ(可重複讀)。

InnoDB鎖機制

為解決資料庫併發控制問題,如在同一時刻,客戶端對於同一個表做更新或者查詢操作,為保證資料的一致性,需要對併發操作進行控制。因此產生了鎖。同時為實現MySQL的各個隔離級別,鎖機制為其提供了保證。

鎖的種類主要有:共享鎖、排他鎖、意向鎖。下面一一介紹。

共享鎖

共享鎖的代號是S,是Share的縮寫,共享鎖的鎖粒度是行或者元組(多個行)。一個事務獲取共享鎖後,可以對鎖定範圍內的資料執行讀操作。

排他鎖

排他鎖的代號是X,是eXclusive的縮寫,排他鎖的粒度與共享鎖相同,也是行或者元組。一個事務獲取排他鎖之後,可以對鎖定範圍內的資料執行寫操作。如果事務A獲取了一個元組的共享鎖,事務B也可以立即獲取這個元組的共享鎖,但不能獲取這個元組的排他鎖,必須等到事務A釋放共享鎖之後。如果事務A獲取了一個元組的排他鎖,那麼事務B既不能獲取這個元組的共享鎖,也不能獲取這個元組的排他鎖。必須等到事務A釋放排他鎖之後。

意向鎖

意向鎖是一種表鎖,鎖定的粒度是整張表,分為意向共享鎖(IS)和意向排他鎖(IX)兩類。意向共享鎖表示一個事務有意獲取資料的共享鎖或者排他鎖。“有意”表示事務想執行操作但還沒有真正執行。

鎖關係

鎖和鎖之間的關係,要麼是相容的,要麼是互斥的。關係表如下:

引數

X

S

IX

IS

X

N

N

N

N

S

N

Y

N

Y

IX

N

N

Y

Y

IS

N

Y

Y

Y

為了提高資料庫的併發量,每次鎖定的資料範圍越小越好,越小的鎖其耗費的系統資源越多,系統性能下降。為在高併發響應和系統性能兩方面進行平衡,就產生了“鎖粒度(Lock granularity)”的概念。

鎖粒度

鎖的粒度主要分為表鎖和行鎖。

表鎖管理鎖的開銷最小,同時允許的併發量也是最小的鎖機制。MyISAM儲存引擎使用該鎖機制,當要寫入資料時,把整個表記錄加鎖,此時其他讀、寫動作一律等待。同時一些特定的動作,如ALTER TABLE執行時使用的也是表鎖。

行鎖可以支援最大的併發。InnoDB儲存引擎使用該鎖機制。如果要支援併發讀、寫,建議採用InnoDB儲存引擎,因為其是採用行級鎖,可以獲得更好的更新效能。

執行

以下是MySQL中一些語句執行行鎖的情況:

SELECT ...... LOCK IN SHARE MODE

此操作會加上一個共享鎖。若會話事務中查詢的資料已經被其他會話事務加上排他鎖的話,共享鎖會等待其結束再加,若等待時間過長就會顯示事務需要的鎖等待超時。

SELECT ...... FOR UPDATE

此操作加上一個排他鎖,其他會話事務將無法再加其他鎖,必須等待其結束。

INSERT / UPDATE /DELETE

會話事務會對DML語句操作的資料加上一個排他鎖,其他會話的事務都將會等待其釋放排他鎖。

當共享鎖或排他鎖需要加到一個區間值域時,InnoDB引擎會自動再為其加上間隙鎖或稱為範圍鎖,對不存在的資料也鎖住,防止出現幻寫。注:上述語句描述的情況,與MySQL所設定的事務隔離級別有較大關係。

當開啟一個事務時,InnoDB儲存引擎會在更新的記錄上加行級鎖,此時其他事務不可以更新被鎖定的記錄。

//事務A
mysql> begin;
Query OK, 0 rows affected

mysql> UPDATE student SET sno = 456 WHERE sname = '張三';
Query OK, 0 rows affected
Rows matched: 1  Changed: 0  Warnings: 0

//事務B
mysql> begin;
Query OK, 0 rows affected

mysql> UPDATE student SET sno = 789 WHERE sname = '張三';
//此時事務B會等待,直到事務A執行COMMIT或超時報錯(Lock wait timeout exceeded; try restarting transaction)