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

1、隔離性與隔離級別

一、事務的特性

原子性mysql

一致性sql

隔離性數據庫

持久性安全

二、不一樣事務隔離級別的區別

讀未提交:別人改數據的事務還沒有提交,我在個人事務中也能讀到。
讀已提交:別人改數據的事務已經提交,我在個人事務中才能讀到。
可重複讀:別人改數據的事務已經提交,我在個人事務中也不去讀。
串行:個人事務還沒有提交,別人就別想改數據。
這4種隔離級別,並行性能依次下降,安全性依次提升。bash

三、讀提交」和「可重複讀」

 假設數據表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會有哪些不一樣的返回結果,也就是圖裏面V一、V二、V3的返回值分別是什麼?學習

 

實際測試代碼以下

查看隔離級別:測試

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

事務A實際測試代碼:spa

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)

四、隔離級別的配置方法

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)

五、可重複讀的場景

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

一、一個表存了每月月底的餘額,一個表存了帳單明細,
二、這時候你要作數據校對,也就是判斷上個月的餘額和當前餘額的差額,是否與本月的帳單明細一直
三、你必定但願在校對的過程當中,即便有用戶發生了一筆新的交易,也不影響你的校隊結果

這時候"可重複讀"隔離級別就很方便,事務啓動時的視圖能夠認爲是靜態的,不受其餘食物更新的影響

2、事務隔離的實現

一、事務隔離的實現

假設一個值1被按順序改爲二、三、4,在回滾日誌裏面就會有相似下面的記錄

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

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

二、事務隔離的幾個爲何?

一、回滾日誌何時刪除?

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

二、何時不須要了?

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

三、爲何儘可能不要使用長事務

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

3、事務啓動方式

一、啓動方式

 其實不少時候業務開發同窗並非有意使用長事務,挺長是因爲誤用所致,MySQL 的事務啓動方式有如下幾種

方式一

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

方式一

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

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

二、建議使用方式一

若是考慮多一次交互問題,可使用commit work and chain語法。在autocommit=1的狀況下用begin顯式啓動事務,

若是執行commit則提交事務。若是執行commit work and chain則提交事務並自動啓動下一個事務。

三、如何查詢長事務

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

4、思考題(同窗們的經典留言)

一、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,爲了保證事務在執行期間看到的數據在先後必須是一致的,那些老的事務視圖、回滾日誌就必須存在了,這就佔用了大量的存儲空間。
源於此,咱們應該儘可能不要使用長事務。

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

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

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

三、髒讀、幻讀、不可重複讀(來自於William同窗)

一、髒讀:

    當數據庫中一個事務A正在修改一個數據可是還未提交或者回滾,
    另外一個事務B 來讀取了修改後的內容而且使用了,
    以後事務A提交了,此時就引發了髒讀。 
   
    此狀況僅會發生在: 讀未提交的的隔離級別.

 二、不可重複讀:


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

三、幻讀

    一個事務按相同的查詢條件從新讀取之前檢索過的數據,
    卻發現其餘事務插入了知足其查詢條件的新數據,這種現象就稱爲幻讀。
    
    幻讀是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,好比這種修改涉及到表中的「所有數據行」。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入「一行新數據」。那麼,之後就會發生操做第一個事務的用戶發現表中還存在沒有修改的數據行,就好象發生了幻覺同樣.
    通常解決幻讀的方法是增長範圍鎖RangeS,鎖定檢索範圍爲只讀,這樣就避免了幻讀。
    
    此狀況會回發生在:讀未提交、讀提交、可重複讀的隔離級別.

四、形象總結(來自null同窗)

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

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

1. 讀提交
2. 可重複讀

讀提交:在每一條 SQL 開始執行時建立視圖,隔離做用域僅限該條 SQL 語句。
可重複讀:事務啓動時建立視圖,所以,在事務任意時刻,對記錄讀取的值都是同樣的。

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

1. 讀未提交2. 串行化讀未提交:直接返回記錄最新值。串行化:經過讀寫鎖來避免並行訪問。讀-讀:容許併發執行讀-寫:只能串行寫-寫:只能串行

相關文章
相關標籤/搜索