在電商活動中,經常會有「秒殺」的搶購活動,這種場景,伺服器通常面臨高併發的請求處理。也就是說,在同一時間,會有大量併發的使用者同時去購買某個商品,這時候,就需要保證不會出現過度**的情況(使用者最終購買的商品數量超過實際的商品數量)。這裡就需要應用到redis中加鎖和解鎖的特性來保證每次的購物操作只有乙個使用者在進行,避免出現競爭導致髒資料的情況發生。
下面,我們來介紹如何正確使用加鎖和解鎖。
redis官方在鎖操作上是建議使用set命令來進行,使用方式如下:
if ($redis->set('my:lock', 1, ['nx'])) 複製**
其中
nx — 表示只有key不存在的時候才設定
這個方法有個問題,假如乙個客戶端獲取到鎖後發生奔潰或者一直占用著鎖不釋放,就會導致死鎖,使得後續的使用者無法獲取到鎖進行操作。所以這個操作需要設定乙個超時時間。
針對上面方法的問題,我們使用expire方法設定超時時間。但到這裡就解決了問題嗎?
沒有!因為這裡expire不是原子操作,如果在操作完setnx後客戶端奔潰,這時候就沒有成功設定超時時間,同樣使得加鎖操作面臨上面的問題。
if ($redis->set('my:lock', 1, ['nx'])) 複製**
為了解決這個問題,我們可以把超時時間設定跟set操作放在一起,如下所示:
if ($redis->set('my:lock', 1, ['nx', 'ex' => 10])) 複製**
那麼到這裡,所有問題解決了嗎?很遺憾,還是沒有。
這裡會出現誤刪其他人鎖的問題。假如客戶端1在獲取鎖後由於超時,這時候鎖會自動釋放,客戶端2就可以獲取到鎖。這時候客戶端1會刪除鎖,而這裡刪除的鎖實際上是客戶端2的。如下圖所示:
所以這裡需要為每乙個鎖設定唯一的標識,在解鎖之前要判斷解得鎖是否跟自己獲取到的是同乙個鎖,如果不是就不做任何操作。
這也是官方推薦的方式,使用lua+redis的方法。之所以使用lua是為了保證原子性。
在lua指令碼中,一共涉及到3個操作,分貝時:get、判斷和del。而如果把這些邏輯放到客戶端,會由於沒有解決原子性問題而面臨上面同樣的問題。所以這裡要用到lua指令碼來解決。
$script = '
if redis.call("get",keys[1]) == ar**[1]
then
return redis.call("del",keys[1])
else
return 0
end ';
$token = uniqid(mt_rand(), true);
if ($redis->set('my:lock', $token, ['nx', 'ex' => 10])) else 複製**
UNIX作業系統中加鎖和解鎖
unix作業系統加鎖和解鎖的基本思想是,當某個程序進入臨界區,它將持有乙個某種型別的鎖 unix裡一般來說是semaphore,linux裡一般是訊號量和原子量或者spinlock 當其他程序在該程序沒有釋放該鎖時試圖進入臨界區 加鎖 它將會被設定成睡眠狀態,然後被置入等待該鎖的程序佇列 某個優先順...
oracle中加鎖與解鎖
oracle中的資料在併發操作時,為了防止錯誤的發生可以進行記錄或者資料庫表的加鎖操作。當鎖操作完成時可以進行解鎖操作。資料庫中加鎖有兩種方式,獨佔模式和共享模式。1.獨佔模式,不允許其他會話以任何方式共享鎖定資源,當進行資料庫資料修改時可以使用這種模式。2.共享模式,允許在資料訪問時,併發共同訪問...
oracle資料庫使用者加鎖和解鎖
分類 database 2012 04 19 17 17 7506人閱讀收藏 舉報 資料庫oracle user sql oracle資料庫安裝好之後,scott之類的使用者預設情況下是被鎖住的,無法使用scott使用者登入資料庫。使用有alter user資料庫許可權的使用者登陸,角色選sysdb...