自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MySQL為何使用可重復(fù)讀(Repeatable Read)為默認(rèn)隔離級(jí)別?

數(shù)據(jù)庫(kù) MySQL
MySQL的鎖系統(tǒng):shared lock 和 exclusive lock 即共享鎖和排他鎖,也叫讀鎖(S)和寫(xiě)鎖(X),共享鎖和排他鎖都屬于悲觀鎖。排他鎖又可以可以分為行鎖和表鎖。

[[405301]]

本文轉(zhuǎn)載自微信公眾號(hào)「java寶典」,作者iTengyu。轉(zhuǎn)載本文請(qǐng)聯(lián)系java寶典公眾號(hào)。

 群里有小伙伴面試時(shí),碰到面試官提了個(gè)很刁鉆的問(wèn)題:Mysql為何使用可重復(fù)讀(Repeatable read)為默認(rèn)隔離級(jí)別???

下面進(jìn)入正題:

我們都知道事務(wù)的幾種性質(zhì) :原子性、一致性、隔離性和持久性 (ACID)

為了維持一致性和隔離性,一般使用加鎖這種方式來(lái)處理,但是加鎖相對(duì)帶來(lái)的是并發(fā)處理能力的降低

而數(shù)據(jù)庫(kù)是個(gè)高并發(fā)的應(yīng)用,因此對(duì)于加鎖的處理是事務(wù)的精髓.

下面我們來(lái)了解一下封鎖協(xié)議,以及事務(wù)在數(shù)據(jù)庫(kù)中做了什么

封鎖協(xié)議(Locking Protocol)

MySQL的鎖系統(tǒng):shared lock 和 exclusive lock 即共享鎖和排他鎖,也叫讀鎖(S)和寫(xiě)鎖(X),共享鎖和排他鎖都屬于悲觀鎖。排他鎖又可以可以分為行鎖和表鎖。

封鎖協(xié)議(Locking Protocol): 在使用X鎖或S鎖對(duì)數(shù)據(jù)加鎖時(shí),約定的一些規(guī)則.例如何時(shí)申請(qǐng)X或S鎖,持續(xù)時(shí)間,何時(shí)釋放鎖等.

一級(jí)、二級(jí)、三級(jí)封鎖協(xié)議

對(duì)封鎖方式規(guī)定不同的規(guī)則,就形成了各種不同的封鎖協(xié)議,不同的封鎖協(xié)議,為并發(fā)操作的正確性提供不同程度的保證

一級(jí)封鎖協(xié)議

一級(jí)封鎖協(xié)議定義:事務(wù)T在修改數(shù)據(jù)R之前必須先對(duì)其加X(jué)鎖(排他鎖),直到事務(wù)結(jié)束才釋放。事務(wù)結(jié)束包括正常結(jié)束(COMMIT)和非正常結(jié)束(ROLLBACK)。

一級(jí)封鎖協(xié)議可以防止丟失修改,并保證事務(wù)T是可恢復(fù)的。使用一級(jí)封鎖協(xié)議可以解決丟失修改問(wèn)題。

在一級(jí)封鎖協(xié)議中,如果僅僅是讀數(shù)據(jù)不對(duì)其進(jìn)行修改,是不需要加鎖的,它不能保證可重復(fù)讀和不讀“臟”數(shù)據(jù)。

二級(jí)封鎖協(xié)議

二級(jí)封鎖協(xié)議定義:一級(jí)封鎖協(xié)議加上事務(wù)T在讀取數(shù)據(jù)R之前必須先對(duì)其加S鎖(共享鎖),讀完后釋放S鎖。事務(wù)的加鎖和解鎖嚴(yán)格分為兩個(gè)階段,第一階段加鎖,第二階段解鎖。

  • 加鎖階段: 在對(duì)任何數(shù)據(jù)進(jìn)行讀操作之前要申請(qǐng)并獲得S鎖(共享鎖,其它事務(wù)可以繼續(xù)加共享鎖,但不能加排它鎖),在進(jìn)行寫(xiě)操作之前要申請(qǐng)并獲得X鎖(排它鎖,其它事務(wù)不能再獲得任何鎖)。加鎖不成功,則事務(wù)進(jìn)入等待狀態(tài),直到加鎖成功才繼續(xù)執(zhí)行。
  • 解鎖階段:當(dāng)事務(wù)釋放了一個(gè)封鎖以后,事務(wù)進(jìn)入解鎖階段,在該階段只能進(jìn)行解鎖操作不能再進(jìn)行加鎖操作。

