在當今的互聯網業界,web站點需要處理的訪問量巨大,處理速度與并發度成為很多站點優化的關鍵點。其中,緩存作為一種緩解服務器壓力的手段,備受開發者的青睞。而Memcached作為一種高效的分布式緩存系統,更是成為了眾多網站的首選。
然而,在使用Memcached進行數據緩存的過程中,程序并發訪問或者多節點同時訪問同一緩存數據,可能會產生“并發問題”,導致數據更新或者讀取不一致。為了解決這些問題,Memcached支持PHP的鎖機制,可以很好的幫助我們處理這些并發問題。
下面我們來詳細講一下使用Memcached進行并發控制。
基礎概念
Md5是常用的一種哈希函數,能夠將數據均勻的映射成一個128位的字符串。在Memcached鎖機制中,也會用到這個特性。
在Memcached中,我們需要使用unique key(同一個鎖)才能給對應的數據上鎖。而我們想要通過PHP對數據進行加鎖,需要暫存一個唯一標識,稱為token。同時,為了確保token的唯一性,我們在token前加上一個固定的前綴,并進行MD5哈希計算,得到最終的key。
下面是一個token生成的例子:
//鎖的名稱
$lockName = 'testLock';
//添加前綴和md5哈希
$token = md5('mem_'.$lockName);
鎖的相關操作
為了防止數據并發寫入或者更新,提高數據操作效率和并發控制,我們需要使用鎖機制來解決。
在Memcached中,鎖有兩種狀態:鎖定和未鎖定。判斷鎖的狀態我們需要使用Memcached提供的cas()函數實現。cas函數是一個原子操作,只有在當前數據版本號與新舊版本號相同的情況下才更新數據,避免了并發更新帶來的問題。
我們通過Memcached提供的gets()、cas()、add()等函數操作鎖的狀態。下面是一份Memcached鎖的范例代碼:
function lock($key, &$token, $seconds = 300) {
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
while(!($value=$memcached->get($key))) {
$memcached->add($key, '', 0, $seconds);
}
$token = uniqid();
$unlocked = FALSE;
while(!$unlocked) {
$lockedby = $memcached->get($key.'_lock');
if (!$lockedby) {
if ($memcached->add($key.'_lock', $token)) {
$unlocked = TRUE;
} else {
$lockedby = $memcached->get($key.'_lock');
}
}
if (!$unlocked) {
if ($lockedby == $token) {
$unlocked = TRUE;
}
}
}
}
function unlock($key, &$token) {
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
$prev_and_value = $memcached->gets($key."_lock");
if ($prev_and_value !== false) {
list($token, $value) = $prev_and_value;
$memcached->cas($prev_and_value['cas'], $key.'_lock', '', 1);
}
}
上述代碼實現了對Memcached鎖的獲取和釋放。其中,lock()函數是一個進入鎖狀態的函數。首先調用Memcached的add函數將鎖狀態設置為鎖定,然后判斷鎖狀態是否鎖定,若未鎖定,則使用Memcached提供的add函數嘗試修改狀態為鎖定,否則循環等待。unlock()函數是一個釋放鎖的函數。函數中通過gets()函數先獲取鎖狀態和version,確保當前鎖狀態未改變,再使用cas()函數嘗試修改鎖狀態。
總結
以上我們介紹了如何通過Memcached的鎖機制來確保數據操作的安全性和并發度的控制。當然,在實際開發中,還需根據實際業務場景進行靈活的應用??偟膩碚f,Memcached提供的鎖機制給我們的海量數據處理提供了非常重要的技術支持。