主頁 > 知識(shí)庫 > MySQL中的樂觀鎖,悲觀鎖和MVCC全面解析

MySQL中的樂觀鎖,悲觀鎖和MVCC全面解析

熱門標(biāo)簽:鐵路電話系統(tǒng) 地方門戶網(wǎng)站 百度競價(jià)排名 呼叫中心市場(chǎng)需求 網(wǎng)站排名優(yōu)化 AI電銷 Linux服務(wù)器 服務(wù)外包

前言

在數(shù)據(jù)庫的實(shí)際使用過程中,我們常常會(huì)遇到不希望數(shù)據(jù)被同時(shí)寫或者讀的情景,例如秒殺場(chǎng)景下,兩個(gè)請(qǐng)求同時(shí)讀到系統(tǒng)還有庫存1個(gè),然后又先后把庫存更新為0,這時(shí)候就會(huì)出現(xiàn)超賣的情況,這時(shí)候貨物的實(shí)際庫存和我們的記錄就會(huì)對(duì)應(yīng)不上了。

為了解決這種資源競爭導(dǎo)致的數(shù)據(jù)不一致等問題,我們需要有一種機(jī)制來進(jìn)行保證數(shù)據(jù)的正確訪問和修改,而在數(shù)據(jù)庫中,這種機(jī)制就是數(shù)據(jù)庫的并發(fā)控制。其中樂觀并發(fā)控制,悲觀并發(fā)控制和多版本并發(fā)控制是數(shù)據(jù)庫并發(fā)控制主要采用的技術(shù)手段。

悲觀并發(fā)控制

本質(zhì)

維基百科:在關(guān)系數(shù)據(jù)庫管理系統(tǒng)里,悲觀并發(fā)控制(又名“悲觀鎖”,Pessimistic Concurrency Control,縮寫“PCC”)是一種并發(fā)控制的方法。它可以阻止一個(gè)事務(wù)以影響其他用戶的方式來修改數(shù)據(jù)。如果一個(gè)事務(wù)執(zhí)行的操作讀某行數(shù)據(jù)應(yīng)用了鎖,那只有當(dāng)這個(gè)事務(wù)把鎖釋放,其他事務(wù)才能夠執(zhí)行與該鎖沖突的操作。

事實(shí)上我們常說的悲觀鎖并不是一種實(shí)際的鎖,而是一種并發(fā)控制的思想,悲觀并發(fā)控制對(duì)于數(shù)據(jù)被修改持悲觀的態(tài)度,認(rèn)為數(shù)據(jù)被外界訪問時(shí),必然會(huì)產(chǎn)生沖突,所以在數(shù)據(jù)處理的過程中都采用加鎖的方式來保證對(duì)資源的獨(dú)占。

數(shù)據(jù)庫的鎖機(jī)制其實(shí)都是基于悲觀并發(fā)控制的觀點(diǎn)進(jìn)行實(shí)現(xiàn)的,而且按照實(shí)際使用情況,數(shù)據(jù)庫的鎖又可以分為許多種類,具體可以見我后面的文章。

實(shí)現(xiàn)方式

數(shù)據(jù)庫悲觀鎖的加鎖流程大致如下:

開始事務(wù)后,按照操作類型給需要加鎖的數(shù)據(jù)申請(qǐng)加某一類鎖:例如共享行鎖等
加鎖成功則繼續(xù)后面的操作,如果數(shù)據(jù)已經(jīng)被加了其他的鎖,而且和現(xiàn)在要加的鎖沖突,則會(huì)加鎖失?。ɡ缫呀?jīng)加了排他鎖),此時(shí)需等待其他的鎖釋放(可能出現(xiàn)死鎖)
完成事務(wù)后釋放所加的鎖

優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

悲觀并發(fā)控制采取的是保守策略:“先取鎖,成功了才訪問數(shù)據(jù)”,這保證了數(shù)據(jù)獲取和修改都是有序進(jìn)行的,因此適合在寫多讀少的環(huán)境中使用。當(dāng)然使用悲觀鎖無法維持非常高的性能,但是在樂觀鎖也無法提供更好的性能前提下,悲觀鎖卻可以做到保證數(shù)據(jù)的安全性。

