redis隊(duì)列和秒殺應(yīng)用方式
1.簡(jiǎn)述
redis隊(duì)列一般用于緩解數(shù)據(jù)庫(kù)壓力 ,諸如秒殺,郵件群發(fā),消息推送等等
redis的加入能很好的 幫助系統(tǒng)中 各個(gè)模塊解耦。
而Redis不僅可作為緩存服務(wù)器,還可用作消息隊(duì)列。它的列表類型天生支持用作消息隊(duì)列。如下圖所示:

對(duì)于服務(wù)器減少io 壓力 有一定的幫助
2.秒殺的原理

秒殺基本原理比較簡(jiǎn)單
用戶點(diǎn)擊搶購(gòu)按鈕 -> 把uid 和時(shí)間存入redis的隊(duì)列中 -> 服務(wù)器中有一個(gè)入庫(kù)程序不停輪詢r(jià)edis隊(duì)列是否有數(shù)據(jù) -> 如果有存入數(shù)據(jù)庫(kù)
這里面有2點(diǎn)需要注意一下:
- 1. 插入隊(duì)列的時(shí)候 ,需要判斷庫(kù)傳,不能出現(xiàn)多插入
- 2. 在入庫(kù)的時(shí)候 如果出現(xiàn)數(shù)據(jù)插入失敗的情況 需要進(jìn)行回滾
3.秒殺的代碼實(shí)現(xiàn)
用戶操作秒殺:
header("Content-type: text/html; charset=utf-8");
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis_name= "miaosha";
//庫(kù)存
$nums = 10;
//用戶id
$user_id = $_GET['uid'];
if(($redis->llen($redis_name)) < $nums ){
$redis->lpush($redis_name,json_encode(array('uid'=>$user_id,'time'=>microtime())));
echo $user_id."秒殺成功!";
exit();
}else{
echo "秒殺失敗!";
exit();
}
$redis->close();后臺(tái)處理秒殺隊(duì)列:
header("Content-type: text/html; charset=utf-8");
error_reporting(E_ALL);
require_once './db.php';
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis_name = "miaosha";
//數(shù)據(jù)庫(kù)
$configs =array('host'=>'127.0.0.1','port'=>'3306','user'=>'***','passwd'=>'','dbname'=>'test');
$mysql = new MMysql($configs);
//處理開始
while ($count = $redis->lLen($redis_name)) {
$task = $redis->rpop($redis_name);
$taskdata = json_decode($task, true);
$data = array(
'uid'=>$taskdata['uid'],
'time'=>$taskdata['time'],
);
$rs = $mysql->insert('redis',$data);
if(!$rs){
//由于我們是在右邊取,所以如果數(shù)據(jù)插入失敗了要從左邊放回去(重新排隊(duì)),以免影響隊(duì)列中其他元素的處理
$redis->lpush($redis_name,$task);
echo "處理失敗<br>";
}else{
echo "處理成功<br>";
}
sleep(1);
}
$redis->close();4.關(guān)于redis里的鎖
4.1 先說(shuō)一下樂(lè)觀鎖
樂(lè)觀鎖,顧名思義,樂(lè)觀的認(rèn)為數(shù)據(jù)不會(huì)被修改,只有當(dāng)更新時(shí)才去判斷數(shù)據(jù)是否被修改過(guò),通常用版本號(hào)或時(shí)間戳來(lái)實(shí)現(xiàn)。
redis中的事務(wù)通過(guò)watch和multi來(lái)實(shí)現(xiàn)。
WATCH命令可以監(jiān)控一個(gè)或多個(gè)鍵,一旦其中有一個(gè)鍵被修改(或刪除),之后的事務(wù)就不會(huì)執(zhí)行。監(jiān)控一直持續(xù)到EXEC命令(事務(wù)中的命令是在EXEC之后才執(zhí)行的,所以在MULTI命令后可以修改WATCH監(jiān)控的鍵值)
$redis = new Redis();
$redis->connect('127.0.0.1', 6379, 60);
//
$tnums = $redis->get('goods_stock_nums');
//設(shè)置商品的庫(kù)存
if($tnums==0){
echo "活動(dòng)已經(jīng)結(jié)束,明天請(qǐng)?jiān)鏴nd!";
exit();
}
//監(jiān)視該key
$redis->watch('goods_stock_nums');
//sleep(5);
//開啟事務(wù)
$redis->multi();
//修改庫(kù)存數(shù)
$redis->decr('goods_stock_nums');
//提交事務(wù),如果在此期間有其他請(qǐng)求修改了該key,那么事務(wù)會(huì)失敗
if ($redis->exec()) {
echo '搶購(gòu)成功suc';
} else {
echo '數(shù)據(jù)錯(cuò)誤,請(qǐng)重新再試fail';
}可以看到我在程序中加入了sleep(5) 這行代碼。
這是方便我在客戶端去實(shí)驗(yàn)

如果我在運(yùn)行上面這段代碼過(guò)程中,我用客戶端修改了這個(gè)值。
那么上面這段代碼就會(huì)失敗,返回 數(shù)據(jù)錯(cuò)誤,請(qǐng)重新再試fail
4.2 再說(shuō)一下 悲觀鎖
function getRedis()
{
$redis = new Redis();
$redis->connect('127.0.0.1', 6379, 60);
return $redis;
}
function lock($key, $random)
{
$redis = getRedis();
return $redis->set($key, $random, ['nx', 'ex' => 3]);
}
function unlock($key, $random)
{
$redis = getRedis();
//使用lua腳本保證原子性
$script = 'if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end';
return $redis->eval($script, [$key, $random], 1);
}
function decrGoodsStockNums()
{
$redis = getRedis();
//獲取商品庫(kù)存數(shù)
$ret = $redis->get('goods_stock_nums');
if ($ret === false) {
return false;
}
if ($ret <= 0) {
return false;
}
$random = mt_rand();
//先獲取鎖
if (lock('goods_stock_nums_lock', $random)) {
//修改庫(kù)存數(shù)
$redis->decr('goods_stock_nums');
//釋放鎖
unlock('goods_stock_nums_lock', $random);
return true;
} else {
usleep(100);
decrGoodsStockNums();
}
}
decrGoodsStockNums();上面這段引用別人的代碼
但是這種鎖的機(jī)制還是不能保證事務(wù)的安全
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
redis監(jiān)聽key過(guò)期事件的詳細(xì)步驟
本文主要介紹了redis監(jiān)聽key過(guò)期事件的詳細(xì)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
redis中RDB(Redis Data Base)的機(jī)制
本文主要介紹了redis中RDB(Redis Data Base)的機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
redis分布式鎖之可重入鎖的實(shí)現(xiàn)代碼
相信大家都知道可重入鎖的作用防止在同一線程中多次獲取鎖而導(dǎo)致死鎖發(fā)生,本文通過(guò)幾個(gè)例子給大家分享redis分布式鎖之可重入鎖的實(shí)現(xiàn)代碼,對(duì)redis分布式鎖的相關(guān)知識(shí),感興趣的朋友一起看看吧2021-05-05
Redis fork進(jìn)程分配不到內(nèi)存解決方案
這篇文章主要介紹了Redis fork進(jìn)程分配不到內(nèi)存解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11