二級(jí)封鎖協(xié)議除防止了丟失修改,還可以進(jìn)一步防止讀“臟”數(shù)據(jù)。但在二級(jí)封鎖協(xié)議中,由于讀完數(shù)據(jù)后釋放S鎖,所以它不能保證可重復(fù)讀。

二級(jí)封鎖的目的是保證并發(fā)調(diào)度的正確性。就是說(shuō),如果事務(wù)滿足兩段鎖協(xié)議,那么事務(wù)的并發(fā)調(diào)度策略是串行性的。保證事務(wù)的并發(fā)調(diào)度是串行化(串行化很重要,尤其是在數(shù)據(jù)恢復(fù)和備份的時(shí)候)

三級(jí)封鎖協(xié)議

三級(jí)封鎖協(xié)議定義:一級(jí)封鎖協(xié)議加上事務(wù)T在讀取數(shù)據(jù)R之前必須先對(duì)其加S鎖(共享鎖),直到事務(wù)結(jié)束才釋放。在一級(jí)封鎖協(xié)議(一級(jí)封鎖協(xié)議:修改之前先加X(jué)鎖,事務(wù)完成釋放)的基礎(chǔ)上加上S鎖,事務(wù)結(jié)束后釋放S鎖

三級(jí)封鎖協(xié)議除防止了丟失修改和不讀“臟”數(shù)據(jù)外,還進(jìn)一步防止了不可重復(fù)讀。上述三級(jí)協(xié)議的主要區(qū)別在于什么操作需要申請(qǐng)封鎖,以及何時(shí)釋放。

事務(wù)四種隔離級(jí)別

在數(shù)據(jù)庫(kù)操作中,為了有效保證并發(fā)讀取數(shù)據(jù)的正確性,提出的事務(wù)隔離級(jí)別。上面提到的封鎖協(xié)議 ,也是為了構(gòu)建這些隔離級(jí)別存在的。

隔離級(jí)別 臟讀(Dirty Read) 不可重復(fù)讀(NonRepeatable Read) 幻讀(Phantom Read)

隔離級(jí)別 臟讀(Dirty Read) 不可重復(fù)讀(NonRepeatable Read) 幻讀(Phantom Read)
未提交讀(Read uncommitted) 可能 可能 可能
已提交讀(Read committed) 不可能 可能 可能
可重復(fù)讀(Repeatable read) 不可 不可 可能
可串行化(Serializable ) 不可能 不可能 不可能

為什么是RR

一般的DBMS系統(tǒng),默認(rèn)都會(huì)使用讀提交(Read-Comitted,RC)作為默認(rèn)隔離級(jí)別,如Oracle、SQL Server等,而MySQL卻使用可重復(fù)讀(Read-Repeatable,RR)。要知道,越高的隔離級(jí)別,能解決的數(shù)據(jù)一致性問(wèn)題越多,理論上性能的損耗更大,且并發(fā)性越低。隔離級(jí)別依次為: SERIALIZABLE > RR > RC > RU

我們可以通過(guò)以下語(yǔ)句設(shè)置和獲取數(shù)據(jù)庫(kù)的隔離級(jí)別:

查看系統(tǒng)的隔離級(jí)別:

  1. mysql> select @@global.tx_isolation isolation
  2. +-----------------+ 
  3. isolation       | 
  4. +-----------------+ 
  5. REPEATABLE-READ | 
  6. +-----------------+ 
  7. 1 row in set, 1 warning (0.00 sec) 

