1. 程式人生 > >redis秒殺簡單實現

redis秒殺簡單實現

redis中的list型別是很好的一個佇列,可以在秒殺的高併發中暫存快取,然後過了秒殺峰期再去插入資料庫,可以減輕伺服器很大的壓力。

基本思路:

先要做一個token防止表單重複提交,這裡用session存一下token,然後前端先請求token的介面把token存到hidden的input中,提交時把token一併提交。這時後端判斷token是否與session中的一致,一致就重新整理token使其重複提交時與原token不一致。

接著後端接收到了傳過來的使用者名稱(這裡應該是從session中拿的,作為演示就先用ajax傳送)後,先在佇列中判斷列表的長度是否超過了設定的長度。沒超過就正常寫入redis,超過了就返回已經被搶光了。

秒殺完後就可以啟動插入資料庫的檔案了,這裡可以設定一個定時任務在秒殺結束後就自行開啟。

下面是程式碼

addBook.php ---接受從前端傳來的使用者名稱和token,比較token以及判斷佇列是否滿了

<?php
session_start();
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
//get post
$token = $_POST['token'];
$user = $_POST['user'];
//判斷表單是否重複提交
if($_SESSION['token'] == $token){
	//重置token
	$_SESSION['token'] = uniqid();
	//判斷佇列長度
	if($redis->lLen('bookKill') < 10){
		//往redis中寫入資料
		$redis->lPush('bookKill',$user);
		echo 'success!!';
	}else{
		echo 'you are late';
	}
}
else{
	echo 'token time out';
}


?>

save.php  ---儲存至資料庫的檔案,迴圈10次從列表從右往左中取10條資料,redis列表中先插的資料在右邊,新的資料在左邊。資料庫有錯誤資訊就記錄到log日誌中。注意插入varchar型別的資料要加上單引號。

<?php
$con = mysqli_connect('127.0.0.1','root','','book');
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
for($i = 0;$i < 10;$i++){
	//獲取列表中的值
	$data = $redis->Rpop('bookKill');
	//如果快取中沒資料了,但還不夠10個,說明沒有被秒殺完。就跳出迴圈。
	if($data == null)
		break;
	//插入資料庫。記得username是字串要加上單引號,不然無法插入又記不了日誌。。。
	$sql = 'insert into book (`username`) values(\''.$data.'\');';
	//插入資料,如果有錯誤資訊寫入到log日誌中
	if(!mysqli_query($con,$sql)){
		file_put_contents('./dbErrorLog.php', mysqli_error($con));
	}
}
?>

shop.html ---前端頁面,使用ajax來發送資料

<!DOCTYPE html>
<html>
<head>
	<title></title>
	<meta charset="utf-8">
</head>
<body>
	<input type="hidden" id="token">
	<input type="text" id="user">
	<input type="button" id="button" value="搶購">
</body>
</html>
<script type="text/javascript" src="jqery.js"></script>
<script type="text/javascript">
	window.onload = function(){
		$.ajax({
			url:'./token.php',
			success:function(result){
				$("#token").val(result);
			}
		});
	}
	$("#button").click(function(){
		$.ajax({
			type:"post",
			url:'./addBook.php',
			data:{
				"user":$("#user").val(),
				"token":$("#token").val(),
			},
			success:function(result){
				alert(result);
			}
		})
	})	
</script>

token.php ---生成token,頁面每次載入都要訪問這個檔案來獲取最新token

<?php
session_start();
$token = uniqid() . 'book';
$_SESSION['token'] = $token;
echo $token;
?>

book.sql  --sql檔案。實驗原因就存了一個id一個使用者名稱。資料表最好是InnoDb的支援事務。

SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @[email protected]@CHARACTER_SET_CLIENT */;
/*!40101 SET @[email protected]@CHARACTER_SET_RESULTS */;
/*!40101 SET @[email protected]@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Database: `book`
--
CREATE DATABASE IF NOT EXISTS `book` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `book`;

-- --------------------------------------------------------

--
-- 表的結構 `book`
--

DROP TABLE IF EXISTS `book`;
CREATE TABLE IF NOT EXISTS `book` (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL COMMENT '秒殺使用者',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='庫存表';
COMMIT;