事務簡言之就是一組SQL執行要麼所有成功,要麼所有失敗。MYSQL的事務在存儲引擎層實現。html
事務都有ACID特性:mysql
原子性(Atomicity):一個事務必須被視爲一個不可分割的單元;sql
一致性(Consistency):數據庫老是從一種狀態切換到另外一種狀態;shell
隔離性(Isolation):一般來講,事務在提交前對於其餘事務不可見;數據庫
持久性(Durablity):一旦事務提交,所作修改永久保存數據庫;ruby
事務最經常使用的例子就是銀行轉帳。假設polo需給lynn轉帳1000元,以下步驟:bash
確認polo帳戶餘額高於1000元;併發
從polo的帳戶餘額減去1000元;less
將lynn的帳戶餘額增長1000元;ui
SQL語句以下:
mysql> BEGIN; mysql> SELECT balance FROM bank_account WHERE uid=10001; mysql> UPDATE bank_account SET balance=balance-1000 WHERE uid=10001; mysql> UPDATE bank_account SET balance=balance+1000 WHERE uid=10002; mysql> COMMIT;
注:mysql啓動事務可以使用BEGIN或者START TRANSACTION;
上述三個步驟執行在一個事務中就可以保證數據的完整性,要麼所有成功,要麼所有失敗。
MYSQL提供兩種事務型引擎:Innodb和NDBCluster。默認採用自動提交模式,執行一條語句自動COMMIT。經過AUTOCOMMIT變量可啓用或者禁用自動提交模式:
mysql> SHOW VARIABLES LIKE "AUTOCOMMIT"; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> SET AUTOCOMMIT=1
AUTOCOMMIT=1表示開啓默認提交,0表示關閉默認提交須要手動提交。
事務隔離性的解釋:一般狀況下,事務在提交以前對於其餘事務不可見。
數據庫有四種隔離級別,固然MYSQL也是如此。分別爲:
READ UNCOMMITED(未提交讀)
READ COMMITED(已提交讀)
EPEATABLE READ(可重複讀)
SEAIALIZABLE(可串行化)
本人理解 : 隔離級別就是決定一個事務的修改另外一個事務什麼狀況下可見。
書本解釋 : 隔離級別都規定了一個事務中所作修改,哪些在事務內和事務間是可見的。
上面兩段理解的區別在因而否存在事務內可見性的規定。我在各個級別彷佛沒有看到
下面開始說明MYSQL的四種隔離級別,先準備一張學生表:
mysql> CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
只有id(主鍵自增)與name字段
READ UNCOMMITTED(未提交讀)
事務中修改沒有提交對其餘事務也是可見的,俗稱髒讀。很是不建議使用。
實例演示
客戶端A和B設置隔離級別爲未提交讀
mysql> SET SESSION TX_ISOLATION='READ-UNCOMMITTED';
客戶端A與B開啓事務並查詢student
mysql> BEGIN; mysql> SELECT * FROM student; Empty set (0.00 sec)
客戶端A和B都是空數據
客服端B插入一條新的數據
mysql> INSERT INTO student(name) VALUES("polo"); Query OK, 1 row affected (0.00 sec)
此時事務未提交,客服端A查看student表
$ SELECT * FROM student; mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 row in set (0.00 sec)
客戶端A看到B未提交的修改
客戶端B執行回滾操做
mysql> ROLLBACK
成功以後,客戶端A查看student表
mysql> SELECT * FROM student; Empty set (0.00 sec)
客戶端A查看數據爲空
以上能夠看出未提交讀隔離級別的危險性,對於一個沒有提交事務所作修改對另外一個事務是可見狀態,容易形成髒讀。非特殊狀況不得使用此級別
READ COMMITTED(提交讀)
多數數據庫系統默認爲此級別(MYSQL不是)。已提交讀級別即爲一個事務只能已提交事務所作的修改,也就解決了未提交讀的問題,即髒讀的問題。
實例演示
客戶端A和B設置隔離級別爲已提交讀
mysql> SET SESSION TX_ISOLATION='READ-COMMITTED';
客戶端A與B開啓事務並查詢student
mysql> BEGIN; mysql> SELECT * FROM student; Empty set (0.00 sec)
客戶端A和B都爲空
客服端B插入一條新的數據,不提交
mysql> INSERT INTO student (name) VALUES('polo');
客戶端A查看student
mysql> SELECT * FROM student; Empty set (0.00 sec)
注意這裏與上面不一樣了,在客戶端B沒有提交事務狀況下無數據
下面客戶端B提交事務
mysql> COMMIT;
客戶端A再次查看student表。
mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 row in set (0.00 sec)
成功讀取到客戶
從上面的示例能夠看出,提交讀沒有了未提交讀的問題,但咱們能夠看到在客戶端A的一個事務中執行兩次一樣的SELECT語句獲得不一樣結果,所以已提交讀又被稱爲不可重複讀。一樣篩選條件可能獲得不一樣的結果。
REPEATABLE READ(可重複讀)
如其名也,解決已提交讀不可重複讀取的問題。
示例演示
客戶端A和B設置隔離級別爲可重複讀
mysql> SET SESSION tx_isolation='REPEATABLE-READ'
客戶端A與B開啓事務並查看
mysql> BEGIN; mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 rows in set (0.00 sec)
客服端B更新polo爲jeff,並提交事務
mysql> UPDATE student SET name='jeff' WHERE id=1; mysql> COMMIT
客戶端A查看student表
mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | jeff | +----+------+ 1 rows in set (0.00 sec)
注意客戶端A查看數據未變,沒有不可重複讀問題
客戶端A提交事務,並查看student表
mysql> COMMIT; mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 rows in set (0.00 sec)
上面實例可知,可重複讀兩次讀取內容同樣。數據庫這級別並無解決幻讀的問題。可是MYSQL在可重複讀基礎上增長了MVCC機制解決了此問題,實例沒法演示幻讀效果。
那什麼是幻讀?首先,可重複讀鎖定範圍爲當前查詢到的內容,如執行
mysql> SELECT * FROM student WHERE id>=1
鎖定的即id>=1查到的行,爲行級鎖。如另外一事務執行並默認提交如下語句
mysql> INSERT INTO student (name) VALUES ('peter');
新增的這行並無被鎖定,此時讀取student
mysql> SELECT * FROM student WHERE id>=1; +----+---------+ | id | name | +----+---------+ | 1 | polo | | 2 | peter | +----+---------+ 2 rows in set (0.00 sec)
便出現了幻讀
除了使用MYSQL的MVCC機制,還可使用可串行化隔離級別解決此問題。
SEAIALIZABLE(可串行化)
可串行化是最高隔離級別,強制事務串行執行。執行串行了也就解決了一切的問題,這個級別只有在對數據一致性要求很是嚴格且沒用併發的狀況下使用
實例演示
客戶端A和B設置隔離級別爲可串行化
mysql> SET SESSION tx_isolation='SERIALIZABLE';
客戶端A執行查詢
mysql> SELECT * FROM student WHERE id<4; +----+---------+ | id | name | +----+---------+ | 1 | polo | | 2 | peter | +----+---------+ 2 rows in set (0.00 sec)
客戶端B執行新增
mysql> INSERT INTO student (name) VALUES('tiger');
好的!效果出現了,此時咱們會發現INSERT語句被阻塞執行,緣由就是A執行了查詢表student同時知足id<4,已被鎖定。若是查詢表student條件爲id<3,則新增語句可正常執行。