麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 數據庫 > Redis > 正文

php結合redis實現高并發下的搶購、秒殺功能的實例

2020-03-17 12:39:46
字體:
來源:轉載
供稿:網友

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

1 高并發對數據庫產生的壓力

2 競爭狀態下如何解決庫存的正確減少("超賣"問題)

對于第一個問題,已經很容易想到用緩存來處理搶購,避免直接操作數據庫,例如使用Redis

重點在于第二個問題

常規寫法:

查詢出對應商品的庫存,看是否大于0,然后執行生成訂單等操作,但是在判斷庫存是否大于0處,如果在高并發下就會有問題,導致庫存量出現負數

<?php$conn=mysql_connect("localhost","big","123456"); if(!$conn){ 	echo "connect failed"; 	exit; } mysql_select_db("big",$conn); mysql_query("set names utf8");$price=10;$user_id=1;$goods_id=1;$sku_id=11;$number=1;//生成唯一訂單function build_order_no(){  return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);}//記錄日志function insertLog($event,$type=0){	global $conn;	$sql="insert into ih_log(event,type) 	values('$event','$type')"; 	mysql_query($sql,$conn); }//模擬下單操作//庫存是否大于0$sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";//解鎖 此時ih_store數據中goods_id='$goods_id' and sku_id='$sku_id' 的數據被鎖住(注3),其它事務必須等待此次事務 提交后才能執行$rs=mysql_query($sql,$conn);$row=mysql_fetch_assoc($rs);if($row['number']>0){//高并發下會導致超賣	$order_sn=build_order_no();	//生成訂單 	$sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price) 	values('$order_sn','$user_id','$goods_id','$sku_id','$price')"; 	$order_rs=mysql_query($sql,$conn); 		//庫存減少	$sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";	$store_rs=mysql_query($sql,$conn); 	if(mysql_affected_rows()){ 		insertLog('庫存減少成功');	}else{ 		insertLog('庫存減少失敗');	} }else{	insertLog('庫存不夠');}?>

優化方案1:將庫存字段number字段設為unsigned,當庫存為0時,因為字段不能為負數,將會返回false

 

//庫存減少$sql="update ih_store set number=number-{$number} where sku_id='$sku_id' and number>0";$store_rs=mysql_query($sql,$conn); if(mysql_affected_rows()){ 	insertLog('庫存減少成功');}

優化方案2:使用MySQL的事務,鎖住操作的行

<?php$conn=mysql_connect("localhost","big","123456"); if(!$conn){ 	echo "connect failed"; 	exit; } mysql_select_db("big",$conn); mysql_query("set names utf8");$price=10;$user_id=1;$goods_id=1;$sku_id=11;$number=1;//生成唯一訂單號function build_order_no(){  return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);}//記錄日志function insertLog($event,$type=0){	global $conn;	$sql="insert into ih_log(event,type) 	values('$event','$type')"; 	mysql_query($sql,$conn); }//模擬下單操作//庫存是否大于0mysql_query("BEGIN");	//開始事務$sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id' FOR UPDATE";//此時這條記錄被鎖住,其它事務必須等待此次事務提交后才能執行$rs=mysql_query($sql,$conn);$row=mysql_fetch_assoc($rs);if($row['number']>0){	//生成訂單 	$order_sn=build_order_no();		$sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price) 	values('$order_sn','$user_id','$goods_id','$sku_id','$price')"; 	$order_rs=mysql_query($sql,$conn); 		//庫存減少	$sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";	$store_rs=mysql_query($sql,$conn); 	if(mysql_affected_rows()){ 		insertLog('庫存減少成功');		mysql_query("COMMIT");//事務提交即解鎖	}else{ 		insertLog('庫存減少失敗');	}}else{	insertLog('庫存不夠');	mysql_query("ROLLBACK");}?>

優化方案3:使用非阻塞的文件排他鎖

<?php$conn=mysql_connect("localhost","root","123456"); if(!$conn){ 	echo "connect failed"; 	exit; } mysql_select_db("big-bak",$conn); mysql_query("set names utf8");$price=10;$user_id=1;$goods_id=1;$sku_id=11;$number=1;//生成唯一訂單號function build_order_no(){  return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);}//記錄日志function insertLog($event,$type=0){	global $conn;	$sql="insert into ih_log(event,type) 	values('$event','$type')"; 	mysql_query($sql,$conn); }$fp = fopen("lock.txt", "w+");if(!flock($fp,LOCK_EX | LOCK_NB)){	echo "系統繁忙,請稍后再試";	return;}//下單$sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";$rs=mysql_query($sql,$conn);$row=mysql_fetch_assoc($rs);if($row['number']>0){//庫存是否大于0	//模擬下單操作 	$order_sn=build_order_no();		$sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price) 	values('$order_sn','$user_id','$goods_id','$sku_id','$price')"; 	$order_rs=mysql_query($sql,$conn); 		//庫存減少	$sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";	$store_rs=mysql_query($sql,$conn); 	if(mysql_affected_rows()){ 		insertLog('庫存減少成功');		flock($fp,LOCK_UN);//釋放鎖	}else{ 		insertLog('庫存減少失敗');	} }else{	insertLog('庫存不夠');}fclose($fp);

優化方案4:使用redis隊列,因為pop操作是原子的,即使有很多用戶同時到達,也是依次執行,推薦使用(mysql事務在高并發下性能下降很厲害,文件鎖的方式也是)

先將商品庫存如隊列

 

<?php$store=1000;$redis=new Redis();$result=$redis->connect('127.0.0.1',6379);$res=$redis->llen('goods_store');echo $res;$count=$store-$res;for($i=0;$i<$count;$i++){	$redis->lpush('goods_store',1);}echo $redis->llen('goods_store');?>