缺點(diǎn):
由于需要加鎖,而且可能面臨鎖沖突甚至死鎖的問題,悲觀并發(fā)控制增加了系統(tǒng)的額外開銷,降低了系統(tǒng)的效率,同時(shí)也會(huì)降低了系統(tǒng)的并行性。

樂觀并發(fā)控制

本質(zhì)

維基百科:在關(guān)系數(shù)據(jù)庫管理系統(tǒng)里,樂觀并發(fā)控制(又名“樂觀鎖”,Optimistic Concurrency Control,縮寫“OCC”)是一種并發(fā)控制的方法。它假設(shè)多用戶并發(fā)的事務(wù)在處理時(shí)不會(huì)彼此互相影響,各事務(wù)能夠在不產(chǎn)生鎖的情況下處理各自影響的那部分?jǐn)?shù)據(jù)。
樂觀并發(fā)控制對(duì)數(shù)據(jù)修改持樂觀態(tài)度,認(rèn)為即使在并發(fā)環(huán)境中,外界對(duì)數(shù)據(jù)的操作一般是不會(huì)造成沖突,所以并不會(huì)去加鎖,而是在提交數(shù)據(jù)更新之前,每個(gè)事務(wù)會(huì)先檢查在該事務(wù)讀取數(shù)據(jù)后,有沒有其他事務(wù)又修改了該數(shù)據(jù)。如果其他事務(wù)有更新的話,則讓返回沖突信息,讓用戶決定如何去做下一步,比如說重試或者回滾。

可以看出,樂觀鎖其實(shí)也不是實(shí)際的鎖,甚至沒有用到鎖來實(shí)現(xiàn)并發(fā)控制,而是采取其他方式來判斷能否修改數(shù)據(jù)。樂觀鎖一般是用戶自己實(shí)現(xiàn)的一種鎖機(jī)制,雖然沒有用到實(shí)際的鎖,但是能產(chǎn)生加鎖的效果。

實(shí)現(xiàn)方式

CAS(比較與交換,Compare and swap) 是一種有名的無鎖算法。無鎖編程,即不使用鎖的情況下實(shí)現(xiàn)多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實(shí)現(xiàn)變量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。實(shí)現(xiàn)非阻塞同步的方案稱為“無鎖編程算法”( Non-blocking algorithm)。

樂觀鎖基本都是基于 CAS(Compare and swap)算法來實(shí)現(xiàn)的。我們先來看下CAS過程,一個(gè)CAS操作的過程可以用以下c代碼表示:

int cas(long *addr, long old, long new)
{
 /* Executes atomically. */
 if(*addr != old)
  return 0;
 *addr = new;
 return 1;
}

CAS有3個(gè)操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B,否則什么都不做。整個(gè)CAS操作是一個(gè)原子操作,是不可分割的。

樂觀鎖的實(shí)現(xiàn)就類似于上面的過程,主要有以下幾種方式:

  • 版本號(hào)標(biāo)記:在表中新增一個(gè)字段:version,用于保存版本號(hào)。獲取數(shù)據(jù)的時(shí)候同時(shí)獲取版本號(hào),然后更新數(shù)據(jù)的時(shí)候用以下命令:update xxx set version=version+1,… where … version="old version" and ....。這時(shí)候通過判斷返回結(jié)果的影響行數(shù)是否為0來判斷是否更新成功,更新失敗則說明有其他請(qǐng)求已經(jīng)更新了數(shù)據(jù)了。
  • 時(shí)間戳標(biāo)記:和版本號(hào)一樣,只是通過時(shí)間戳來判斷。一般來說很多數(shù)據(jù)表都會(huì)有更新時(shí)間這一個(gè)字段,通過這個(gè)字段來判斷就不用再新增一個(gè)字段了。
  • 待更新字段:如果沒有時(shí)間戳字段,而且不想新增字段,那可以考慮用待更新字段來判斷,因?yàn)楦聰?shù)據(jù)一般都會(huì)發(fā)生變化,那更新前可以拿要更新的字段的舊值和數(shù)據(jù)庫的現(xiàn)值進(jìn)行比對(duì),沒有變化則更新。
  • 所有字段標(biāo)記:數(shù)據(jù)表所有字段都用來判斷。這種相當(dāng)于就、不僅僅對(duì)某幾個(gè)字段做加鎖了,而是對(duì)整個(gè)數(shù)據(jù)行加鎖,只要本行數(shù)據(jù)發(fā)生變化,就不進(jìn)行更新。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

