資料庫鎖機制

来源:http://www.cnblogs.com/houan/archive/2017/01/05/6253293.html
-Advertisement-
Play Games

一、為什麼需要瞭解鎖 1.1 死鎖問題 1.2 併發問題導致的不正確數據的讀取和存儲,破壞數據一致性的 丟失更新:當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,就會發生丟失更新問題--最後的更新覆蓋了由其他事務所做的更新。例如,兩個編輯人員製作了同一 ...


一、為什麼需要瞭解鎖

1.1 死鎖問題

1.2 併發問題導致的不正確數據的讀取和存儲,破壞數據一致性的

  • 丟失更新:當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,就會發生丟失更新問題--最後的更新覆蓋了由其他事務所做的更新。例如,兩個編輯人員製作了同一文檔的電子副本。每個編輯人員獨立地更改其副本,然後保存更改後的副本,這樣就覆蓋了原始文檔。最後保存其更改副本的編輯人員覆蓋另一個編輯人員所做的更改。如果在一個編輯人員完成並提交事務之前,另一個編輯人員不能訪問同一文件,則可避免此問題
  • 臟讀:一個事務正在對一條記錄做修改,在這個事務完成並提交前,這條記錄的數據就處於不一致狀態;這時,另一個事務也來讀取同一條記錄,如果不加控制,第二個事務讀取了這些“臟”數據,並據此做進一步的處理,就會產生未提交的數據依賴關係。這種現象被形象地叫做”臟讀”。
  • 不可重覆讀:一個事務在讀取某些數據後的某個時間,再次讀取以前讀過的數據,卻發現其讀出的數據已經發生了改變、或某些記錄已經被刪除了!這種現象就叫做“不可重覆讀”。
  • 幻讀:一個事務按相同的查詢條件重新讀取以前檢索過的數據,卻發現其他事務插入了滿足其查詢條件的新數據,這種現象就稱為“幻讀”。

二、鎖的分類

2.1 資料庫維度

  • 共用鎖:用於不更改或不更新數據的操作(只讀操作)。共用鎖允許併發事務讀取同一個資源,數據資源上存在共用鎖時,任何其他事務不允許修改數據
  • 排它鎖: 用於數據修改,確保不會同時多重更新同一數據。資源上存在排他鎖時,其他任何事務不允許給資源上鎖,當資源上有其他鎖時,也無法對其加上排它鎖

PS:只有共用鎖與共用鎖相互相容,共用鎖與排它鎖、排它鎖之間都互不相容

  • 更新鎖 
  1. 用於可更新的資源中,防止多個會話在讀取、鎖定以及隨後可能進行的資源更新時發生的死鎖問題。
  2. 通常形式的死鎖:一般一個更新模式由一個事務組成,此事務讀取記錄,獲取資源的共用鎖,然後修改行,此操作要求鎖轉換為排它鎖。如果兩個事務同時獲得了資源上的共用鎖,然後試圖同時更新數據,則一個事務嘗試將鎖轉換為排它鎖。由於一個事務的排它鎖與另一事務的共用鎖的不相容,從共用鎖到排它鎖的轉換必須要等待一段時間,發送鎖等待。而第二個事務同時也試圖獲取排它鎖進行更新。由於兩個事務都要轉化為排它鎖,並且每個事務都要等待另一個事務釋放掉共用鎖,因此發生死鎖
  3. 更新鎖一次只有一個事務可以獲取資源的更新鎖。如果事務修改資源,那麼更新鎖轉化為排它鎖,否則轉化為共用鎖。當資源上存在更新鎖時,允許資源被讀取(即更新鎖與共用鎖相容),但不允許資源被修改
  4. 一般來說,在執行UPDATE操作時,SQL SERVER會使用到更新鎖而不是依次加上共用鎖和排它鎖,已經迴避了這種通常形式的死鎖,更新鎖與意向鎖相互相容

2.2 資料庫鎖機制

