搶購是如今很常見的乙個應用場景,主要需要解決的問題

2021-08-09 17:19:38 字數 1979 閱讀 3280

搶購是如今很常見的乙個應用場景,主要需要解決的問題有兩個:

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.全頁面快取 如果你使用的是伺服器端內...