查看當(dāng)前會(huì)話的 隔離級(jí)別:

mysql> select @@tx_isolation;

  1. mysql> select @@tx_isolation; 
  2. +----------------+ 
  3. | @@tx_isolation | 
  4. +----------------+ 
  5. READ-COMMITTED | 
  6. +----------------+ 
  7. 1 row in set, 1 warning (0.00 sec) 

設(shè)置會(huì)話的隔離級(jí)別,隔離級(jí)別由低到高設(shè)置依次為:

  1. set session transacton isolation level read uncommitted
  2. set session transacton isolation level read committed
  3. set session transacton isolation level repeatable read
  4. set session transacton isolation level serializable

設(shè)置當(dāng)前系統(tǒng)的隔離級(jí)別,隔離級(jí)別由低到高設(shè)置依次為:

  1. set global transacton isolation level read uncommitted
  2. set global transacton isolation level read committed
  3. set global transacton isolation level repeatable read
  4. set global transacton isolation level serializable

可重復(fù)讀(Repeated Read):可重復(fù)讀?;阪i機(jī)制并發(fā)控制的DBMS需要對(duì)選定對(duì)象的讀鎖(read locks)和寫(xiě)鎖(write locks)一直保持到事務(wù)結(jié)束,但不要求“范圍鎖(range-locks)”,因此可能會(huì)發(fā)生“幻影讀(phantom reads)” 在該事務(wù)級(jí)別下,保證同一個(gè)事務(wù)從開(kāi)始到結(jié)束獲取到的數(shù)據(jù)一致。是Mysql的默認(rèn)事務(wù)級(jí)別。

下面我們先來(lái)思考2個(gè)問(wèn)題

  • 在讀已提交(Read Commited)級(jí)別下,出現(xiàn)不可重復(fù)讀問(wèn)題怎么辦?需要解決么?

不用解決,這個(gè)問(wèn)題是可以接受的!畢竟你數(shù)據(jù)都已經(jīng)提交了,讀出來(lái)本身就沒(méi)有太大問(wèn)題!Oracle ,SqlServer 默認(rèn)隔離級(jí)別就是RC,我們也沒(méi)有更改過(guò)它的默認(rèn)隔離級(jí)別.

  • 在Oracle,SqlServer中都是選擇讀已提交(Read Commited)作為默認(rèn)的隔離級(jí)別,為什么Mysql不選擇讀已提交(Read Commited)作為默認(rèn)隔離級(jí)別,而選擇可重復(fù)讀(Repeatable Read)作為默認(rèn)的隔離級(jí)別呢?

歷史原因,早階段Mysql(5.1版本之前)的Binlog類(lèi)型Statement是默認(rèn)格式,即依次記錄系統(tǒng)接受的SQL請(qǐng)求;5.1及以后,MySQL提供了Row,Mixed,statement 3種Binlog格式, 當(dāng)binlog為statement格式,使用RC隔離級(jí)別時(shí),會(huì)出現(xiàn)BUG因此Mysql將可重復(fù)讀(Repeatable Read)作為默認(rèn)的隔離級(jí)別!

Binlog簡(jiǎn)介

Mysql binlog是二進(jìn)制日志文件,用于記錄mysql的數(shù)據(jù)更新或者潛在更新(比如DELETE語(yǔ)句執(zhí)行刪除而實(shí)際并沒(méi)有符合條件的數(shù)據(jù)),在mysql主從復(fù)制中就是依靠的binlog??梢酝ㄟ^(guò)語(yǔ)句“show binlog events in 'binlogfile'”來(lái)查看binlog的具體事件類(lèi)型。binlog記錄的所有操作實(shí)際上都有對(duì)應(yīng)的事件類(lèi)型的

