天天看點

為什麼MySQL預設的隔離級别是RR而大廠使用的是RC?

1寫作目的

現在的服務都是分布式,MySQL的叢集架構也是一樣。那麼MySQL的叢集架構中有一個點是讀寫分離,而讀寫分離是基于binlog實作的。那麼接下來就MySQL的讀寫分離和binlog為突破點進行分析為什麼大廠中的預設隔離級别是RC。總體來說以時間線為基準進行講解。

2binlog格式

為什麼MySQL預設的隔離級别是RR而大廠使用的是RC?

3為什麼MySQL預設的隔離級别是RR

參考:​​網際網路項目中mysql應該選什麼事務隔離級别​​ 在Oracle,SqlServer中都是選擇讀已送出(Read Commited)作為預設的隔離級别,為什麼Mysql不選擇讀已送出(Read Commited)作為預設隔離級别,而選擇可重複讀(Repeatable Read)作為預設的隔離級别呢?

首先要明确一點:MySQL需要主從複制!。主從複制需要binlog和事務的隔離級别。

曆史原因:5.0版本及之前binlog隻支援STATEMENT這種格式。為了支援主從複制,那麼事務的隔離級别隻能是可重複讀(Repeatable Read)。

因為當binlog為STATEMENT格式,且隔離級别為讀已送出(Read Commited)時,會有bug導緻主從庫的資料不一緻。

如下圖所示,在主(master)上執行如下事務。

為什麼MySQL預設的隔離級别是RR而大廠使用的是RC?

此時在主(master)上執行下列語句

select * from test;      

輸出如下

+---+
| b |
+---+
| 3 |
+---+
1 row in set      

但是,你在此時在從(slave)上執行該語句,得出輸出如下

Empty set      

這樣,你就出現了主從不一緻性的問題!原因其實很簡單,就是在master上執行的順序為先删後插!而此時binlog為STATEMENT格式,它記錄的順序為先插後删!從(slave)同步的是binglog,是以從機執行的順序和主機不一緻!就會出現主從不一緻!

如何解決? 解決方案有兩種。

  • 隔離級别設為可重複讀(Repeatable Read),在該隔離級别下引入間隙鎖。當Session 1執行delete語句時,會鎖住間隙。那麼,Ssession 2執行插入語句就會阻塞住!
  • 将binglog的格式修改為row格式,此時是基于行的複制,自然就不會出現sql執行順序不一樣的問題!奈何這個格式在mysql5.1版本開始才引入。

是以由于曆史原因,mysql将預設的隔離級别設為可重複讀(Repeatable Read),保證主從複制不出問題!

4為什麼大廠MySQL設定的隔離級别是RC

  • RC和RR的一個很大的差別是RR解決了不可重複讀的問題。但是仔細想一想,不可重複讀是問題嗎?其實不是問題。我第一次讀到的是1,再次讀的時候為2,中間有人把1修改為2,那我讀取到2就沒問題。RC反應的是真實資料的變遷。主要資料真實有效(沒送出就是髒讀,無效),為什麼怕被别人讀出來呢?
  • RR下有間隙鎖,使用鎖就會導緻資源的消耗和等待。

5MySQL主從複制的三種方式

5.1異步複制

為什麼MySQL預設的隔離級别是RR而大廠使用的是RC?
  • Slave 端的 IO 程序連接配接上 Master,向 Master 請求指定日志檔案的指定位置(或者從最開始的日志)之後的日志内容;
  • Master 接收到來自 Slave 的 IO 程序的請求後,負責複制的 IO 程序根據 Slave 的請求資訊,讀取相應日志内容,傳回給Slave 的IO程序,并将本次請求讀取的 bin-log 檔案名及位置一起傳回給 Slave 端 Slave 端的 IO
  • 程序接收到資訊後,将接收到的日志内容依次添加到 Slave 端的 relay-log(中繼日志) 檔案的最末端,并将讀取到的 Master端的 bin-log 的檔案名和位置記錄到 master-info 檔案中,以便在下一次讀取的時候能夠清楚的告訴 Master:”我需要從某個 bin-log 的哪個位置開始往後的日志内容,請發給我”;
  • Slave 端的 Sql 程序檢測到 relay-log(中繼日志)中新增加了内容後,會馬上解析 relay-log 的内容成為在 Master 端真實執行時候的那些可執行的内容,并在本地執行。

存在的問題:

主庫的資料commit到binlog後則傳回SUCCESS給用戶端。因為binlog同步給slave是異步的,如果此時master送出而未同步給到slave,則資料發生丢失。

5.2半同步複制

為什麼MySQL預設的隔離級别是RR而大廠使用的是RC?

主庫在執行完用戶端送出的事務後不是立刻傳回給用戶端,而是等待至少一個從庫接收到并寫到relay log中才傳回給用戶端。相對于異步複制,半同步複制提高了資料的安全性,同時它也造成了一定程度的延遲,這個延遲最少是一個TCP/IP往返的時間。是以,半同步複制最好在低延時的網絡中使用。

首先明确一點:master上已送出,然後等待slave的ACK,最後傳回給用戶端結果。

存在的問題:

幻讀:當使用者發起一個事務,該事務已經寫入redo日志和binlog日志,是以該資料為有效狀态。在RC隔離級别下其他事務是可以讀取到的。如果在等待slave的ack過程中binlog還沒傳輸到slave上,則其他事務查詢該資料為修改後的資料,此時master當機。slave上升為master。此時在查詢新master則查詢不到資料,出現幻讀。

資料丢失:提高資料的安全性,但不能完全避免資料丢失。

5.3增強半同步複制

為什麼MySQL預設的隔離級别是RR而大廠使用的是RC?

現在我們已經知道,在半同步環境下,主庫是在事務送出之後等待Slave ACK,是以才會有資料不一緻問題。是以這個Slave ACK在什麼時間去等待,也是一個很關鍵的問題了。是以MySQL針對半同步複制的問題,在5.7.2引入了Loss-less Semi-Synchronous,在調用binlog sync之後,engine層commit之前等待Slave ACK。這樣隻有在确認Slave收到事務events後,事務才會送出。

首先明确一點:在等待ack的時候,master狀态為未送出。

6參考