樂觀并發(fā)控制沒有實(shí)際加鎖,所以沒有額外開銷,也不錯(cuò)出現(xiàn)死鎖問題,適用于讀多寫少的并發(fā)場(chǎng)景,因?yàn)闆]有額外開銷,所以能極大提高數(shù)據(jù)庫的性能。

缺點(diǎn):
樂觀并發(fā)控制不適合于寫多讀少的并發(fā)場(chǎng)景下,因?yàn)闀?huì)出現(xiàn)很多的寫沖突,導(dǎo)致數(shù)據(jù)寫入要多次等待重試,在這種情況下,其開銷實(shí)際上是比悲觀鎖更高的。而且樂觀鎖的業(yè)務(wù)邏輯比悲觀鎖要更為復(fù)雜,業(yè)務(wù)邏輯上要考慮到失敗,等待重試的情況,而且也無法避免其他第三方系統(tǒng)對(duì)數(shù)據(jù)庫的直接修改的情況。

多版本并發(fā)控制

本質(zhì)

維基百科: 多版本并發(fā)控制(Multiversion concurrency control, MCC 或 MVCC),是數(shù)據(jù)庫管理系統(tǒng)常用的一種并發(fā)控制,也用于程序設(shè)計(jì)語言實(shí)現(xiàn)事務(wù)內(nèi)存。

樂觀并發(fā)控制和悲觀并發(fā)控制都是通過延遲或者終止相應(yīng)的事務(wù)來解決事務(wù)之間的競爭條件來保證事務(wù)的可串行化;雖然前面的兩種并發(fā)控制機(jī)制確實(shí)能夠從根本上解決并發(fā)事務(wù)的可串行化的問題,但是其實(shí)都是在解決寫沖突的問題,兩者區(qū)別在于對(duì)寫沖突的樂觀程度不同(悲觀鎖也能解決讀寫沖突問題,但是性能就一般了)。而在實(shí)際使用過程中,數(shù)據(jù)庫讀請(qǐng)求是寫請(qǐng)求的很多倍,我們?nèi)绻芙鉀Q讀寫并發(fā)的問題的話,就能更大地提高數(shù)據(jù)庫的讀性能,而這就是多版本并發(fā)控制所能做到的事情。

與悲觀并發(fā)控制和樂觀并發(fā)控制不同的是,MVCC是為了解決讀寫鎖造成的多個(gè)、長時(shí)間的讀操作餓死寫操作問題,也就是解決讀寫沖突的問題。MVCC 可以與前兩者中的任意一種機(jī)制結(jié)合使用,以提高數(shù)據(jù)庫的讀性能。

數(shù)據(jù)庫的悲觀鎖基于提升并發(fā)性能的考慮,一般都同時(shí)實(shí)現(xiàn)了多版本并發(fā)控制。不僅是MySQL,包括Oracle、PostgreSQL等其他數(shù)據(jù)庫系統(tǒng)也都實(shí)現(xiàn)了MVCC,但各自的實(shí)現(xiàn)機(jī)制不盡相同,因?yàn)镸VCC沒有一個(gè)統(tǒng)一的實(shí)現(xiàn)標(biāo)準(zhǔn)。

總的來說,MVCC的出現(xiàn)就是數(shù)據(jù)庫不滿用悲觀鎖去解決讀-寫沖突問題,因性能不高而提出的解決方案。

實(shí)現(xiàn)方式

MVCC的實(shí)現(xiàn),是通過保存數(shù)據(jù)在某個(gè)時(shí)間點(diǎn)的快照來實(shí)現(xiàn)的。每個(gè)事務(wù)讀到的數(shù)據(jù)項(xiàng)都是一個(gè)歷史快照,被稱為快照讀,不同于當(dāng)前讀的是快照讀讀到的數(shù)據(jù)可能不是最新的,但是快照隔離能使得在整個(gè)事務(wù)看到的數(shù)據(jù)都是它啟動(dòng)時(shí)的數(shù)據(jù)狀態(tài)。而寫操作不覆蓋已有數(shù)據(jù)項(xiàng),而是創(chuàng)建一個(gè)新的版本,直至所在事務(wù)提交時(shí)才變?yōu)榭梢姟?/p>

