PHP redis實現的悲觀鎖機制示例

2023-03-19 00:45:31 字數 2240 閱讀 7713

鎖機制

通常使用的鎖分為樂觀鎖,悲觀鎖這兩種,簡單介紹下這兩種鎖,作為本文的背景知識,對這類知識已經有足程式設計客棧夠了解的同學可以跳過這部分。

樂觀鎖先來看下百度百科上的解釋:大多是基於資料版本( version )記錄機制實現。何謂資料版本?即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表增加一個 “version” 欄位來實現。讀取出資料時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交資料的版本資料與資料庫表對應記錄的當前版本資訊進行比對,如果提交的資料版本號大於資料庫表當前版本號,則予以更新,否則認為是過期資料。

其實說白了,就是好比一個健身房裡只有一臺跑步機,在健身房門口有個排號機,每個進健身房的人都得先領一個號碼才能進入,如果跑步機上有人,則在一邊做做熱身、喝喝水,如果跑步機上沒人,則確認跑步機上當前顯示的號碼(上一個用過跑步機的人的號碼)是否比自己手持的小,如果小,則可以使用;否則,就意味著過號,而過號在現實中我們的都知道要麼走,要麼重排,就是不能插隊,在系統中也是一樣的,通常是返回錯誤。

悲觀鎖同樣,來看下百度百科的解釋:具有強烈的獨佔和排他特性。它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制(也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改資料)。

然後,也同樣通俗的解釋下,還是那個健身房。這次在門口不需要排號機了,而是掛著把鑰匙(只有一把),想進去的人必須拿到這把鑰匙才行,拿到鑰匙的人可以進入,不管是熱身、喝水還是跑步都可以,直到他出來把鑰匙掛回牆上,下一個才能去爭取,拿到的才可以再進去。聽著好像有點不人性化,所以悲觀鎖比較適合強一致性的場景,但效率比較低,特別是讀的併發低。樂觀鎖則適用於讀多寫少,併發衝突少的場景。

背景先說下,本文的開發背景,方便大家瞭解為什麼要使用悲觀鎖以及文中鎖的詳細設計。

任務分發系統:任務池(mysql)中存在大量任務(文章),現在需要使用者協助編輯,系統基本需求如下(簡化版):

1、推送使用者感興趣的分類下的任務到使用者編輯器中;

2、使用者編輯提交一個任務後,自動推送下一個任務;

3、每次只分配一個任務給使用者;

4、如果一個使用者佔有某任務超過一定時間,則自動釋放任務,任務進任務池,重新迴圈;

5、……

目標目標有兩個:

1、一個任務在同一時間段內只能被一個使用者所持有;

2、避免出現死任務,即避免任務被使用者長時間佔有,無法釋放。

思路由於系統併發量較大,並且有頻繁的寫操作,所以選擇悲觀鎖來控制每個任務只能同時被一個使用者領取。主要思路如下:

1、從任務池中找出一部分可分配的任務;

2、根據一定順序,選擇一個任務,作為候選推送任務;

3、嘗試對候選推送任務加鎖;

4、如果加鎖成功,則推送任務給使用者,並修改對應的任務狀態和使用者狀態;

5、如果加鎖失敗,則任務已被領取,重複2-5,直到推送成功。

實現這裡只介紹下鎖的實現機制,其餘業務邏輯略過。由於加鎖過程應該是不可拆解的,也就是常說的原子型操作,因此這裡選擇redis中的setnx操作作為加鎖的方法。

簡化版的**如下:

function lock($strmutex, $inttimeout)

return false;

}這段**有個問題,就是setnx成功,但expire失敗,這就可能存在死任務的情況。解決這個問題的一種通用方法是通過使用incr方法代替setnx,具體如下:

function lock($strmutex, $inttimeout, $intmaxtimes = 0)

if ($intmaxtimes > 0 && $intret >= $intmaxtimes && $objredis->ttl($strmutex) === -1)

