MySQL 事務隔離 | 樂觀鎖

MySQL 事務隔離級別詳解

數據庫事務具備四個特徵,簡稱爲事務的ACID特性mysql

原子性(Atomicity)、
一致性(Consistency)、
隔離性(Isoation)、
持久性(Durability),

什麼是事務隔離

事務的隔離性是指在併發環境中,併發的事務是相互隔離的,
能夠理解爲多個事務同一時間段對數據庫的增刪改時是要隔離的

 隔離種類

SQL標準中定義了四種數據庫事務隔離級別,級別從低到高分別爲:sql

讀未提交(Read Uncommitted)、-> 髒讀
讀已提交(Read Committed)、 -> 不可重複讀
可重複讀(Repeatable Read)、-> 換讀
串行化(Serializable)。
在事務的併發操做中會出現髒讀、不可重複讀、幻讀。在事務的併發操做中第二類更新丟失能夠經過樂觀鎖和悲觀鎖解決。

 

 

讀未提交(Read Uncommitted)

  • 該隔離級別,全部事務均可以看到其餘未提交事務的執行結果。通俗地講就是,在一個事務中能夠讀取到另外一個事務中新增或修改但未提交的數據。
  • 該隔離級別可能致使的問題是髒讀。由於另外一個事務可能回滾,因此在第一個事務中讀取到的數據極可能是無效的髒數據,形成髒讀現象。
>: set tx_isolation='READ-UNCOMMITTED';

 

 eg:

髒讀(讀取未提交數據)

A事務讀取B事務還沒有提交的數據,此時若是B事務發生錯誤並執行回滾操做,那麼A事務讀取到的數據就是髒數據。就好像本來的數據比較乾淨、純粹,此時因爲B事務更改了它,這個數據變得再也不純粹。這個時候A事務當即讀取了這個髒數據,但事務B良心發現,又用回滾把數據恢復成原來乾淨、純粹的樣子,而事務A卻什麼都不知道,最終結果就是事務A讀取了這次的髒數據,稱爲髒讀。數據庫

這種狀況常發生於轉帳與取款操做中
django

 

 

讀已提交(Read Committed) 

  • 這是大多數數據庫系統的默認隔離級別(但不是mysql默認的)
  • 一個事務只能看見已經提交事務所作的修改。換句話說,一個事務從開始直到提交以前,所作的任何修改對其餘事務都是不可見的。
  • 該隔離級別可能致使的問題是不可重複讀。由於兩次執行一樣的查詢,可能會獲得不同的結果。
>: set tx_isolation='READ-COMMITTED';

 

 eg:

不可重複讀(先後屢次讀取,數據內容不一致)

事務A在執行讀取操做,由整個事務A比較大,先後讀取同一條數據須要經歷很長的時間 。而在事務A第一次讀取數據,好比此時讀取了小明的年齡爲20歲,事務B執行更改操做,將小明的年齡更改成30歲,此時事務A第二次讀取到小明的年齡時,發現其年齡是30歲,和以前的數據不同了,也就是數據不重複了,系統不能夠讀取到重複的數據,成爲不可重複讀。
併發

 

 

可重複讀(Repeatable Read)

  • 這是MySQL的默認事務隔離級別
  • 它確保同一事務的多個實例在併發讀取數據時,會看到一樣的數據行。通俗來說,可重複讀在一個事務裏讀取數據,怎麼讀都不會變,除非提交了該事務,再次進行讀取。
  • 該隔離級別存在的問題是幻讀
>: set tx_isolation='REPEATABLE-READ';

 

eg:

幻讀(先後屢次讀取,數據總量不一致)

事務A在執行讀取操做,須要兩次統計數據的總量,前一次查詢數據總量後,此時事務B執行了新增數據的操做並提交後,這個時候事務A讀取的數據總量和以前統計的不同,就像產生了幻覺同樣,無緣無故的多了幾條數據,成爲幻讀。post

 

 

串行化(Serializable)

  • 這是最高的隔離級別
  • 它經過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。通俗地講就是,假如兩個事務都操做到同一數據行,那麼這個數據行就會被鎖定,只容許先讀取/操做到數據行的事務優先操做,只有當事務提交了,數據行纔會解鎖,後一個事務才能成功操做這個數據行,不然只能一直等待
  • 該隔離級別可能致使大量的超時現象和鎖競爭。
>: set tx_isolation='SERIALIZABLE';

 

不可重複讀和幻讀到底有什麼區別呢?

(1)不可重複讀是讀取了其餘事務更改的數據,針對insert與update操做atom

解決:使用行級鎖,鎖定該行,事務A屢次讀取操做完成後才釋放該鎖,這個時候才容許其餘事務更改剛纔的數據。spa

(2)幻讀是讀取了其餘事務新增的數據,針對insert與delete操做調試

解決:使用表級鎖,鎖定整張表,事務A屢次讀取數據總量以後才釋放該鎖,這個時候才容許其餘事務新增數據。

rest

樂觀鎖

    在更新語句中增長過濾條件,進行版本的判斷,能夠是這條記錄的某個字段值。而後經過影響行數來判斷是否更新成功。影響條數爲0則更新失敗,表明這條記錄被其餘事務修改了.

eg: 事務+樂觀鎖的使用

# 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分爲表級鎖和行級鎖,共享鎖和排它鎖是行級鎖。表級鎖在此不作討論。

相關文章
相關標籤/搜索