資料庫鎖機制

来源: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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...