MySQL binlog的三種工作模式:Row(用到MySQL的特殊功能如存儲(chǔ)過(guò)程、觸發(fā)器、函數(shù),又希望數(shù)據(jù)最大化一直則選擇Row模式,我們公司選擇的是row) 簡(jiǎn)介:日志中會(huì)記錄每一行數(shù)據(jù)被修改的情況,然后在slave端對(duì)相同的數(shù)據(jù)進(jìn)行修改。優(yōu)點(diǎn):能清楚的記錄每一行數(shù)據(jù)修改的細(xì)節(jié) 缺點(diǎn):數(shù)據(jù)量太大

Statement (默認(rèn))簡(jiǎn)介:每一條被修改數(shù)據(jù)的sql都會(huì)記錄到master的bin-log中,slave在復(fù)制的時(shí)候sql進(jìn)程會(huì)解析成和原來(lái)master端執(zhí)行過(guò)的相同的sql再次執(zhí)行。在主從同步中一般是不建議用statement模式的,因?yàn)闀?huì)有些語(yǔ)句不支持,比如語(yǔ)句中包含UUID函數(shù),以及LOAD DATA IN FILE語(yǔ)句等 優(yōu)點(diǎn):解決了 Row level下的缺點(diǎn),不需要記錄每一行的數(shù)據(jù)變化,減少bin-log日志量,節(jié)約磁盤(pán)IO,提高新能 缺點(diǎn):容易出現(xiàn)主從復(fù)制不一致

Mixed(混合模式)簡(jiǎn)介:結(jié)合了Row level和Statement level的優(yōu)點(diǎn),同時(shí)binlog結(jié)構(gòu)也更復(fù)雜。

我們可以簡(jiǎn)單理解為binlog是一個(gè)記錄數(shù)據(jù)庫(kù)更改的文件,主從復(fù)制時(shí)需要此文件,具體細(xì)節(jié)先略過(guò)

主從不一致實(shí)操

binlog為STATEMENT格式,且隔離級(jí)別為**讀已提交(Read Commited)**時(shí),有什么bug呢?測(cè)試表:

  1. mysql> select * from test; 
  2. +----+------+------+ 
  3. | id | name | age  | 
  4. +----+------+------+ 
  5. |  1 | NULL | NULL | 
  6. |  2 | NULL | NULL | 
  7. |  3 | NULL | NULL | 
  8. |  4 | NULL | NULL | 
  9. |  5 | NULL | NULL | 
  10. |  6 | NULL | NULL | 
  11. +----+------+------+ 
  12. rows in set (0.00 sec) 
Session1 Session2
mysql> set tx_isolation = 'read-committed';  
Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> set tx_isolation = 'read-committed';
Query OK, 0 rows affected, 1 warning (0.00 sec)  
begin;
Query OK, 0 rows affected (0.00 sec)
begin;
Query OK, 0 rows affected (0.00 sec)
delete from test where 1=1;  
Query OK, 6 rows affected (0.00 sec)  
  insert into test values (null,'name',100);
Query OK, 1 row affected (0.00 sec)  
  commit;
Query OK, 0 rows affected (0.01 sec)  
commit;  
Query OK, 0 rows affected (0.01 sec)

Master此時(shí)輸出

  1. select * from test; 
  2. +----+------+------+ 
  3. | id | name | age  | 
  4. +----+------+------+ 
  5. |  7 | name |  100 | 
  6. +----+------+------+ 
  7. 1 row in set (0.00 sec) 

但是,你在此時(shí)在從(slave)上執(zhí)行該語(yǔ)句,得出輸出

  1. mysql> select * from test; 
  2. Empty set (0.00 sec) 

在master上執(zhí)行的順序?yàn)橄葎h后插!而此時(shí)binlog為STATEMENT格式,是基于事務(wù)記錄,在事務(wù)未提交前,二進(jìn)制日志先緩存,提交后再寫(xiě)入記錄的,因此順序?yàn)橄炔搴髣h!slave同步的是binglog,因此從機(jī)執(zhí)行的順序和主機(jī)不一致!slave在插入后刪除了所有數(shù)據(jù).

