MySQL實戰45講學習筆記:第三講


一、隔離性與隔離級別

1、事務的特性

原子性

一致性

隔離性

持久性

2、不同事務隔離級別的區別

讀未提交:別人改數據的事務尚未提交,我在我的事務中也能讀到。
讀已提交:別人改數據的事務已經提交,我在我的事務中才能讀到。
可重復讀:別人改數據的事務已經提交,我在我的事務中也不去讀。
串行:我的事務尚未提交,別人就別想改數據。
這4種隔離級別,並行性能依次降低,安全性依次提高。

3、讀提交”和“可重復讀”

 假設數據表T中只有一列,期中一行的值為1,下面是按照時間順序執行兩個事物的行為

mysql> create table T(c int) engine=InnoDB;
insert into T(c) values(1);

實際測試代碼如下:

mysql> create table T(c int) engine=InnoDB;
Query OK, 0 rows affected (0.01 sec)

mysql> insert into T(c) values(1);
Query OK, 1 row affected (0.01 sec)

 我們來看看在不同隔離級別下,事務A會有哪些不同的返回結果,也就是圖里面V1、V2、V3的返回值分別是什么?

 

實際測試代碼如下

查看隔離級別:

mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+

事務A實際測試代碼:

mysql> use test;
Database changed
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from T;
+------+
| c    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> select * from T;
+------+
| c    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> mysql> select * from T;
+------+
| c    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from T;
+------+
| c    |
+------+
|    2 |
+------+
1 row in set (0.00 sec)

事務B實際測試代碼:

mysql> use test;
Database changed
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from T;
+------+
| c    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> update T set c=5 where id=1;
ERROR 1054 (42S22): Unknown column 'id' in 'where clause'
mysql> update T set c=2 ;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

4、隔離級別的配置方法

Oracle 數據庫的默認隔離級別其實就是“讀提交”,因此對對於一些從 Oracle 遷移到 MySQL 的應用,為保證數據庫隔離級別的一致,你一定要記得將 MySQL 的隔離級別
設置為“讀提交”

mysql> show variables like 'transaction_isolation';

+-----------------------+----------------+

| Variable_name | Value |

+-----------------------+----------------+

| transaction_isolation | READ-COMMITTED |

+-----------------------+----------------+

實際測試代碼如下:

mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.01 sec)

5、可重復讀的場景

假設你在管理一個個銀行賬戶表,

1、一個表存了每個月月底的余額,一個表存了賬單明細,
2、這時候你要做數據校對,也就是判斷上個月的余額和當前余額的差額,是否與本月的賬單明細一直
3、你一定希望在校對的過程中,即使有用戶發生了一筆新的交易,也不影響你的校隊結果

這時候"可重復讀"隔離級別就很方便,事務啟動時的視圖可以認為是靜態的,不受其他食物更新的影響

二、事務隔離的實現

1、事務隔離的實現

假設一個值1被按順序改成2、3、4,在回滾日志里面就會有類似下面的記錄

每條記錄在更新的時候都會同時記錄一條回滾操作。同一條記錄在系統中可以存在多個版本,這就是數據庫的多版本並發控制(MVCC)

對於read-view A ,要的到1就必須將當前值依次執行途中所有回滾操作的到

2、事務隔離的幾個為什么?

1、回滾日志什么時候刪除?

系統會判斷當沒有事務需要用到這些回滾日志的時候,回滾日志會被刪除

2、什么時候不需要了?

當系統里沒有比這個回滾日志更早的read-view的時候。

3、為什么盡量不要使用長事務

長事務意味着系統里面會存在很老的事務視圖,在這個事務提交之前,回滾記錄都要保留,這會導致大量占用存儲空間。除此之外,長事務還占用鎖資源,可能會拖垮庫。

三、事務啟動方式

1、啟動方式

 其實很多時候業務開發同學並不是有意使用長事務,挺長是由於誤用所致,MySQL 的事務啟動方式有以下幾種

方式一

顯式啟動事務語句,begin或者start transaction,提交commit,回滾rollback;

方式一

set autocommit=0,該命令會把這個線程的自動提交關掉。這樣只要執行一個select語句,事務就啟動,

並不會自動提交,直到主動執行commit或rollback或斷開連接

2、建議使用方式一

如果考慮多一次交互問題,可以使用commit work and chain語法。在autocommit=1的情況下用begin顯式啟動事務,

如果執行commit則提交事務。如果執行commit work and chain則提交事務並自動啟動下一個事務。

