4 回答

TA貢獻(xiàn)1871條經(jīng)驗(yàn) 獲得超13個(gè)贊
簡(jiǎn)單來(lái)說(shuō),Redis使用樂(lè)觀鎖,相對(duì)于悲觀鎖,在實(shí)現(xiàn)中更加簡(jiǎn)單,在某些場(chǎng)景中的性能也更好。Redis作為一個(gè)輕量級(jí)的、快速的緩存引擎,而不是一個(gè)全功能的關(guān)系型數(shù)據(jù)庫(kù),既沒(méi)有使用悲觀鎖的必要,也難以承受使用悲觀鎖的成本。
詳細(xì)來(lái)說(shuō),要深入到Redis和MySQL的事務(wù)處理機(jī)制。Redis關(guān)于事務(wù)的文檔見(jiàn)此:
Transactions(事務(wù))
Redis對(duì)于事務(wù)只提供了非常有限的支持,其實(shí)更多地是試圖繞過(guò)問(wèn)題。
首先,Redis對(duì)于同一事務(wù)中的一組操作,而不是立即執(zhí)行,而是放入一個(gè)queue中,當(dāng)執(zhí)行到EXEC時(shí),再一起執(zhí)行。事務(wù)執(zhí)行是全局獨(dú)占的,也就是同一時(shí)間只有一個(gè)事務(wù)被執(zhí)行,中途不能被其它事務(wù)打斷。Redis用這種最簡(jiǎn)單的、也是性能最差的方式避免了race condition。
其次,在Redis的事務(wù)中,如果有一個(gè)或多個(gè)操作失敗,其它操作仍然會(huì)成功,也就是說(shuō)它根本沒(méi)有回滾機(jī)制。
這種方式會(huì)帶來(lái)很多嚴(yán)重的問(wèn)題,其中之一是,無(wú)法先讀取某個(gè)數(shù)值后再進(jìn)行依賴這個(gè)值的操作,因?yàn)榉旁谝粋€(gè)事務(wù)里會(huì)被在同一個(gè)瞬間執(zhí)行,不放在同一個(gè)事務(wù)里又會(huì)導(dǎo)致race condition。解決方法是使用WATCH,它會(huì)監(jiān)視一個(gè)或多個(gè)變量,如果變量的值在調(diào)用WATCH以后和事務(wù)提交之前被別的事務(wù)修改過(guò)了,整個(gè)事務(wù)都會(huì)失敗。這類似于操作系統(tǒng)中的CAS(Compare and Set)。我不知道WATCH具體是怎么實(shí)現(xiàn)的,但是我推測(cè)它監(jiān)控了指定變量的版本號(hào)。
即使有了WATCH,Redis的事務(wù)也是受到嚴(yán)重限制的。第一,它沒(méi)有實(shí)現(xiàn)讀數(shù)據(jù)時(shí)的一致性,因?yàn)閃ATCH對(duì)于讀操作不起作用。第二,它不支持回滾。第三,在對(duì)同一變量存在大量并發(fā)寫(xiě)操作時(shí),性能會(huì)非常差,因?yàn)槊看翁峤皇聞?wù)時(shí),WATCH監(jiān)控的變量都已經(jīng)被修改了,導(dǎo)致事務(wù)將多次提交失敗。但是,Redis本來(lái)就是一個(gè)KV類型的緩存引擎,要處理的是大量讀少量寫(xiě)的場(chǎng)景,對(duì)一致性也沒(méi)有要求。
MySQL就完全不一樣了,作為一個(gè)典型的關(guān)系型數(shù)據(jù)庫(kù),它需要完整地實(shí)現(xiàn)ACID,所以Redis的方式是解決不了它的問(wèn)題的。
MySQL中的MVCC機(jī)制(Oracle的也是),通過(guò)undo 日志來(lái)獲取某個(gè)行記錄的歷史快照,從而實(shí)現(xiàn)了所謂的讀一致性。它的目的是讀取某個(gè)時(shí)間點(diǎn)上的歷史數(shù)據(jù)(而不是可能已經(jīng)被修改了的數(shù)據(jù)),而不是避免悲觀鎖的使用。嚴(yán)格地說(shuō)這不能稱之為樂(lè)觀鎖,因?yàn)樗炔籆ompare當(dāng)前版本和歷史版本,也不進(jìn)行Set。事實(shí)上,在讀取記錄的歷史快照時(shí),當(dāng)前記錄有可能(由于并發(fā)的寫(xiě)操作)已經(jīng)被加上獨(dú)占鎖。
進(jìn)一步的問(wèn)題是:有沒(méi)有可能使用樂(lè)觀鎖來(lái)實(shí)現(xiàn)RDBMS中的寫(xiě)一致性?有沒(méi)有可能使用樂(lè)觀鎖實(shí)現(xiàn)完整的ACID特性?
回答是可以。例如,MS SQL SERVER的Hekaton引擎通過(guò)一套基于時(shí)間戳的多版本管理系統(tǒng),實(shí)現(xiàn)了不使用了悲觀鎖的ACID。
但是,這并不意味著樂(lè)觀鎖必然優(yōu)于悲觀鎖。除了維護(hù)多版本的開(kāi)銷以外,樂(lè)觀鎖無(wú)法避免的一個(gè)問(wèn)題是,當(dāng)多個(gè)寫(xiě)操作試圖更新同一個(gè)對(duì)象時(shí),只有第一個(gè)操作可以成功,其它的操作都會(huì)在Compare時(shí)失敗然后回滾,從而造成極大的性能問(wèn)題。在這種情況下,樂(lè)觀鎖的性能會(huì)低于悲觀鎖。
目前的趨勢(shì)似乎是,大規(guī)模的分布式數(shù)據(jù)庫(kù)更傾向于使用樂(lè)觀鎖來(lái)達(dá)到所謂的external consistency,因?yàn)榛趥鹘y(tǒng)悲觀鎖的分布式鎖在集群大到一定程度以后(從幾百臺(tái)擴(kuò)展到成千上萬(wàn)臺(tái)時(shí)),性能開(kāi)銷就大得無(wú)法接受了。Google的Spanner就是基于樂(lè)觀鎖。當(dāng)然這完全是另外一個(gè)問(wèn)題了。

