1. 程式人生 > 實用技巧 >關於MySQL的一致性讀

關於MySQL的一致性讀

MySQL的一致性讀

MySQL的一致性讀
一、什麼是一致性讀
1.一致性的定義
2.對一致性的分析
二、MySQL怎樣保證資料的一致性
三、可重讀隔離級別的一致性讀
四、模擬測試
五、結論toc

  資料可用性:正確性、完整性、一致性。這是我們進行資料備份時的要求,如果無法保證備份資料的可用性那麼備份資料也就失去了意義。前兩個性質很好理解,但是一致性具體是什麼呢?

一、什麼是一致性讀

1.一致性的定義

資料的一致性:指相關聯的資料之間的邏輯關係是否正確。
資料庫的一致性:指資料庫從一個一致性狀態變成另一個狀態。這期間資料可能會發生變化但是狀態不會改變。

2.對一致性的分析

關於資料的一致性,舉個例子當前時間中午11點,商城一臺電腦價值5000元我的賬戶下剛好有5000元我是買得起的但是我沒有買,中午12點我從賬戶取出1000元買其他東西此時餘額為4000元已經無法購買電腦了。可以說現在買不起這臺電腦但是不能說一直買不起,因為在12點的時候是有足夠的錢的,也就是說不能拿我現在的餘額放到11點時的邏輯關係中這是不一樣的,11點時的邏輯關係中相對應的是11點時我的餘額這才保證了資料的一致性。
關於資料庫的一致性,這個就比較好理解了還是一家銀行,這個銀行只有A,B兩人,A存了2w,B存3w。A給B轉了1w,此時A餘額為1w,B餘額為4w。這就是兩個一致性狀態的切換。因為對銀行而言總額是不變的一直是5w。雖然裡面AB的資料是有變化的。

二、MySQL怎樣保證資料的一致性

資料的一致性在資料庫備份中有比較明顯的體現。我們在一個時間點開始備份怎樣能保證所有資料在這個時間點後都不會發生改變呢?
加鎖:針對備份策略,將所有涉及的表都加上鎖,保證在備份結束之前所有的表都不會被修改。這樣不管這次備份持續多長時間,都可以確保資料始終一致在備份開始的時候。但是這種缺點也很明顯,鎖表對資料庫影響比較大,這期間不能對資料庫做任何寫操作,只能讀。
快照:在備份開始時對目標資料做一個快照,因為快照記錄了那個時刻所有資料的樣子,所以在這個快照範圍內所有的資料都具有一致性。如果儲存引擎為innodb那麼利用事務的隔離性就可以保證資料的一致性。也就實現了快照功能。事務可重讀隔離級別可以實現資料的一致性,雖然可重讀可能因為更新資料導致幻讀,但是資料備份是個只讀操作。所以只要保證備份操作放在一個單獨的事務中即可,其他對資料庫的操作是別的事務中的因為隔離性這個特點並不會影響其他事務。這樣就不會出現幻讀了也保證了在備份過程中,保證了資料的一致性讀。

三、可重讀隔離級別的一致性讀

上面說的一致性讀也被稱為快照讀。具體就是上面快照的含義。接下來我們來模擬一下這個場景。

四、模擬測試

1)需要將兩個資料庫會話中的事務隔離級別都設定為可重讀,然後在兩個會話中都開啟兩個事務。
首先關閉兩個會話的自動提交,設定會話隔離模式為可重讀並開啟一個事務。

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)

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

注意會話區別:
會話一:

會話二:

2)兩個會話分別檢視測試表資訊如下:
會話一:

會話二:

3)在會話二中進行資料更新插入一條資料並提交:

4)在會話一中檢視資料情況,可以看到此時資料並沒有變化,在會話一中插入資料提交後,看到會話二新插入的資料發生了幻讀。

解釋:可以看到在會話二中做完插入操作檢視這個測試表已經4條資料了,但是在會話一中看還是隻有3條資料。這時我們在會話一做更新資料的操作,也就是插入資料然後提交卻發現多出兩條資料,這就是可重讀級別下的幻讀。
(插入操作並不會起到更新全部資料的效果,只會更新自己插入的資料,起到作用的是commit,將全部資料更新出來包括會話二插入的資料。這裡可以適用update做更新資料的操作。update語句並沒有指定任何條件,相當於更新表中的所有行的對應欄位,如果你指定了條件,並且沒有更新到"隱藏"的行,那麼可能無法看到幻讀現象。)

在備份操作中,我們所有的操作都是讀操作並不涉及到更新資料,所以當我們把所有的備份操作都放到一個單獨的‘事務’中,並且此事務將事務隔離級別設定為可重讀,是不會出現幻讀問題的,也就是達到了在某一時間點讀取資料資料一致性的目的。


那麼是不是隻要在可重讀隔離模式下,啟動一個事務就相當於給當時的資料庫打了一個快照?答案是否定的,具體測試如下:

1)仍然使用上面實驗,如果是新開啟的會話需要重新設定隔離模式以及關閉自動提交。檢視此時兩個會話查詢測試表的資訊如下:
會話一:

會話二:

2)在會話一中啟動一個事務,其餘什麼操作也不做,用來觀察

3)在會話二中做資料插入操作並提交。資料成功插入。

4)在會話一中進行查詢,可以看到資料被更新了,並不是我們想的那樣被打了快照。

那麼這是為什麼呢?第一個模擬實驗是成功實現的,第二個卻出現問題。而二者的差別在哪裡?其實第一個實驗中在啟動會話後進行了一次查詢也就是那個時候快照才真正的開始,而第二次實驗我們並沒有在會話開啟後進行查詢,所以啟動會話並不是直接打快照。也就是說不是以start transaction語句開始的時間點作為"快照"建立的時間點。官方文件有這樣的說明:
If the transaction isolation level is REPEATABLE READ (the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction. You can get a fresher snapshot for your queries by committing the current transaction and after that issuing new queries.
也就是說,當事務處於"可重讀"隔離級別時,並不是事務開始時就代表快照建立,而是事務中的第一個查詢語句執行時,快照點才會被建立。

五、結論

  那麼針對上述現象應該怎樣才能在指定的時間點開啟一個快照呢?難道要每次開啟一個事務後都進行一次查詢才能實現嗎?MySQL已經為我們提供了另一種選擇,在啟動會話時指定引數即可。START TRANSACTION WITH consistent snapshot
  使用start transaction with consistent snapshot;命令啟動事務,就表示啟動事務的同時就建立快照,也就是說,只要事務開始,就能保證"一致性讀"。



來自為知筆記(Wiz)