redis秒殺簡單實現
阿新 • • 發佈:2018-11-12
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;