當(dāng)前讀和快照讀

什么是MySQL InnoDB下的當(dāng)前讀和快照讀?

當(dāng)前讀
像select lock in share mode(共享鎖), select for update ; update, insert ,delete(排他鎖)這些操作都是一種當(dāng)前讀,為什么叫當(dāng)前讀?就是它讀取的是記錄的最新版本,讀取時(shí)還要保證其他并發(fā)事務(wù)不能修改當(dāng)前記錄,會(huì)對(duì)讀取的記錄進(jìn)行加鎖。

快照讀
像不加鎖的select操作就是快照讀,即不加鎖的非阻塞讀;快照讀的前提是隔離級(jí)別不是未提交讀和串行化級(jí)別,因?yàn)槲刺峤蛔x總是讀取最新的數(shù)據(jù)行,而不是符合當(dāng)前事務(wù)版本的數(shù)據(jù)行。而串行化則會(huì)對(duì)所有讀取的行都加鎖

優(yōu)缺點(diǎn)

MVCC 使大多數(shù)讀操作都可以不用加鎖,這樣設(shè)計(jì)使得讀數(shù)據(jù)操作很簡單,性能很好,并且也能保證只會(huì)讀取到符合標(biāo)準(zhǔn)的行。不足之處是每行記錄都需要額外的存儲(chǔ)空間,需要做更多的行檢查工作,以及一些額外的維護(hù)工作。

適用場(chǎng)景

悲觀鎖

用來解決讀-寫沖突和寫-寫沖突的的加鎖并發(fā)控制
適用于寫多讀少,寫沖突嚴(yán)重的情況,因?yàn)楸^鎖是在讀取數(shù)據(jù)的時(shí)候就加鎖的,讀多的場(chǎng)景會(huì)需要頻繁的加鎖和很多的的等待時(shí)間,而在寫沖突嚴(yán)重的情況下使用悲觀鎖可以保證數(shù)據(jù)的一致性
數(shù)據(jù)一致性要求高
可以解決臟讀,幻讀,不可重復(fù)讀,第一類更新丟失,第二類更新丟失的問題

樂觀鎖

解決寫-寫沖突的無鎖并發(fā)控制
適用于讀多寫少,因?yàn)槿绻霈F(xiàn)大量的寫操作,寫沖突的可能性就會(huì)增大,業(yè)務(wù)層需要不斷重試,這會(huì)大大降低系統(tǒng)性能
數(shù)據(jù)一致性要求不高,但要求非常高的響應(yīng)速度
無法解決臟讀,幻讀,不可重復(fù)讀,但是可以解決更新丟失問題

MVCC

解決讀-寫沖突的無鎖并發(fā)控制
與上面兩者結(jié)合,提升它們的讀性能

以上就是MySQL中的樂觀鎖和悲觀鎖和MVCC全面解析的詳細(xì)內(nèi)容,更多關(guān)于MySQL中的樂觀鎖和悲觀鎖和MVCC的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

您可能感興趣的文章:
  • 詳解MySQL事務(wù)的隔離級(jí)別與MVCC
  • Mysql MVCC機(jī)制原理詳解
  • mysql的MVCC多版本并發(fā)控制的實(shí)現(xiàn)
  • 詳解MySQL 數(shù)據(jù)庫隔離級(jí)別與MVCC
  • 淺析MySQL - MVCC
  • mysql多版本并發(fā)控制MVCC的實(shí)現(xiàn)
  • 關(guān)于Mysql隔離級(jí)別、鎖與MVCC介紹
  • 詳解MySQL多版本并發(fā)控制機(jī)制(MVCC)源碼

標(biāo)簽:仙桃 黃山 湖南 銅川 蘭州 衡水 崇左 湘潭

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《MySQL中的樂觀鎖,悲觀鎖和MVCC全面解析》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266