TA貢獻(xiàn)1804條經(jīng)驗(yàn) 獲得超3個(gè)贊
mysql也有樂(lè)觀鎖
樂(lè)觀鎖假設(shè)數(shù)據(jù)一般情況下不會(huì)造成沖突,所以在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會(huì)正式對(duì)數(shù)據(jù)的沖突與否進(jìn)行檢測(cè),如果發(fā)現(xiàn)沖突了,則返回錯(cuò)誤的信息,讓用戶決定如何去做。在對(duì)數(shù)據(jù)庫(kù)進(jìn)行處理的時(shí)候,樂(lè)觀鎖并不會(huì)使用數(shù)據(jù)庫(kù)提供的鎖機(jī)制。一般的實(shí)現(xiàn)樂(lè)觀鎖的方式就是記錄數(shù)據(jù)版本。數(shù)據(jù)版本,就是為數(shù)據(jù)增加的一個(gè)版本標(biāo)識(shí)。當(dāng)讀取數(shù)據(jù)時(shí),將版本標(biāo)識(shí)的值一同讀出,數(shù)據(jù)每更新一次,同時(shí)對(duì)版本標(biāo)識(shí)進(jìn)行更新。當(dāng)我們提交更新的時(shí)候,判斷數(shù)據(jù)庫(kù)表對(duì)應(yīng)記錄的當(dāng)前版本信息與第一次取出來(lái)的版本標(biāo)識(shí)進(jìn)行比對(duì),如果數(shù)據(jù)庫(kù)表當(dāng)前版本號(hào)與第一次取出來(lái)的版本標(biāo)識(shí)值相等,則予以更新,否則認(rèn)為是過(guò)期數(shù)據(jù)。實(shí)現(xiàn)數(shù)據(jù)版本有兩種方式,第一種是使用版本號(hào),第二種是使用時(shí)間戳。使用版本號(hào)時(shí),可以在數(shù)據(jù)初始化時(shí)指定一個(gè)版本號(hào),每次對(duì)數(shù)據(jù)的更新操作都對(duì)版本號(hào)執(zhí)行+1操作,并判斷當(dāng)前版本號(hào)是不是該數(shù)據(jù)的最新的版本號(hào)。

TA貢獻(xiàn)1839條經(jīng)驗(yàn) 獲得超15個(gè)贊
Memcached是全內(nèi)存的數(shù)據(jù)緩沖系統(tǒng),Redis雖然支持?jǐn)?shù)據(jù)的持久化,但是全內(nèi)存畢竟才是其高性能的本質(zhì)。
作為基于內(nèi)存的存儲(chǔ)系統(tǒng)來(lái)說(shuō),機(jī)器物理內(nèi)存的大小就是系統(tǒng)能夠容納的最大數(shù)據(jù)量。
如果需要處理的數(shù)據(jù)量超過(guò)了單臺(tái)機(jī)器的物理內(nèi)存大小,就需要構(gòu)建分布式集群來(lái)擴(kuò)展存儲(chǔ)能力。
- 4 回答
- 0 關(guān)注
- 1319 瀏覽
添加回答
舉報(bào)