數據庫事務具備四個特徵,簡稱爲事務的ACID特性mysql
原子性(Atomicity)、
一致性(Consistency)、
隔離性(Isoation)、
持久性(Durability),
事務的隔離性是指在併發環境中,併發的事務是相互隔離的,
能夠理解爲多個事務同一時間段對數據庫的增刪改時是要隔離的
SQL標準中定義了四種數據庫事務隔離級別,級別從低到高分別爲:sql
讀未提交(Read Uncommitted)、-> 髒讀
讀已提交(Read Committed)、 -> 不可重複讀
可重複讀(Repeatable Read)、-> 換讀
串行化(Serializable)。
在事務的併發操做中會出現髒讀、不可重複讀、幻讀。在事務的併發操做中第二類更新丟失能夠經過樂觀鎖和悲觀鎖解決。
>: set tx_isolation='READ-UNCOMMITTED';
A事務讀取B事務還沒有提交的數據,此時若是B事務發生錯誤並執行回滾操做,那麼A事務讀取到的數據就是髒數據。就好像本來的數據比較乾淨、純粹,此時因爲B事務更改了它,這個數據變得再也不純粹。這個時候A事務當即讀取了這個髒數據,但事務B良心發現,又用回滾把數據恢復成原來乾淨、純粹的樣子,而事務A卻什麼都不知道,最終結果就是事務A讀取了這次的髒數據,稱爲髒讀。數據庫
這種狀況常發生於轉帳與取款操做中django
>: set tx_isolation='READ-COMMITTED';
事務A在執行讀取操做,由整個事務A比較大,先後讀取同一條數據須要經歷很長的時間 。而在事務A第一次讀取數據,好比此時讀取了小明的年齡爲20歲,事務B執行更改操做,將小明的年齡更改成30歲,此時事務A第二次讀取到小明的年齡時,發現其年齡是30歲,和以前的數據不同了,也就是數據不重複了,系統不能夠讀取到重複的數據,成爲不可重複讀。併發
>: set tx_isolation='REPEATABLE-READ';
事務A在執行讀取操做,須要兩次統計數據的總量,前一次查詢數據總量後,此時事務B執行了新增數據的操做並提交後,這個時候事務A讀取的數據總量和以前統計的不同,就像產生了幻覺同樣,無緣無故的多了幾條數據,成爲幻讀。post
>: set tx_isolation='SERIALIZABLE';
(1)不可重複讀是讀取了其餘事務更改的數據,針對insert與update操做atom
解決:使用行級鎖,鎖定該行,事務A屢次讀取操做完成後才釋放該鎖,這個時候才容許其餘事務更改剛纔的數據。spa
(2)幻讀是讀取了其餘事務新增的數據,針對insert與delete操做調試
解決:使用表級鎖,鎖定整張表,事務A屢次讀取數據總量以後才釋放該鎖,這個時候才容許其餘事務新增數據。
rest
在更新語句中增長過濾條件,進行版本的判斷,能夠是這條記錄的某個字段值。而後經過影響行數來判斷是否更新成功。影響條數爲0則更新失敗,表明這條記錄被其餘事務修改了.
# django中使用 事務 + 樂觀鎖 # 這是一個商城訂單提交要修改數據庫案例 from rest_framework.views import APIView from rest_framework.response import Response from django.db import transaction # 導入事務 class Creat(APIView): @transaction.atomic # 裝飾事務 def post(self,request): ...... sid=transaction.savepoint() # 對下面代碼建立事務 for product in all_product: product.product_id=str(product.product_id) order_data['order_total']+=product.price*buy_list[product.product_id] order_data['quantity']+=buy_list[product.product_id] #建立子訂單 for i in range(3): stock=product.stock.quantity # 商品在庫中的數量 new_stock=stock-buy_list[product.product_id] # 減掉用戶購買數量 if new_stock<0: # 庫存數小於用戶購買數量 transaction.savepoint_rollback(sid) # 回滾 return Response({"code":203,"msg":f"{product.name}庫存不足"}) # 使用樂觀鎖: quantity=字段, 在執行update前,鎖定該字段的是否沒有被改動,若改動了,則update影響調試爲0 res=models.Stock.objects.filter(quantity=stock,stock_id=product.stock.stock_id).update(quantity=new_stock) if not res: if i==2: transaction.savepoint_rollback(sid) # 回滾 return Response({"code": 203, "msg": f"建立訂單失敗"}) else: continue else: break
悲觀鎖分爲共享鎖和排它鎖。
共享鎖又稱爲讀鎖,簡稱S鎖,顧名思義,共享鎖就是多個事務對於同一數據能夠共享一把鎖,共享鎖是用來讀取數據的。另外,一個事務獲取了同一數據的共享鎖,其餘事務就不能獲取該數據的排它鎖。
排它鎖又稱爲寫鎖,簡稱X鎖,顧名思義,排它鎖就是不能與其餘所並存,如一個事務獲取了一個數據行的排它鎖,其餘事務就不能再獲取該行的其它鎖,包括共享鎖和排它鎖。另外不存什麼事務隔離級別,update/insert/delete會自動獲取排它鎖
共享鎖獲取方式:select * from account where name='jack' lock in share mode;
排它鎖獲取方式:select * from account where name='jack' for update;
MySQL分爲表級鎖和行級鎖,共享鎖和排它鎖是行級鎖。表級鎖在此不作討論。