elasticsearch入門系列">elasticsearch入門系列
1001
2022-05-30
前言
在電商秒殺等高并發(fā)場景中,僅僅開啟事務(wù)還是無法避免數(shù)據(jù)沖突。比如用戶A和用戶B獲取某一商品的庫存并嘗試對(duì)其修改,A, B查詢的商品庫存都為5件,結(jié)果A下單5件,B也下單5件,這就出現(xiàn)問題了。解決方案就是操作( 查詢或修改)某個(gè)商品庫存信息時(shí)對(duì)其加鎖。鎖有悲觀鎖和樂觀鎖。
1.悲觀鎖
總是假設(shè)最壞的情況,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)阻塞直到它拿到鎖(共享資源每次只給一個(gè)線程使用,其它線程阻塞,用完后再把資源轉(zhuǎn)讓給其它線程)。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里邊就用到了很多這種鎖機(jī)制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
2.樂觀鎖
總是假設(shè)最好的情況,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒有去更新這個(gè)數(shù)據(jù),可以使用版本號(hào)機(jī)制和CAS算法實(shí)現(xiàn)。樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量,像數(shù)據(jù)庫提供的類似于write_condition機(jī)制,其實(shí)都是提供的樂觀鎖。
一、Django中的悲觀鎖
Django中使用悲觀鎖鎖定一個(gè)對(duì)象,需要使用select_for_update()方法。它本質(zhì)是一個(gè)行級(jí)鎖,能鎖定所有匹配的行,直到事務(wù)結(jié)束。
1.悲觀鎖案例
# 案例1:類視圖,鎖定id=10的SKU對(duì)象 class OrderView(APIView): @transaction.atomic def post(self, request): # select_for_update表示鎖,只有獲取到鎖才會(huì)執(zhí)行查詢,否則阻塞等待。 sku = GoodsSKU.objects.select_for_update().get(id=10) # 等事務(wù)提交后,會(huì)自動(dòng)釋放鎖。 ...... return Response("xxx")
# 案例2:函數(shù)視圖,鎖定所有符合條件的文章對(duì)象列表。 from django.db import transaction with transaction.atomic(): entries = Entry.objects.select_for_update().filter(author=request.user) for entry in entries: ...
一般情況下如果其他事務(wù)鎖定了相關(guān)行,那么本次查詢將被阻塞,直到鎖被釋放。如果不想要使查詢阻塞的話,使用 select_for_update(nowait=True)。
注意點(diǎn):
select_for_update方法必須與事務(wù)(transaction)同時(shí)使用。
MySQL版本要在8.0.1+ 以上才支持 nowait和 of選項(xiàng)。
2.關(guān)聯(lián)對(duì)象鎖定
當(dāng)你同時(shí)使用select_for_update與select_related方法時(shí),select_related指定的相關(guān)對(duì)象也會(huì)被鎖定。你可以通過select_for_update(of=(…))方法指定需要鎖定的關(guān)聯(lián)對(duì)象。
二、Django中的樂觀鎖
Django項(xiàng)目中實(shí)現(xiàn)樂觀鎖可以借助于django-concurrency這個(gè)第三方庫, 它可以給模型增加一個(gè)version字段,每次執(zhí)行save操作時(shí)會(huì)自動(dòng)給版本號(hào)+1。
from django.db import models from concurrency.fields import IntegerVersionField class ConcurrentModel( models.Model ): version = IntegerVersionField( ) name = models.CharField(max_length=100)
下例中a和b同時(shí)獲取了pk=1的模型對(duì)象信息,并嘗試對(duì)其name字段進(jìn)行修改。由于a.save()方法調(diào)用成功以后對(duì)象的版本號(hào)version已經(jīng)加1,b再調(diào)用b.save()方法時(shí)將會(huì)報(bào)RecordModifiedError的錯(cuò)誤,這樣避免了a,b同時(shí)修改同一對(duì)象信息造成數(shù)據(jù)沖突。
a = ConcurrentModel.objects.get(pk=1) a.name = '1' b = ConcurrentModel.objects.get(pk=1) b.name = '2' a.save() b.save()
總結(jié)
悲觀鎖:適用于多寫的場景
樂觀鎖:適用于多讀場景
5G教育 Django Python
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。