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 @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@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;