解決方案有兩種!(1)隔離級(jí)別設(shè)為可重復(fù)讀(Repeatable Read),在該隔離級(jí)別下引入間隙鎖。當(dāng)Session 1執(zhí)行delete語(yǔ)句時(shí),會(huì)鎖住間隙。那么,Ssession 2執(zhí)行插入語(yǔ)句就會(huì)阻塞住!(2)將binglog的格式修改為row格式,此時(shí)是基于行的復(fù)制,自然就不會(huì)出現(xiàn)sql執(zhí)行順序不一樣的問(wèn)題!奈何這個(gè)格式在mysql5.1版本開(kāi)始才引入。因此由于歷史原因,mysql將默認(rèn)的隔離級(jí)別設(shè)為可重復(fù)讀(Repeatable Read),保證主從復(fù)制不出問(wèn)題!

RU和Serializable

項(xiàng)目中不太使用**讀未提交(Read UnCommitted)和串行化(Serializable)**兩個(gè)隔離級(jí)別,原因:

讀未提交(Read UnCommitted)

允許臟讀,也就是可能讀取到其他會(huì)話中未提交事務(wù)修改的數(shù)據(jù) 一個(gè)事務(wù)讀到另一個(gè)事務(wù)未提交讀數(shù)據(jù)

串行化(Serializable)

使用的悲觀鎖的理論,實(shí)現(xiàn)簡(jiǎn)單,數(shù)據(jù)更加安全,但是并發(fā)能力非常差。如果你的業(yè)務(wù)并發(fā)的特別少或者沒(méi)有并發(fā),同時(shí)又要求數(shù)據(jù)及時(shí)可靠的話,可以使用這種模式。一般是使用mysql自帶分布式事務(wù)功能時(shí)才使用該隔離級(jí)別

RC和 RR

此時(shí)我們糾結(jié)的應(yīng)該就只有一個(gè)問(wèn)題了:隔離級(jí)別是用讀已提交還是可重復(fù)讀?

接下來(lái)對(duì)這兩種級(jí)別進(jìn)行對(duì)比的第一種情況:

在RR隔離級(jí)別下,存在間隙鎖,導(dǎo)致出現(xiàn)死鎖的幾率比RC大的多!

實(shí)現(xiàn)一個(gè)簡(jiǎn)單的間隙鎖例子

  1. select * from test where id <11 ; 
  2. +----+------+------+ 
  3. | id | name | age  | 
  4. +----+------+------+ 
  5. |  1 | NULL | NULL | 
  6. |  2 | NULL | NULL | 
  7. |  3 | NULL | NULL | 
  8. |  4 | NULL | NULL | 
  9. |  5 | NULL | NULL | 
  10. |  6 | NULL | NULL | 
  11. |  7 | name |   7  | 
  12. +----+------+------+ 
  13. rows in set (0.00 sec) 
session1 session2
mysql> set tx_isolation = 'repeatable-read';  
Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> set tx_isolation = 'repeatable-read';
Query OK, 0 rows affected, 1 warning (0.00 sec)  
Begin;  
select * from test where id <11 for update;  
  insert into test values(null,'name',9);   //被阻塞!
commit;  
Query OK, 0 rows affected (0.00 sec)  
  Query OK, 1 row affected (12.23 sec) //鎖釋放后完成了操作

在RR隔離級(jí)別下,可以鎖住(-∞,10] 這個(gè)間隙,防止其他事務(wù)插入數(shù)據(jù)!而在RC隔離級(jí)別下,不存在間隙鎖,其他事務(wù)是可以插入數(shù)據(jù)!

ps:在RC隔離級(jí)別下并不是不會(huì)出現(xiàn)死鎖,只是出現(xiàn)幾率比RR低而已

鎖表和鎖行

