搶購是如今很常見的乙個應用場景,主要需要解決的問題有兩個:
1 高併發對資料庫產生的壓力
2 競爭狀態下如何解決庫存的正確減少(「超賣」問題)
對於第乙個問題,已經很容易想到用快取來處理搶購,避免直接運算元據庫,例如使用redis。重點在於第二個問題,我們看看下面一種常規的實現**:
<?php
require('predis/src/autoloader.php');
$redis = new predis\client(array(
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => '6379'
));//redis 登入
$redis->auth('123456');
//庫存
$num = 10;
//使用者id
$user_id = $_session['user_id'];
//檢查庫存
$len = $redis->llen('order:1');
if($len >= $num)
//把搶到的使用者存入到列表中
$result = $redis->lpush('order:1',$user_id);
if($result)
?>
如果**正常執行,列表order:1中最多只能儲存10個使用者的id,因為庫存只有10個。
然而,在使用apache ab工具模擬很多使用者併發請求時,最後發現order:1中總是超過10個使用者,也就是出現了「超賣」。
問題就出在這一段**:
//檢查庫存
$len = $redis->llen('order:1');
if($len >= $num)
在搶購進行到一定程度,假如現在已經有9個人搶購成功,又來了3個使用者同時搶購,這時if條件將會被繞過,這三個使用者都能搶購成功。而實際上只有一件庫存可以搶了。
在高併發下,很多不是問題的,都成了問題。要解決「超賣」問題,核心在於保證檢查庫存時的操作是依次執行的,形象的說就是把「多執行緒」轉成「單執行緒」。即使有很多使用者同時到達,也是乙個個檢查並給與搶購資格,一旦庫存搶盡,後面的使用者就無法繼續了。
我們需要使用redis的原子操作來實現這個「單執行緒」。首先我們把庫存存在goods:1這個列表中,假設有10件庫存,就往列表中push10個數,這個數沒有實際意義,僅僅代表一件庫存。搶購開始後,每到來乙個使用者,就從goods:1中pop乙個數,表示使用者搶購成功。當列表為空時,表示已經被搶光了。因為列表的pop操作是原子的,即使有很多使用者同時到達,也是依次執行的。搶購的示例**如下:
<?php
//搶購
require('predis/src/autoloader.php');
$redis = new predis\client(array(
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => '6379'
));$redis->auth('123456');
//使用者id
$user_id = $_session['user_id'];
$check = $redis->lpop('goods:1');
if(!$check)
$result = $redis->lpush('order:1',$user_id);
if($result)
?>
使用者搶購成功後,我們將使用者id存入了order:1列表中。接下來我們可以引導這些使用者去完成訂單的其他步驟,這裡才涉及到與資料庫的互動。最終只有很少的人走到這一步,也就解決的資料庫的壓力問題。
為了檢測實際效果,我使用apache ab工具模擬10、20、1000個使用者併發進行搶購,經過大量的測試,最終搶購成功的使用者始終為10,沒有出現「超賣」。
Redis 的 5 個常見應用場景
redis 是乙個強大的記憶體型儲存,具有豐富的資料結構,使其可以應用於很多方面,包括作為資料庫 快取 訊息佇列等等。如果你的印象中redis只是乙個 key value 儲存,那就錯過了redis很多強大的功能,下面就是實際應用場景中5個最普遍的案例。如果你使用的是伺服器端內容渲染,你又不想為每個...
Redis 的 5 個常見應用場景
redis 是乙個強大的記憶體型儲存,具有豐富的資料結構,使其可以應用於很多方面,包括作為資料庫 快取 訊息佇列等等。如果你的印象中redis只是乙個 key value 儲存,那就錯過了redis很多強大的功能,下面就是實際應用場景中5個最普遍的案例。如果你使用的是伺服器端內容渲染,你又不想為每個...
Redis 的 5 個常見應用場景
前言 redis 是乙個強大的記憶體型儲存,具有豐富的資料結構,使其可以應用於很多方面,包括作為資料庫 快取 訊息佇列等等。如果你的印象中redis只是乙個 key value 儲存,那就錯過了redis很多強大的功能,下面就是實際應用場景中5個最普遍的案例。1.全頁面快取 如果你使用的是伺服器端內...