DBMS SELECT UPDATE INSERT DELETE
MySQL(InnoDB) 不加鎖 排它鎖 排它鎖 排它鎖
SQL SERVER 共用鎖 更新鎖  排它鎖 排它鎖

 

 

 

 

 這兩種鎖機制的區別在於MySQL的查詢與更新操作互相不阻塞;而SQL SERVER的更新鎖轉化成排它鎖之前,其查詢與更新操作互相不阻塞,當更新鎖要轉化為排它鎖時,需要等待共用鎖的釋放,當更新鎖轉化為排它鎖後,查詢數據需要等待排它鎖的釋放。

參考:
[資料庫鎖機制](http://blog.csdn.net/samjustin1/article/details/52210125)
[InnoDB鎖機制](http://blog.chinaunix.net/uid-24111901-id-2627857.html)
[SQL SERVER鎖機制](http://blog.itpub.net/13651903/viewspace-1091664/)

2.3 程式員思想維度

  • 悲觀鎖
  1. 悲觀併發控制(又名“悲觀鎖”,Pessimistic Concurrency Control,縮寫“PCC”)是一種併發控制的方法。它可以阻止一個事務以影響其他用戶的方式來修改數據。如果一個事務執行的操作都某行數據應用了鎖,那隻有當這個事務把鎖釋放,其他事務才能夠執行與該鎖衝突的操作。悲觀鎖的實現,往往依靠資料庫提供的鎖機制。悲觀併發控制主要用於數據爭用激烈的環境,以及發生併發衝突時使用鎖保護數據的成本要低於回滾事務的成本的環境中。
  2.  悲觀併發控制實際上是“先取鎖再訪問”的保守策略,為數據處理的安全提供了保證。但是在效率方面,處理加鎖的機制會讓資料庫產生額外的開銷,還有增加產生死鎖的機會;另外,在只讀型事務處理中由於不會產生衝突,也沒必要使用鎖,這樣做只能增加系統負載;還有會降低了並行性,一個事務如果鎖定了某行數據,其他事務就必須等待該事務處理完才可以處理那行數
  •  樂觀鎖
  1. 樂觀併發控制(又名“樂觀鎖”,Optimistic Concurrency Control,縮寫“OCC”)是一種併發控制的方法。它假設多用戶併發的事務在處理時不會彼此互相影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分數據。在提交數據更新之前,每個事務會先檢查在該事務讀取數據後,有沒有其他事務又修改了該數據。如果其他事務有更新的話,正在提交的事務會進行回滾。
  2. 樂觀併發控制相信事務之間的數據競爭(data race)的概率是比較小的,因此儘可能直接做下去,直到提交的時候才去鎖定,所以不會主動產生任何鎖和死鎖。但是在併發量高的情況下,可能導致某次數據修改多次重試,影響單次成功操作的時間。
  3. 數據版本實現樂觀鎖:實現數據版本有兩種方式,第一種是使用版本號,第二種是使用時間戳。使用版本號時,可以在數據初始化時指定一個版本號,每次對數據的更新操作都對版本號執行+1操作。並判斷當前版本號是不是該數據的最新的版本號。
update table 
set date=1,version=version+1
where id=#{id} and version=#{version};

參考:
[樂觀鎖與悲觀鎖](http://www.open-open.com/lib/view/open1452046967245.html)

2.4 樂觀鎖另一種實現方式CAS

CAS是項樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變數時,只有其中一個線程能更新變數的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。

CAS 操作包含三個操作數 —— 記憶體位置(V)、預期原值(A)和新值(B)。如果記憶體位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新為新值。否則,處理器不做任何操作。無論哪種情況,它都會在 CAS 指令之前返回該位置的值。CAS 有效地說明瞭“我認為位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。”這其實和樂觀鎖的衝突檢查+數據更新的原理是一樣的。

java.util.concurrent(J.U.C)就是建立在CAS之上的。相對於對於synchronized這種阻塞演算法,CAS是非阻塞演算法的一種常見實現。所以J.U.C在性能上有了很大的提升。

public class AtomicInteger extends Number implements java.io.Serializable {
  private volatile int value; 

  public final int get() { 
    return value; 
  } 

  public final int getAndIncrement() { 
    for (;;) { 
      int current = get(); 
      int next = current + 1; 
      if (compareAndSet(current, next)) 
        return current; 
    } 
  } 

  public final boolean compareAndSet(int expect, int update) { 
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 
  } 
}

 

參考:
[樂觀鎖的一種實現方式—CAS](http://www.importnew.com/20472.html)

三、案例分析

3.1 初審統計數據遷移

  • 遷移背景:原有的統計方式採用的是實時count的方法獲取統計數據,造成的問題是查詢慢且無法獲取長時間段的統計數據(sql超時)、無法獲取某日統計數據的快照(前一天的待審核數據會變成今天的審核通過數據)
  • 採用遷移方式:使用raptor遷移平臺,掃描審核記錄表,取出累計統計數據後進行加1操作,然後更新到統計表中。由於平臺特性,數據遷移過程具有高併發性,由於強行採用先讀取後更新的方式,會造成丟失更新的情況,於是這裡考慮採用CAS

step1:

 

select id,passCount,rejectCount,hideCount,warnCount,waitCount 
from book.TradeItemAuditCount 
where type = #{type} and date = #{date} and editor = #{editor} 
and isDeleted = 0 limit 1

step 2:【失敗重試】

update book.TradeItemAuditCount 
set passCount = #{passCount} , rejectCount = #{rejectCount} , hideCount = #{hideCount} , warnCount = #{warnCount} , waitCount = #{waitCount} , updated = #{updated}
where id = #{id} and passCount = #{oldPassCount} and rejectCount = #{oldRejectCount} and hideCount = #{oldHideCount} and warnCount = #{oldWarnCount} and waitCount = #{oldWaitCount}
and isDeleted = 0 limit 1
  •  處理結果 :一共掃描審核結果4335668條數據,對重試次數超過100的更新操作進行記錄,發現更新操作出現大部分的重試,任務本身DB寫操作的qps較低【都不需要通過控制台限制速率..】

3.2 商品庫存

  • 商品庫存與上述案例1一致,都是對數據記錄進行加減操作,發現庫存的更新方式如下:
update 庫存表
set stock=stock-1
where id=#{id}
  • 直接使用資料庫的排它鎖就簡單的避免了併發導致的丟失更新問題,之前提到的一次只有一個事務擁有資源的排它鎖,併發的更新操作都試圖占有資源的排它鎖,當資源上存在排它鎖時,其他更新操作需要等待鎖的釋放
  • 相比案例1的解決方案,案例2的解決方式直接使用了MySQL InnoDB更新操作本身就擁有的排它鎖,不需要額外的開銷,而案例1不必要的查詢操作以及多次的重試操作嚴重影響到了數據遷移的性能,所以案例1是反面例子..

3.3 商品打標

  • 隨著上打標的qps上漲,出現達標更新數據丟失的情況
{"tags":"16,32,233,22","itemState":1,"hd":"ai:4|nd:18","au":"baoming"}
  • 處理方案

  1.樂觀鎖:採用CAS

update TradeItem
set extra=#{extra}
where tradeItemId=#{tradeItemId} and extra=#{oldExtra}

這裡使用長字元串做更新條件,會影響到SQL性能

  2.樂觀鎖:採用數據版本 表中新增version欄位標識數據版本,作為數據更新的檢查方式

update TradeItem
set extra=#{extra} , version=version+1
where tradeItemId=#{tradeItemId} and version=#{version}

此方案改造較大,還需要為表新增欄位,而且採用樂觀鎖擁有這一律的弊端:重試帶來的時間代價,一旦併發量上漲,某次更新操作的重試次數也會隨之上漲,直接影響到暴露服務的響應時間。【限制重試次數能夠一定程度上控制更新操作的響應時間,但是仍然會出現更新丟失的現象(讓調用方進行重試操作,分攤單次請求的響應時間?)】  

  3. 悲觀鎖:更新丟失的根本原因是執行查詢、修改兩個操作之間數據被另一事務修改了,單純的UPDATE操作其實也是進行著先查詢後修改的操作,沒有產生更新丟失是因為數據上存在排它鎖(sql server則是更新鎖),在執行期間並不允許其他修改。同理我們將要打標的商品記錄加上排它鎖或者更新鎖就能解決問題。 

MySQL:

start transaction;
SELECT extra
FROM TradeItem 
WHERE tradeItemId=#{tradeItemId}
FOR UPDATE;
UPDATE TradeItem 
SET extra = bdo.AddTag(tag,extra)
WHERE tradeItemId=#{tradeItemId};
commit;

SQL SERVER:

BEGIN TRANSACTION --開始一個事務
SELECT extra
FROM TradeItem WITH (UPDLOCK)
WHERE tradeItemId=#{tradeItemId}
UPDATE TradeItem 
SET extra = bdo.AddTag(tag,extra)
WHERE tradeItemId=#{tradeItemId}
COMMIT TRANSACTION --提交事務

該方案避免了重試帶來的開銷,同時使用排它鎖(更新鎖)也沒有額外增加鎖的開銷

四、悲觀鎖樂觀鎖的取捨


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 嗯,遇見了表中存在重覆的記錄的問題,直接寫sql刪除時最快的,才不要慢慢的複製到excel表中慢慢的人工找呢。哼。 如下sql,找出重覆的記錄,和重覆記錄中ID值最小的記錄(表中ID為自增長) 然後就可以直接刪除,基本原理就是,找到重覆記錄的每一條記錄,排除掉重覆id最小的記錄,刪除剩餘的重覆記錄。 ...
  • sqlcmd -s DESKTOP-Q3VQ7U1 -U sa -P 123456 -d db_test -r -i G:\test.sql 黑色字體為關鍵命令,其他顏色(從左至右):伺服器名稱,用戶名,密碼,資料庫,文件路徑 通過select @@servername獲取服務名稱:DESKTOP- ...
  • --允許將顯示值插入表的標識列中-ON:允許 OFF:不允許set identity_insert T_shell ONset identity_insert T_Shell OFF ...
  • 一、負責收集數據的工具:Sqoop(關係型數據導入Hadoop)Flume(日誌數據導入Hadoop,支持數據源廣泛)Kafka(支持數據源有限,但吞吐大) 二、負責存儲數據的工具:HBaseMongoDBCassandraAccumulo MySqlOracleDB2 HDFS(Hadoop Di ...
  • 一、 表空間 Oracle資料庫包含邏輯結構和物理結構。 資料庫的物理結構指的是構成資料庫的一組操作系統文件。 資料庫的邏輯結構是指描述數據組織方式的一組邏輯概念以及它們之間的關係。 表空間是資料庫邏輯結構的一個重要組件。 表空間可以存放各種應用對象,如表、索引等。 而每一個表空間由一個或多個數據文 ...
  • pt-table-checksum是percona公司提供的一個用於線上比對主從數據一致性的工具。 實現原理 將一張大表分成多個chunk,每次針對一個chunk進行校驗,同時將校驗的結果通過REPLACE INTO語句寫入到percona.checksums表中,然後該語句通過主從複製,在SLAV ...
  • 記憶體優化表(Memory-Optimized Table,簡稱MOT)使用樂觀策略(optimistic approach)實現事務的併發控制,在讀取MOT時,使用多行版本化(Multi-Row versioning)創建數據快照,讀操作不會對數據加鎖,因此,讀寫操作不會相互阻塞。寫操作會申請行級鎖 ...
  • 因為長時間沒有使用資料庫了,或者把密碼改完之後就忘了資料庫密碼,不能正常進入資料庫,也無法修改密碼,有一個簡單的常用修改密碼方式: 1.首先找到和打開mysql.exe和mysqld.exe所在的文件夾(在你所安裝的Mysql的bin文件夾下),複製路徑地址。 2.Windows+R打開cmd命令提 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...