每次拿資料的時候都會擔心會被別人修改(疑心重很悲觀),所以每次在拿資料的時候都會上鎖。確保自己使用的過程中不會被別人訪問,自己使用完後再解鎖。
期間需要訪問該資料的都會等待。
每次拿資料的時候都完全不擔心會被別人修改(心態好很樂觀),所以每次在拿資料的時候都不會上鎖。
但是在更新資料的時候去判斷該期間是否被別人修改過(使用版本號等機制),期間該資料可以隨便被其他人讀取。
適合修改少,讀多,提高吞吐量,即使出現了少量的衝突,這樣也省去了大量的鎖的開銷。如果經常發生衝突(寫資料比較多的情況下),上層應用不不斷的retry,這樣反而降低了效能,對於這種情況使用悲觀鎖就更合適。
以常用的mysql innodb儲存引擎為例:
加入商品表items表中有乙個欄位status(1未下單,2已下單),下單前確保status=1。
假設有一件商品,其id為10000;
如果不使用鎖,那麼操作方法如下:
//查出商品狀態: select status from items where id=10000;
//根據商品資訊生成訂單:insert into orders(id,item_id) values(null,10000);/
/修改商品狀態為2:update items set status=2 where id=10000;
先一步修改為2了,不知道被修改了,下單2次,資料不一致。
查詢出資訊後就鎖定,直到修改完後再解鎖。
注:要使用悲觀鎖,我們必須關閉mysql自動提交屬性(預設autocommit模式)
set autocommit=0;(關閉)
//開始事務: begin;/begin work;/start transaction; (三者選一就可以)
同上。。。(select,insert,update)
//提交事務: commit;/commit work;
注:當我執行select status from items where id=10000 for update;後。
我在另外的事務中如果再次執行select status from items where id=10000 for update;
則第二個事務會一直等待第乙個事務的提交,此時第二個查詢處於阻塞的狀態,查詢不會。
使用select…for update會把資料給鎖住,需要注意鎖的級別:
mysql innodb預設row-level lock,mysql 只有明確地指定主鍵,才會執行row lock (只鎖住被選取的資料) ,
否則mysql 將會執行table lock 。除了主鍵外,使用索引也會影響資料庫的鎖定級別。
1.使用資料版本(version)記錄機制實現,即為資料增加乙個版本標識,一般是通過為資料庫表增加乙個數字型別的 「version」 欄位來實現。
當讀取資料時,將version欄位的值一同讀出,資料每更新一次,對此version值+1。
表當前版本號與第一次取出來的version值相等,則予以更新,否則認為是過期資料。用下面的一張圖來說明:
2.樂觀鎖控制的table中增加乙個字段,名稱無所謂,字段型別使用時間戳(timestamp), 和上面的version類似,也是在更新提交的時候檢查當前資料庫中資料的時間戳和自己更新前取到的時間戳進行對比,如果一致則ok,否則就是版本衝突。
以mysql innodb儲存引擎為例,還是拿之前的例子商品表items表中有乙個欄位status,status=1表示該商品未被下單,status=2表示該商品已經被下單,那麼我們對每個商品下單前必須確保此商品的status=1。
假設有一件商品,其id為10000;
下單操作包括3步驟:
select (status,version) from items where id=#
根據商品資訊生成訂單
update items set status=2,version=version+1 where id=# and version=#;
為了使用樂觀鎖,我們需要首先修改items表,增加乙個version欄位,資料預設version可設為1;
很多產品都有樂觀鎖的使用,比如分布式儲存引擎tair,tair中儲存的每個資料都有版本號,更新後都會遞增,相應的,在tair put介面中也有此version引數,這個引數是為了解決併發更新同乙個資料而設定的,這其實就是樂觀鎖;
舉例說明:
資料庫表t_goods,包括id,status,name三個字段,id為主鍵,資料庫中記錄如下;
注:為了測試資料庫鎖,我使用兩個console來模擬不同的事務操作,分別用console1、console2來表示。
例1: (明確指定主鍵,並且有此資料,row lock)
console1:查詢出結果,但是把該條資料鎖定了
console2:查詢被阻塞
mysql> select * from t_goods where id=1 for update;
console2:如果console1長時間未提交,則會報錯
mysql> select * from t_goods where id=1 for update;
error 1205 : lock wait timeout exceeded; try restarting transaction
例2: (明確指定主鍵,若查無此資料,無lock)
console1:查詢結果為空
mysql> select * from t_goods where id=3 for update;
empty set
console2:查詢結果為空,查詢無阻塞,說明console1沒有對資料執行鎖定
mysql> select * from t_goods where id=3 for update;
empty set
例3: (無主鍵,table lock)
console1:查詢name=道具 的資料,查詢正常
console2:查詢name=裝備 的資料,查詢阻塞,說明console1把表給鎖住了
mysql> select * from t_goods where name=『裝備』 for update;
console2:若console1長時間未提交,則查詢返回為空
mysql> select * from t_goods where name=『裝備』 for update;
query ok, -1 rows affected
例4: (主鍵不明確,table lock)
console1:查詢正常
console2:查詢被阻塞,說明console1把表給鎖住了
mysql> select * from t_goods where id>1 for update;
console1:提交事務
mysql> commit;
query ok, 0 rows affected
console2:console1事務提交後,console2查詢結果正常
除了主鍵外,使用索引也會影響資料庫的鎖定級別(同上)
資料庫樂觀鎖與悲觀鎖
演示案例 為何需要樂觀鎖,與悲觀鎖這樣的鎖?idname money 1god 1000 假設god同志的賬上有1000元,現在有兩個執行緒同時往他的賬戶上轉錢。1.a執行緒準備向god賬戶上轉200,讀取到賬戶上有1000元,事務還未提交 2.b執行緒準備向god賬戶上轉100,讀取到賬戶上有10...
資料庫 樂觀鎖與悲觀鎖
總是認為不會產生併發問題,每次去取資料的時候總認為不會有其他執行緒對資料進行修改,因此不會上鎖,但是在更新時會判斷其他執行緒在這之前有沒有對資料進行修改,一般會使用版本號機制或cas操作實現。update table set x x 1,version version 1 where id and ...
資料庫鎖 樂觀鎖 悲觀鎖理解
參考 mysql innodb中,樂觀鎖 悲觀鎖 共享鎖 排它鎖 行鎖 表鎖 死鎖概念的理解 樂觀鎖最簡單的實現就是在表中加乙個版本號欄位如version,每次新增設定為1,更新的時候檢查版本號是否一致,如果不一致就更新失敗。版本一致才能更新,然後將版本號 1。首先資料庫需要關閉自動提交功能,或者說...