1. 關於鎖
1.1 樂觀鎖
樂觀鎖的出發點是,同一條數據很少會因為並發修改而產生沖突,適用於讀多寫少的場景,用以提高吞吐量。
實現方式,讀取一個字段,執行處理邏輯,當需要更新數據時,再次檢查該字段是否和第一次讀取一致。如果一致,則更新數據,否則拒絕更新,重新讀取后再提交。
1.2 悲觀鎖
悲觀鎖的出發點是,當一條數據正在被修改時,不允許其他任何關於這條數據的操作。
實現方式,讀取一個字段之后,加鎖,不允許其他任何讀、寫操作。執行處理邏輯,更新數據完畢后,釋放鎖。
1.3 比較
樂觀鎖的開銷遠低於悲觀鎖。
悲觀鎖可能會導致死鎖。當 A 鎖定了 a 資源,需要 b 資源。而 b 被 B 鎖定,正在等待 a 資源。此時,導致出現死鎖。但是,可以通過設置超時來處理這個問題。
悲觀鎖可以有效降低沖突后,重試的次數。
樂觀鎖可以提高響應速度。
2. Django 中的事務
Django 默認每條數據庫操作都會被立即提交到數據庫。
這樣會導致一個問題,如果有一系列的數據庫操作構成,要么全部執行,要么就全部都不執行,怎么辦?
這時,就需要事務。將一系列數據庫操作設置為一個事務,提交給數據庫執行。
Django 提供 atomic 裝飾器以開啟事務。 atomic 使用一個參數來指定數據庫的名字。如果不設置值,Django 就會使用系統默認的數據庫。
2.1 整個 View 函數開啟事務
from django.db import transaction @transaction.atomic def viewfunc(request): # This code executes inside a transaction. do_stuff()
2.2 部分函數 do_more_stuff()
開啟事務。
from django.db import transaction def viewfunc(request): # This code executes in autocommit mode (Django's default). do_stuff() with transaction.atomic(): # This code executes inside a transaction. do_more_stuff()
2.3 不要在事務中處理異常
from django.db import transaction def viewfunc(request): do_stuff() try: with transaction.commit_on_success(): do_more_stuff_1() # in transaction try: do_more_stuff_2() # not in transaction except: pass do_more_stuff_3() # in transaction except: pass
當退出原子塊時,Django 會查看它是否正常退出或者是否有異常來確定是否提交或者回滾。
如果你捕獲並處理原子塊中的異常,可以能會隱藏 Django 中發生問題的事實。這樣可能會造成非預期的行為。
3. 利用 F 函數更新運算
通常更新數據庫的操作,需要將對象讀取到內存。在內存中進行修改之后,再寫回數據庫。
在內存中的操作,如果存在同時操作的情況,會導致運算邏輯錯誤。
F() 函數的作用就是直接生成 SQL 語句,不必將需要更新的對象讀取到內存。避免了並發導致的數據不一致問題。
from django.db.models import F reporter = Reporters.objects.get(name='OICQ') reporter.stories_filed = F('stories_filed') + 1 reporter.save()
4. 利用 select_for_update 函數
select_for_update 使用的是悲觀鎖。
select for update 函數使用數據庫查詢語句,select ... for update
對數據庫進行操作。
這是數據庫層面的,解決並發取數據后再修改的問題方法。
def mark_as_readed(self, notification_id): # 讓s elect for update 和 update 語句發生在一個完整的事務里面 with transaction.commit_on_success(): # 使用select_for_update 來保證並發請求同時只有一個請求在處理,其他的請求 # 等待鎖釋放 notification = Notification.objects.select_for_update().get(pk=notification_id) # 沒有必要重復標記一個已經讀過的通知 if notication.has_readed: return notification.has_readed = True notification.save() # 在這里更新我們的計數器,嗯,我感覺好極了 self.update_unread_count(-1)