3、如何查詢長事務

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

實際測試代碼如下:

mysql> select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60\G;
*************************** 1. row ***************************
                    trx_id: 1806
                 trx_state: RUNNING
               trx_started: 2019-10-15 03:34:50
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 2
       trx_mysql_thread_id: 6
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 1
          trx_lock_structs: 2
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 1
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
*************************** 2. row ***************************
                    trx_id: 421762016601952
                 trx_state: RUNNING
               trx_started: 2019-10-15 03:34:11
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 2
       trx_mysql_thread_id: 7
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 1
          trx_lock_structs: 2
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 1
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
2 rows in set (0.00 sec)

ERROR: 
No query specified

四、思考題(同學們的經典留言)

1、Gavin同學的形象實例

下面是我的自問自答,也是我的學習筆記,問下斌哥,這樣理解准確嗎?
在可重復讀的隔離級別下,如何理解**當系統里沒有比這個回滾日志更早的 read-view 的時候**,這個回滾日志就會被刪除?

這也是**盡量不要使用長事務**的主要原因。



比如,在某個時刻(今天上午9:00)開啟了一個事務A(對於可重復讀隔離級別,此時一個視圖read-view A也創建了),這是一個很長的事務……

事務A在今天上午9:20的時候,查詢了一個記錄R1的一個字段f1的值為1……
今天上午9:25的時候,一個事務B(隨之而來的read-view B)也被開啟了,它更新了R1.f1的值為2(同時也創建了一個由2到1的回滾日志),這是一個短事務,事務隨后就被commit了。
今天上午9:30的時候,一個事務C(隨之而來的read-view C)也被開啟了,它更新了R1.f1的值為3(同時也創建了一個由3到2的回滾日志),這是一個短事務,事務隨后就被commit了。
……
到了下午3:00了,長事務A還沒有commit,為了保證事務在執行期間看到的數據在前后必須是一致的,那些老的事務視圖、回滾日志就必須存在了,這就占用了大量的存儲空間。
源於此,我們應該盡量不要使用長事務。

2、MySQL中undo的內容會被記錄到redo中嗎?(來自於* 曉 *同學)

比如一個事務在執行到一半的時候實例崩潰了,在恢復的時候是不是先恢復redo,再根據redo構造undo回滾宕機前沒有提交的事務呢?

作者回復: 對的,是你說的這個流程

3、臟讀、幻讀、不可重復讀(來自於William同學)

1、臟讀:

    當數據庫中一個事務A正在修改一個數據但是還未提交或者回滾,
    另一個事務B 來讀取了修改后的內容並且使用了,
    之后事務A提交了,此時就引起了臟讀。 
   
    此情況僅會發生在: 讀未提交的的隔離級別.

 2、不可重復讀:


    在一個事務A中多次操作數據,在事務操作過程中(未最終提交),
    事務B也才做了處理,並且該值發生了改變,這時候就會導致A在事務操作
    的時候,發現數據與第一次不一樣了。 就是不可重復讀。
  
    此情況僅會發生在:讀未提交、讀提交的隔離級別.

3、幻讀

    一個事務按相同的查詢條件重新讀取以前檢索過的數據,
    卻發現其他事務插入了滿足其查詢條件的新數據,這種現象就稱為幻讀。
    
    幻讀是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,比如這種修改涉及到表中的“全部數據行”。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入“一行新數據”。那么,以后就會發生操作第一個事務的用戶發現表中還存在沒有修改的數據行,就好象發生了幻覺一樣.
    一般解決幻讀的方法是增加范圍鎖RangeS,鎖定檢索范圍為只讀,這樣就避免了幻讀。
    
    此情況會回發生在:讀未提交、讀提交、可重復讀的隔離級別.

4、形象總結(來自null同學)

視圖理解為數據副本,每次創建視圖時,將當前『已持久化的數據』創建副本,后續直接從副本讀取,從而達到數據隔離效果。

1、存在視圖的 2 種隔離級別:

1. 讀提交
2. 可重復讀

讀提交:在每一條 SQL 開始執行時創建視圖,隔離作用域僅限該條 SQL 語句。
可重復讀:事務啟動時創建視圖,因此,在事務任意時刻,對記錄讀取的值都是一樣的。

2、其他 2 種無視圖的隔離級別:

1. 讀未提交
2. 串行化

讀未提交:直接返回記錄最新值。

串行化:通過讀寫鎖來避免並行訪問。
讀-讀:允許並發執行
讀-寫:只能串行
寫-寫:只能串行


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM