MySQL 事務主要用於處理操做量大,複雜度高的數據。好比說,在人員管理系統中,你刪除一我的員,你即須要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操做語句就構成一個事務!html
通常來講,事務是必須知足4個條件(ACID)::原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、持久性(Durability)。mysql
不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住知足條件的行,解決幻讀須要鎖表。sql
爲了解決上面事務的併發問題,sql標準提出了4種隔離級別,下面是每種隔離級別可以解決的問題對應關係:shell
事務隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
read-uncommitted | N | N | N |
read-committed | Y | N | N |
repeatable-read(default) | Y | Y | N |
serializable | Y | Y | Y |
mysql的默認隔離級別是Repeatable。數據庫
查看系統級和會話級的隔離級別:session
mysql> select @@global.tx_isolation,@@tx_isolation; +-----------------------+-----------------+ | @@global.tx_isolation | @@tx_isolation | +-----------------------+-----------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+-----------------+ 1 row in set, 2 warnings (0.01 sec)
下面用例子說明一下這四種隔離級別:併發
1. read-uncommittedsvn
更改隔離級別爲read-uncommitted:性能
mysql> set session tx_isolation='read-uncommitted'; Query OK, 0 rows affected, 1 warning (0.01 sec) mysql> select @@tx_isolation; +------------------+ | @@tx_isolation | +------------------+ | READ-UNCOMMITTED | +------------------+ 1 row in set, 1 warning (0.00 sec)
首先,準備一些測試數據:測試
mysql> select * from user; +----+----------+------+ | id | name | age | +----+----------+------+ | 1 | zhangsan | 25 | | 2 | lisi | 26 | | 3 | wangwu | 27 | | 4 | nike | 28 | | 5 | lucy | 29 | +----+----------+------+ 5 rows in set (0.00 sec)
客戶端A:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from user; +----+----------+------+ | id | name | age | +----+----------+------+ | 1 | zhangsan | 25 | | 2 | lisi | 26 | | 3 | wangwu | 27 | | 4 | nike | 28 | | 5 | lucy | 29 | +----+----------+------+ 5 rows in set (0.00 sec)
客戶端B:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> update user set age=52 where name='zhangsan'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0
客戶端A:
mysql> select * from user; +----+----------+------+ | id | name | age | +----+----------+------+ | 1 | zhangsan | 52 | | 2 | lisi | 26 | | 3 | wangwu | 27 | | 4 | nike | 28 | | 5 | lucy | 29 | +----+----------+------+ 5 rows in set (0.00 sec)
能夠看到,客戶端B的事務尚未提交,在客戶端A的事務內就看到了更新的數據。
客戶端B:
mysql> rollback; Query OK, 0 rows affected (0.02 sec)
客戶端A:
mysql> select * from user; +----+----------+------+ | id | name | age | +----+----------+------+ | 1 | zhangsan | 25 | | 2 | lisi | 26 | | 3 | wangwu | 27 | | 4 | nike | 28 | | 5 | lucy | 29 | +----+----------+------+ 5 rows in set (0.00 sec)
因爲客戶端B的事務回滾,客戶端A讀取的數據又變成了原始數據,所以上一次客戶端A讀取的數據變成了髒數據。在併發事務中,這種讀取數據的問題就叫作髒讀。
2. read-commited
要解決上面的問題,能夠把數據庫的隔離級別改爲read-commited。
客戶端A:
mysql> set session tx_isolation='read-committed'; Query OK, 0 rows affected, 1 warning (0.00 sec)
再按照上述步驟測試一下,發現髒讀問題已經解決,在事務B沒有commit以前,事務A不會讀取到髒數據。
下面演示一下不可重複讀的問題。
客戶端A:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from user; +----+----------+------+ | id | name | age | +----+----------+------+ | 1 | zhangsan | 25 | | 2 | lisi | 26 | | 3 | wangwu | 27 | | 4 | nike | 28 | | 5 | lucy | 29 | +----+----------+------+ 5 rows in set (0.00 sec)
客戶端B:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> update user set age=52 where name='zhangsan'; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> commit; Query OK, 0 rows affected (0.01 sec)
客戶端A:
mysql> select * from user; +----+----------+------+ | id | name | age | +----+----------+------+ | 1 | zhangsan | 52 | | 2 | lisi | 26 | | 3 | wangwu | 27 | | 4 | nike | 28 | | 5 | lucy | 29 | +----+----------+------+ 5 rows in set (0.00 sec) mysql> rollback; Query OK, 0 rows affected (0.00 sec)
能夠看到在客戶端B的事務提交先後,客戶端A讀取到的數據不同了。也就是重複讀取相同的數據有不一樣的結果。
我的理解,髒讀也屬於不可重複讀的一個範疇,只是髒讀在事務B未提交以前就致使兩次讀取數據不同,不可重複讀在事務B提交以後致使兩次讀取結果不同。還有就是髒讀之因此叫髒數據,是由於這條數據沒有真正的在數據庫中保存過,這是事務的一箇中間狀態。而不可重複讀兩次讀取不一樣的數據實際都已經存在於數據庫中了。
3. repeatable-read
要解決不可重複讀的問題,能夠將數據庫的隔離級別改成repeatable-read。
客戶端A:
mysql> set session tx_isolation='repeatable-read'; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set, 1 warning (0.00 sec)
再按照上述步驟測試一下,發現不可重複讀的問題已經解決,在事務B沒有commit以後,事務A讀取的數據沒有變化,關閉這個事務從新打開一個事務纔會讀到更新後的數據。
下面演示一下幻讀的問題。
客戶端A:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from user; +----+----------+------+ | id | name | age | +----+----------+------+ | 1 | zhangsan | 25 | | 2 | lisi | 26 | | 3 | wangwu | 27 | | 4 | nike | 28 | | 5 | lucy | 29 | +----+----------+------+ 5 rows in set (0.00 sec)
客戶端B:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into user values(6,'shell',30); Query OK, 1 row affected (0.01 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec)
客戶端A:
mysql> insert into user values(6,'shell',30); ERROR 1062 (23000): Duplicate entry '6' for key 'PRIMARY' mysql> select * from user; +----+----------+------+ | id | name | age | +----+----------+------+ | 1 | zhangsan | 25 | | 2 | lisi | 26 | | 3 | wangwu | 27 | | 4 | nike | 28 | | 5 | lucy | 29 | +----+----------+------+ 5 rows in set (0.00 sec)
能夠看到,對於客戶端A來講,命名沒有id爲6的數據,可是仍是插入失敗,再查詢一下仍是沒有啊,感受產生了幻覺,這就是幻讀問題。幻讀和不可重複讀的區別在於,不可重複讀重點是更新後的讀取,幻讀重點是插入刪除這些操做,解決不可重複讀,只須要對對應的數據行加鎖就好了。解決幻讀則須要對整張表加鎖。
若是兩個事務B沒有提交以前事務A執行插入會如何呢?咱們來看一下:
客戶端A:
mysql> insert into user values(6,'shell',30);
能夠看到若是插入的id和事務B同樣,那麼事務A的操做會被阻塞,直到事務B提交commit後,纔會報錯:
客戶端A:
mysql> insert into user values(8,'svn',32); ERROR 1062 (23000): Duplicate entry '8' for key 'PRIMARY'
若是客戶端A插入到的數據事務B不衝突,那麼會當即返回成功:
客戶端A:
mysql> insert into user values(9,'svn',32); Query OK, 1 row affected (0.00 sec)
4. serializable
要解決幻讀的問題,能夠將數據庫的隔離級別改成serializable。
客戶端A:
mysql> set session tx_isolation='serializable'; Query OK, 0 rows affected, 1 warning (0.00 sec)
再按照上述步驟測試一下,發現幻讀的問題已經解決,當事務B嘗試insert的事務,被阻塞,也就是事務A將整張表鎖住了。直到事務A提交commit之後,事務B的操做纔會返回結果。
在這種狀況下,只容許一個事務在執行,其它事務必須等待這個事務執行完後才能執行。沒有併發,只是單純的串行。
參考:http://www.runoob.com/mysql/m...