在RR隔離級(jí)別下,條件列未命中索引會(huì)鎖表!而在RC隔離級(jí)別下,只鎖行

  1. select * from test; 
  2. +----+------+------+ 
  3. | id | name | age  | 
  4. +----+------+------+ 
  5. |  8 | name |   11 | 
  6. |  9 | name |    9 | 
  7. | 10 | name |   15 | 
  8. | 11 | name |   15 | 
  9. | 12 | name |   16 | 
  10. +----+------+------+ 

鎖表的例子:

session1 session2
Begin;  
update test set age = age+1  where age = 15;  
Rows matched: 2  Changed: 2  Warnings: 0  
  insert into test values(null,'test',15);
ERROR 1205 (HY000): Lock wait timeout exceeded;  
Commit;

session2插入失敗 查詢(xún) 數(shù)據(jù)顯示:

  1. select * from test; 
  2. +----+------+------+ 
  3. | id | name | age  | 
  4. +----+------+------+ 
  5. |  8 | name |   11 | 
  6. |  9 | name |    9 | 
  7. | 10 | name |   16 | 
  8. | 11 | name |   16 | 
  9. | 12 | name |   16 | 
  10. +----+------+------+ 

半一致性讀(semi-consistent)特性

在RC隔離級(jí)別下,半一致性讀(semi-consistent)特性增加了update操作的并發(fā)性!

在5.1.15的時(shí)候,innodb引入了一個(gè)概念叫做“semi-consistent”,減少了更新同一行記錄時(shí)的沖突,減少鎖等待。所謂半一致性讀就是,一個(gè)update語(yǔ)句,如果讀到一行已經(jīng)加鎖的記錄,此時(shí)InnoDB返回記錄最近提交的版本,判斷此版本是否滿足where條件。若滿足則重新發(fā)起一次讀操作,此時(shí)會(huì)讀取行的最新版本并加鎖!

建議

在RC級(jí)別下,用的binlog為row格式,是基于行的復(fù)制,Innodb的創(chuàng)始人也是建議binlog使用該格式

互聯(lián)網(wǎng)項(xiàng)目請(qǐng)用:讀已提交(Read Commited)這個(gè)隔離級(jí)別

總結(jié)

由于歷史原因,老版本Mysql的binlog使用statement格式,不使用RR隔離級(jí)別會(huì)導(dǎo)致主從不一致的情況

目前(5.1版本之后)我們使用row格式的binlog 配合RC隔離級(jí)別可以實(shí)現(xiàn)更好的并發(fā)性能.

 

責(zé)任編輯:武曉燕 來(lái)源: java寶典
相關(guān)推薦

2024-07-16 08:19:46

MySQL數(shù)據(jù)InnoDB

2010-09-30 16:21:40

DB2隔離級(jí)別

2024-05-13 11:46:33

MySQL數(shù)據(jù)庫(kù)

2021-06-17 09:16:34

MySQL數(shù)據(jù)庫(kù)隔離級(jí)別

2022-06-29 11:01:05

MySQL事務(wù)隔離級(jí)別

2024-04-23 08:17:14

MySQL數(shù)據(jù)庫(kù)隔離

2022-01-03 07:18:05

臟讀幻讀 MySQL

2020-07-02 08:22:56

MySQL間隙鎖過(guò)行鎖

2023-11-01 14:13:00

MySQL事務(wù)隔離級(jí)別

2022-06-10 11:51:49

MySQL事務(wù)隔離

2019-12-24 14:50:01

MySQL可重復(fù)讀數(shù)據(jù)庫(kù)

2024-03-11 00:00:00

mysqlInnoDB幻讀

2023-02-02 07:06:10

2023-07-03 08:15:46

MySQLInnoDB

2021-07-26 10:28:13

MySQL事務(wù)隔離

2023-12-12 10:54:55

MySQL模式InnoDB

2022-09-21 09:00:10

MySQL幻讀隔離級(jí)別

2018-12-19 16:46:38

MySQL事務(wù)隔離數(shù)據(jù)庫(kù)

2023-10-16 10:29:51

mysqlMVCC

2022-09-08 08:02:26

MySQL隔離
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)