搶購、描述邏輯

<?php$conn=mysql_connect("localhost","big","123456"); if(!$conn){ 	echo "connect failed"; 	exit; } mysql_select_db("big",$conn); mysql_query("set names utf8");$price=10;$user_id=1;$goods_id=1;$sku_id=11;$number=1;//生成唯一訂單號function build_order_no(){  return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);}//記錄日志function insertLog($event,$type=0){	global $conn;	$sql="insert into ih_log(event,type) 	values('$event','$type')"; 	mysql_query($sql,$conn); }//模擬下單操作//下單前判斷redis隊列庫存量$redis=new Redis();$result=$redis->connect('127.0.0.1',6379);$count=$redis->lpop('goods_store');if(!$count){	insertLog('error:no store redis');	return;}//生成訂單 $order_sn=build_order_no();$sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price) values('$order_sn','$user_id','$goods_id','$sku_id','$price')"; $order_rs=mysql_query($sql,$conn); //庫存減少$sql="update ih_store set number=number-{$number} where sku_id='$sku_id'";$store_rs=mysql_query($sql,$conn); if(mysql_affected_rows()){ 	insertLog('庫存減少成功');}else{ 	insertLog('庫存減少失敗');} 

模擬5000高并發測試

webbench -c 5000 -t 60 http://192.168.1.198/big/index.php
ab -r -n 6000 -c 5000  http://192.168.1.198/big/index.php

上述只是簡單模擬高并發下的搶購,真實場景要比這復雜很多,很多注意的地方

如搶購頁面做成靜態的,通過ajax調用接口

再如上面的會導致一個用戶搶多個,思路:

需要一個排隊隊列和搶購結果隊列及庫存隊列。高并發情況,先將用戶進入排隊隊列,用一個線程循環處理從排隊隊列取出一個用戶,判斷用戶是否已在搶購結果隊列,如果在,則已搶購,否則未搶購,庫存減1,寫數據庫,將用戶入結果隊列。

測試數據表

---- 數據庫: `big`---- ------------------------------------------------------------ 表的結構 `ih_goods`--CREATE TABLE IF NOT EXISTS `ih_goods` (  `goods_id` int(10) unsigned NOT NULL AUTO_INCREMENT,  `cat_id` int(11) NOT NULL,  `goods_name` varchar(255) NOT NULL,  PRIMARY KEY (`goods_id`)) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;---- 轉存表中的數據 `ih_goods`--INSERT INTO `ih_goods` (`goods_id`, `cat_id`, `goods_name`) VALUES(1, 0, '小米手機');-- ------------------------------------------------------------ 表的結構 `ih_log`--CREATE TABLE IF NOT EXISTS `ih_log` ( `id` int(11) NOT NULL AUTO_INCREMENT, `event` varchar(255) NOT NULL, `type` tinyint(4) NOT NULL DEFAULT '0', `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;---- 轉存表中的數據 `ih_log`---- ------------------------------------------------------------ 表的結構 `ih_order`--CREATE TABLE IF NOT EXISTS `ih_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_sn` char(32) NOT NULL, `user_id` int(11) NOT NULL, `status` int(11) NOT NULL DEFAULT '0', `goods_id` int(11) NOT NULL DEFAULT '0', `sku_id` int(11) NOT NULL DEFAULT '0', `price` float NOT NULL, `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='訂單表' AUTO_INCREMENT=1 ;---- 轉存表中的數據 `ih_order`---- ------------------------------------------------------------ 表的結構 `ih_store`--CREATE TABLE IF NOT EXISTS `ih_store` ( `id` int(11) NOT NULL AUTO_INCREMENT, `goods_id` int(11) NOT NULL, `sku_id` int(10) unsigned NOT NULL DEFAULT '0', `number` int(10) NOT NULL DEFAULT '0', `freez` int(11) NOT NULL DEFAULT '0' COMMENT '虛擬庫存', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='庫存' AUTO_INCREMENT=2 ;---- 轉存表中的數據 `ih_store`--INSERT INTO `ih_store` (`id`, `goods_id`, `sku_id`, `number`, `freez`) VALUES(1, 1, 11, 500, 0);

以上就是小編為大家帶來的php結合redis實現高并發下的搶購、秒殺功能的實例全部內容了,希望大家多多支持VEVB武林網~


注:相關教程知識閱讀請移步到Redis頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 久久国精品| 香蕉国产9| 久久久国产精品电影 | 久久恋| 日韩视频一区二区在线观看 | 久久久三级免费电影 | 久久一区三区 | 毛片在哪里看 | 久草在线新时代视觉 | 欧美国产成人在线 | 欧美性久久久 | 久久精品视频首页 | 欧美一级免费在线观看 | av电影免费在线看 | av在线直播观看 | 亚洲第五色综合网 | 成人毛片免费 | 成人在线精品视频 | 91久久精品一区二区 | 欧美日本免费一区二区三区 | av免费在线网 | 老女人碰碰在线碰碰视频 | 久久精品国产精品亚洲 | 中国国语毛片免费观看视频 | 在线看国产视频 | 2019中文字幕在线播放 | 黄色网页在线看 | 国产精品视频专区 | 日韩一级片毛片 | 久久久三级免费电影 | 97干在线| 久久久亚洲欧美综合 | 欧美一级毛片一级毛片 | 嗯~啊~弄嗯~啊h高潮视频 | 欧美一级二级毛片视频 | 成人区一区二区 | 在线播放免费视频 | 欧美激情图区 | 91美女视频在线观看 | 毛片a级毛片免费播放100 | 精品国产一区二区亚洲人成毛片 |