return false;

}這段**通過$intmaxtimes來保證即使在expire未成oqfymcir功的時候也能強制解鎖,保證系統不會出現死任務。

還有沒有更好的方法呢?

其實redis中的set操作已相容了setnx,並且支援設定過期時間。

function lock($strmutex, $inttimeout)

return false;

}這個方法是我認為目前最好的,但是為什麼沒有直接介紹這個方法,而是先介紹incr那個方法呢?其實細心的同學可以看到上面那個方面有兩個加粗的字”通用“。之所以這麼說是因為set方法是從redis2.6.12版本才開始支援多引數的。

水平有限,歡迎指正~

5 悲觀鎖實現 InnoDB 悲觀鎖實現

innodb中的select for update語句 1 select for update語句僅適用於innodb 2 select for update語句必須在事務中才能生效。3 在執行事務中的select for update語句時,mysql會對查詢結果集中的每行資料都新增排他鎖 行鎖 ...

php redis 學習 二 悲觀鎖

悲觀鎖 pessimistic lock 顧名思義,就是很悲觀,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖。傳統的關係型資料庫裡邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。header co...

mysql悲觀鎖的實現

0.開始事務 begin begin work start transaction 三者選一就可以 1.查詢出商品資訊 select status from table where id 1 for update 2.根據商品資訊生成訂單 insert into table111 id,goods ...

MySQL悲觀鎖與樂觀鎖的實現方案

目錄 悲觀鎖和樂觀鎖是用來解決併發問題的兩種思想,在不同的平臺有著各自的實現。例如在j a中,synchronized就可以認為是悲觀鎖的實現 不嚴謹,有鎖升級的過程,升級到重量級鎖才算 atomic 原子類可以認為是樂觀鎖的實現。悲觀鎖具有強烈的獨佔和排他特性,在整個處理過程中將資料處於鎖定狀態,...

MySQL樂觀鎖,悲觀鎖以及實現

悲觀鎖 當事務在運算元據時把這部分資料進行鎖定,直到操作完畢後再解鎖,其他事務操作才可操作該部分資料。這將防止其他程序讀取或修改表中的資料。當事務提交的時候會釋放事務過程的鎖。實現 一般使用 select for update 對所選擇的資料進行加鎖處理,select from account wh...

mysql實現悲觀鎖 mysql 悲觀鎖詳解

悲觀鎖指的是對資料被外界 包括本系統當前的其他事務,以及來自外部系統的事務處理 修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制 也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會...

redis悲觀鎖實現教程

通常使用的鎖分為樂觀鎖,悲觀鎖這兩種,簡單介紹下這兩種鎖,作為本文的背景知識,對這類知識已經有足夠了解的同學可以跳過這部分。先來看下百科上的解釋 大多是基於資料版本 version 記錄機制實現。何謂資料版本?即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表增加一個 v...

樂觀鎖和悲觀鎖的簡單實現

資料庫表 seckill 樂觀鎖 樂觀鎖,大多是基於資料版本 version 記錄機制實現。何謂資料版本?即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表增加一個 version 欄位來實現。讀取出資料時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交資料...

樂觀鎖悲觀鎖區別雨實現

1 無論是選擇悲觀鎖策略,還是樂觀鎖策略。如果一個物件被上了鎖,那麼該物件都會受這個鎖的控制和影響。如果這個鎖是個排它鎖,那麼其它會話都不能修改它。2 選擇悲觀鎖策略,還是樂觀鎖策略,這主要是由應用和業務需求來確定的。如果你的應用和業務經常會出現從我看到要修改的記錄的值,到我修改完成該記錄這個時間段...

樂觀鎖,悲觀鎖

1.使用自增長的整數表示資料版本號。更新時檢查版本號是否一致,比如資料庫中資料版本為6,更新提交時version 6 1,使用該version值 7 與資料庫version 1 7 作比較,如果相等,則可以更新,如果不等則有可能其他程式已更新該記錄,所以返回錯誤。2.使用時間戳來實現.注 對於以上兩...