MySQL事務隔離級別小記

概要:在數據庫操作中,爲了保證併發讀取數據的正確性,一致性,提出的事務隔離級別,隔離級別越高,越能保證數據的完整性和一致性,但是對併發性能的影響也越大。

一、髒讀、不可重複讀、幻讀

  • 髒讀
    事務A讀取事務B尚未提交的數據,如果事務B執行回滾操作,那麼A事務讀取到的數據就是髒數據。

  • 不可重複讀
    在一個事務中,對同一行數據重複讀取兩次,但是卻得到了不同的結果。

  • 幻讀
    事 務在操作過程中進行兩次查詢,兩次查詢的數據記錄數不匹配。

二、事務的隔離級別
MySQL事務有四個級別:未提交讀(read uncommitted)、已提交讀(read committed)、可重複讀(repeatable read)、串行化(serializable)

  • 默認事務隔離級別

    mysql>  SELECT @@tx_isolation;
    ERROR 2006 (HY000): MySQL server has gone away
    No connection. Trying to reconnect...
    Connection id:    138591
    Current database: FA
    
    +-----------------+
    | @@tx_isolation  |
    +-----------------+
    | REPEATABLE-READ |
    +-----------------+
    
  • 未提交讀(read uncommitted)
    在事務A中可以讀取到事務B未提交的數據,但是可能產生 髒讀不可重複讀幻讀

    測試髒讀:

    mysql> set global transaction_isolation ='READ-UNCOMMITTED';
    Query OK, 0 rows affected (0.00 sec)
    
    +------------------+
    | @@tx_isolation   |
    +------------------+
    | READ-UNCOMMITTED |
    +------------------+
    1 row in set, 1 warning (0.00 sec)
    

    1、事務A:

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SELECT * FROM INFO;
    +----+------+------+
    | ID | NAME | AGE  |
    +----+------+------+
    |  1 | lisi |   18 |
    +----+------+------+
    1 row in set (0.00 sec)
    

    2、事務B

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> INSERT INTO INFO(NAME, AGE) VALUES('wangwu', 12);
    Query OK, 1 row affected (0.00 sec)
    

    3、事務A

    mysql> SELECT * FROM INFO;
    +----+--------+------+
    | ID | NAME   | AGE  |
    +----+--------+------+
    |  1 | lisi   |   18 |
    |  2 | wangwu |   12 |
    +----+--------+------+
    2 rows in set (0.00 sec)
    

    4、事務B

    mysql> ROLLBACK;
    Query OK, 0 rows affected (0.01 sec)
    

    5、事務A

    mysql> SELECT * FROM INFO;
    +----+------+------+
    | ID | NAME | AGE  |
    +----+------+------+
    |  1 | lisi |   18 |
    +----+------+------+
    1 row in set (0.00 sec)
    
    mysql> COMMIT;
    Query OK, 0 rows affected (0.00 sec)
    

    由上可知:事務B在執行完步驟2後未提交,此時事務A執行查詢,查詢到B未提交的數據,然後B因某原因回滾了數據,此時事務A讀取的兩條數據中有一條是不存在的,是髒數據。

  • 已提交讀(read committed)
    讀取數據的事務允許其他事務繼續訪問該行數據,但是未提交的寫事務將會禁止其他事務訪問該數據行。可以避免髒讀,但可能產生不可重複讀和幻讀

    測試不可重複讀:

    mysql> set global transaction_isolation ='READ-COMMITTED';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql>  SELECT @@tx_isolation;
    +------------------+
    | @@tx_isolation   |
    +------------------+
    | READ-COMMITTED |
    +------------------+
    1 row in set, 1 warning (0.00 sec)
    

    1、事務A

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SELECT * FROM INFO;
    +----+------+------+
    | ID | NAME | AGE  |
    +----+------+------+
    |  1 | lisi |   18 |
    +----+------+------+
    1 row in set (0.00 sec)
    

    2、事務B

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> UPDATE INFO SET NAME='maliu' WHERE ID='1';
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    

    3、事務A

    mysql> SELECT * FROM INFO;
    +----+------+------+
    | ID | NAME | AGE  |
    +----+------+------+
    |  1 | lisi |   18 |
    +----+------+------+
    1 row in set (0.00 sec)
    

    4、事務B

    mysql> COMMIT;
    Query OK, 0 rows affected (0.01 sec)
    

    5、事務A

    mysql> SELECT * FROM INFO;
    +----+-------+------+
    | ID | NAME  | AGE  |
    +----+-------+------+
    |  1 | maliu |   18 |
    +----+-------+------+
    1 row in set (0.00 sec)
    

    由上可知:當事務B修改數據後,但未提交,此時事務A是讀取不到的,所有該隔離級別可以防止髒讀;當事務B提交事務後,此時事務A再次查詢,發現數據已經被修改,兩次讀取結果不一致,產生不可重複讀。

  • 可重複讀(repeatable read)
    事務A在開啓事務期間,所訪問的數據是不可修改的,可以避免不可重複讀,但是可能產生幻讀。

    mysql> set global transaction_isolation ='REPEATABLE-READ';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql>  SELECT @@tx_isolation;
    +-----------------+
    | @@tx_isolation  |
    +-----------------+
    | REPEATABLE-READ |
    +-----------------+
    1 row in set, 1 warning (0.00 sec)
    

    1、事務A

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SELECT * FROM INFO;
    +----+-------+------+
    | ID | NAME  | AGE  |
    +----+-------+------+
    |  1 | maliu |   18 |
    +----+-------+------+
    1 row in set (0.00 sec)
    

    2、事務B

    mysql> UPDATE INFO SET NAME='adam' WHERE ID='1';
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0
    
    mysql> COMMIT;
    Query OK, 0 rows affected (0.01 sec)
    

    3、事務A

    mysql> SELECT * FROM INFO;
    +----+-------+------+
    | ID | NAME  | AGE  |
    +----+-------+------+
    |  1 | maliu |   18 |
    +----+-------+------+
    1 row in set (0.00 sec)
    

    4、事務A

    mysql> COMMIT;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SELECT * FROM INFO;
    +----+------+------+
    | ID | NAME | AGE  |
    +----+------+------+
    |  1 | adam |   18 |
    +----+------+------+
    1 row in set (0.00 sec)
    

    由上可知:當事務隔離級別爲可重複讀時,在事務A未提交前,是看不到其他事務提交的數據,可以避免不可重複讀,但是可能產生幻讀。

    當SQL增加FOR UPDATE時,是禁止其他事務對NAME=‘adam’的數據進行操作(新增刪除)

    SELECT * FROM INFO WHERE NAME = 'adam' FOR UPDATE;
    

    FOR UPDATE是一種行級鎖,又叫排它鎖。可以解決可重複讀級別下的幻讀現象。

  • 串行化(serializable)
    提供嚴格的事務隔離。它要求事務串行化執行,事務只能一個接着一個地執行,不能併發執行。可以解決髒讀,不可重複讀,幻讀現象,但大大降低了併發性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章