好久沒(méi)來(lái)整理文章了,閑了沒(méi)事寫(xiě)篇文章記錄下php+redis實(shí)現(xiàn)商城秒殺功能。
1、安裝redis,根據(jù)自己的php版本安裝對(duì)應(yīng)的redis擴(kuò)展(此步驟簡(jiǎn)單的描述一下)
1.1.安裝php_igbinary.dll,php_redis.dll擴(kuò)展此處需要注意你的php版本如圖:
1.2.php.ini文件新增extension=php_igbinary.dll;extension=php_redis.dll兩處擴(kuò)展
ok此處已經(jīng)完成第一步redis環(huán)境搭建完成看看phpinfo
2、項(xiàng)目中實(shí)際使用redis
2.1.第一步配置redis參數(shù)如下,redis安裝的默認(rèn)端口為6379:
?php
/* 數(shù)據(jù)庫(kù)配置 */
return array(
'DATA_CACHE_PREFIX' => 'Redis_',//緩存前綴
'DATA_CACHE_TYPE'=>'Redis',//默認(rèn)動(dòng)態(tài)緩存為Redis
'DATA_CACHE_TIMEOUT' => false,
'REDIS_RW_SEPARATE' => true, //Redis讀寫(xiě)分離 true 開(kāi)啟
'REDIS_HOST'=>'127.0.0.1', //redis服務(wù)器ip,多臺(tái)用逗號(hào)隔開(kāi);讀寫(xiě)分離開(kāi)啟時(shí),第一臺(tái)負(fù)責(zé)寫(xiě),其它[隨機(jī)]負(fù)責(zé)讀;
'REDIS_PORT'=>'6379',//端口號(hào)
'REDIS_TIMEOUT'=>'300',//超時(shí)時(shí)間
'REDIS_PERSISTENT'=>false,//是否長(zhǎng)連接 false=短連接
'REDIS_AUTH'=>'',//AUTH認(rèn)證密碼
);
?>
2.2.實(shí)際函數(shù)中使用redis:
/**
* redis連接
* @access private
* @return resource
* @author bieanju
*/
private function connectRedis(){
$redis=new \Redis();
$redis->connect(C("REDIS_HOST"),C("REDIS_PORT"));
return $redis;
}
2.3. 秒殺的核心問(wèn)題是在大并發(fā)的情況下不會(huì)超出庫(kù)存的購(gòu)買(mǎi),這個(gè)就是處理的關(guān)鍵所以思路是第一步在秒殺類的先做一些基礎(chǔ)的數(shù)據(jù)生成:
//現(xiàn)在初始化里面定義后邊要使用的redis參數(shù)
public function _initialize(){
parent::_initialize();
$goods_id = I("goods_id",'0','intval');
if($goods_id){
$this->goods_id = $goods_id;
$this->user_queue_key = "goods_".$goods_id."_user";//當(dāng)前商品隊(duì)列的用戶情況
$this->goods_number_key = "goods".$goods_id;//當(dāng)前商品的庫(kù)存隊(duì)列
}
$this->user_id = $this->user_id ? $this->user_id : $_SESSION['uid'];
}
2.4. 第二步就是關(guān)鍵所在,用戶在進(jìn)入商品詳情頁(yè)前先將當(dāng)前商品的庫(kù)存進(jìn)行隊(duì)列存入redis如下:
/**
* 訪問(wèn)產(chǎn)品前先將當(dāng)前產(chǎn)品庫(kù)存隊(duì)列
* @access public
* @author bieanju
*/
public function _before_detail(){
$where['goods_id'] = $this->goods_id;
$where['start_time'] = array("lt",time());
$where['end_time'] = array("gt",time());
$goods = M("goods")->where($where)->field('goods_num,start_time,end_time')->find();
!$goods $this->error("當(dāng)前秒殺已結(jié)束!");
if($goods['goods_num'] > $goods['order_num']){
$redis = $this->connectRedis();
$getUserRedis = $redis->hGetAll("{$this->user_queue_key}");
$gnRedis = $redis->llen("{$this->goods_number_key}");
/* 如果沒(méi)有會(huì)員進(jìn)來(lái)隊(duì)列庫(kù)存 */
if(!count($getUserRedis) !$gnRedis){
for ($i = 0; $i $goods['goods_num']; $i ++) {
$redis->lpush("{$this->goods_number_key}", 1);
}
}
$resetRedis = $redis->llen("{$this->goods_number_key}");
if(!$resetRedis){
$this->error("系統(tǒng)繁忙,請(qǐng)稍后搶購(gòu)!");
}
}else{
$this->error("當(dāng)前產(chǎn)品已經(jīng)秒殺完!");
}
}
接下來(lái)要做的就是用ajax來(lái)異步的處理用戶點(diǎn)擊購(gòu)買(mǎi)按鈕進(jìn)行符合條件的數(shù)據(jù)進(jìn)入購(gòu)買(mǎi)的排隊(duì)隊(duì)列(如果當(dāng)前用戶沒(méi)在當(dāng)前產(chǎn)品用戶的隊(duì)列就進(jìn)入排隊(duì)并且pop一個(gè)庫(kù)存隊(duì)列,如果在就拋出,):
/**
* 搶購(gòu)商品前處理當(dāng)前會(huì)員是否進(jìn)入隊(duì)列
* @access public
* @author bieanju
*/
public function goods_number_queue(){
!$this->user_id $this->ajaxReturn(array("status" => "-1","msg" => "請(qǐng)先登錄"));
$model = M("flash_sale");
$where['goods_id'] = $this->goods_id;
$goods_info = $model->where($where)->find();
!$goods_info $this->error("對(duì)不起當(dāng)前商品不存在或已下架!");
/* redis 隊(duì)列 */
$redis = $this->connectRedis();
/* 進(jìn)入隊(duì)列 */
$goods_number_key = $redis->llen("{$this->goods_number_key}");
if (!$redis->hGet("{$this->user_queue_key}", $this->user_id)) {
$goods_number_key = $redis->lpop("{$this->goods_number_key}");
}
if($goods_number_key){
// 判斷用戶是否已在隊(duì)列
if (!$redis->hGet("{$this->user_queue_key}", $this->user_id)) {
// 插入搶購(gòu)用戶信息
$userinfo = array(
"user_id" => $this->user_id,
"create_time" => time()
);
$redis->hSet("{$this->user_queue_key}", $this->user_id, serialize($userinfo));
$this->ajaxReturn(array("status" => "1"));
}else{
$modelCart = M("cart");
$condition['user_id'] = $this->user_id;
$condition['goods_id'] = $this->goods_id;
$condition['prom_type'] = 1;
$cartlist = $modelCart->where($condition)->count();
if($cartlist > 0){
$this->ajaxReturn(array("status" => "2"));
}else{
$this->ajaxReturn(array("status" => "1"));
}
}
}else{
$this->ajaxReturn(array("status" => "-1","msg" => "系統(tǒng)繁忙,請(qǐng)重試!"));
}
}
附加一個(gè)調(diào)試的函數(shù),刪除指定隊(duì)列值:
public function clearRedis(){
set_time_limit(0);
$redis = $this->connectRedis();
//$Rd = $redis->del("{$this->user_queue_key}");
$Rd = $redis->hDel("goods49",'用戶id'');
$a = $redis->hGet("goods_49_user", '用戶id');
if(!$a){
dump($a);
}
if($Rd == 0){
exit("Redis隊(duì)列已釋放!");
}
}
走到此處的時(shí)候秒殺的核心基本就完了,細(xì)節(jié)還需要自己在去完善,像購(gòu)物車這邊的處理還有訂單的處理,好吧開(kāi)始跑程序利用apache自身的ab可以進(jìn)行簡(jiǎn)單的模擬并發(fā)測(cè)試如下:
跑起來(lái),我擦跑步起來(lái)redis沒(méi)有任何反應(yīng),此時(shí)還少一步重要的步驟就是開(kāi)啟redis服務(wù),請(qǐng)根據(jù)自己的系統(tǒng)下一個(gè)redisbin_x32或者redisbin_x64的redis服務(wù)管理工具,點(diǎn)擊redis-server.exe,ok至此全部完成如下圖:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
您可能感興趣的文章:- php結(jié)合redis實(shí)現(xiàn)高并發(fā)下的搶購(gòu)、秒殺功能的實(shí)例
- Redis瞬時(shí)高并發(fā)秒殺方案總結(jié)
- redis使用watch秒殺搶購(gòu)實(shí)現(xiàn)思路
- 基于redis分布式鎖實(shí)現(xiàn)秒殺功能
- php和redis實(shí)現(xiàn)秒殺活動(dòng)的流程
- Redis使用watch完成秒殺搶購(gòu)功能的代碼
- Java使用Redis實(shí)現(xiàn)秒殺功能
- 使用Redis實(shí)現(xiàn)秒殺功能的簡(jiǎn)單方法