Transaction數據庫
事務:一個最小的不可再分的工做單元;一般一個事務對應一個完整的業務(例如銀行帳戶轉帳業務,該業務就是一個最小的工做單元)django
一個完整的業務須要批量的DML(insert、update、delete)語句共同聯合完成併發
事務只和DML語句 ( 數據庫操做語句 ) 有關,或者說DML語句纔有事務。這個和業務邏輯有關,業務邏輯不一樣,DML語句的個數不一樣高併發
▧ 原子性(A) 事務是最小單位,不可再分post
▧ 一致性(C) 事務要求全部的DML語句操做的時候,必須保證同時成功或者同時失敗atom
▧ 隔離性(I) 事務A和事務B之間具備隔離性spa
▧ 持久性(D) 是事務的保證,事務終結的標誌(內存的數據持久到硬盤文件中)線程
▧ 開啓事務 Start Transactioncode
▧ 事務結束 End Transactionblog
▧ 提交事務 Commit Transaction
▧ 回滾事務 Rollback Transaction
任何一條DML語句(insert、update、delete)執行,標誌事務的開啓
▧ 提交 成功的結束,將全部的DML語句操做歷史記錄和底層硬盤數據來一次同步
▧ 回滾 失敗的結束,將全部的DML語句操做歷史記錄所有清空
Django 自帶的代碼庫
from django.db import transaction
方式一, 直接使用將一段操做設置爲事務
with transaction.atomic():
...
方式二, 裝飾器方式
@transaction.atomic def foo(): ....
老是假設最壞的狀況,每次取數據時都認爲其餘線程會修改,因此都會加鎖(讀鎖、寫鎖、行鎖等)
當其餘線程想要訪問數據時,都須要阻塞掛起。能夠依靠數據庫實現,如行鎖、讀鎖和寫鎖等,都是在操做以前加鎖
保證同一時刻只有一個線程能操做數據,其餘線程則會被 block
▧ 無髒讀 上鎖數據保證一致, 所以無髒讀, 對髒讀不容許的環境悲觀鎖能夠勝任
▧ 無並行 悲觀鎖對事務成功性能夠保證, 可是會對數據加鎖致使沒法實現數據的並行處理.
▧ 事務成功率高 上鎖保證一次成功, 所以在對數據處理的成功率要求較高的時候更適合悲觀鎖.
▧ 開銷大 悲觀鎖的上鎖解鎖是有開銷的, 若是超大的併發量這個開銷就不容小視, 所以不適合在高併發環境中使用悲觀鎖
▧ 一次性完成 若是樂觀鎖屢次嘗試的代價比較大,也建議使用悲觀鎖, 悲觀鎖保證一次成功
from django.shortcuts import render from django.http import HttpResponse from django.views.generic import View from django.db import transaction from 應用名.models import 模型類名 # 類視圖 (併發,悲觀鎖) class MyView(View): @transaction.atomic def post(self, request): # select * from 表名 where id=1 for update; # for update 就表示鎖,只有獲取到鎖纔會執行查詢,不然阻塞等待。 obj = 模型類名.objects.select_for_update().get(id=1) # 等事務提交後,會自動釋放鎖。 return HttpResponse('ok')
老是認爲不會產生併發問題,每次去取數據的時候總認爲不會有其餘線程對數據進行修改,所以不會上鎖
可是在更新時會判斷其餘線程在這以前有沒有對數據進行修改,通常會使用版本號機制或CAS操做實現。
若是發現數據被改了. 就進行事務回滾取消以前的操做
▧ 髒讀 樂觀鎖不涉及到上鎖的處理, 所以在數據並行需求的時候是更適合樂觀鎖,固然會產生髒讀, 不過用回滾取消掉了.
▧ 高併發 相比起悲觀鎖的開銷, 樂觀鎖也是比悲觀鎖更適合於高併發場景
▧ 事務成功率低 樂觀鎖不能保證每次事務的成功, 是使用回滾方式來保證數據一致性, 所以會致使事務成功率很低.
▧ 讀多寫少 樂觀鎖適用於讀多寫少的應用場景,這樣能夠提升併發粒度
▧ 開銷小 可能會致使不少次的回滾都不能拿到正確的處理迴應, 所以若是對成功性要求低,並且每次開銷小比較適合樂觀鎖
from django.shortcuts import render from django.http import JsonResponse from django.views.generic import View from django.db import transaction from 應用名.models import GoodsSKU # 類視圖 (併發,樂觀鎖) class MyView(View): @transaction.atomic def post(self, request): '''訂單建立''' count = 3 # 訂購3件商品 # 設置事務保存點 s1 = transaction.savepoint() # 樂觀鎖,最多嘗試5次 for i in range(5): # 查詢商品的信息(庫存) try: sku = GoodsSKU.objects.get(id=1) except: # 商品不存在 transaction.savepoint_rollback(s1) return JsonResponse({'res': 1, 'errmsg': '商品不存在'}) # 判斷商品的庫存 if count > sku.stock: transaction.savepoint_rollback(s1) return JsonResponse({'res': 2, 'errmsg': '商品庫存不足'}) # 更新商品的庫存和銷量 orgin_stock = sku.stock # 原庫存 (數據庫隔離級別必須是Read Committed;若是是Repeatable Read,那麼屢次嘗試讀取的原庫存都是同樣的,讀不到其餘線程提交更新後的數據。) new_stock = orgin_stock - count # 更新後的庫存 new_sales = sku.sales + count # 更新後的銷量 # update 商品表 set stock=new_stock, sales=new_sales where id=1 and stock = orgin_stock # 經過where子句中的條件判斷庫存是否進行了修改。(併發,樂觀鎖) # 返回受影響的行數 res = GoodsSKU.objects.filter(id=1, stock=orgin_stock).update(stock=new_stock, sales=new_sales) if res == 0: # 若是修改失敗 if i == 4: # 若是嘗試5次都失敗 transaction.savepoint_rollback(s1) return JsonResponse({'res': 3, 'errmsg': '下單失敗'}) continue # 再次嘗試 # 不然更新成功 # 跳出嘗試循環 break # 提交事務 transaction.savepoint_commit(s1) # 返回應答 return JsonResponse({'res': 4, 'message': '建立成功'})