转载:

TP5秒杀高并发教程

介绍

测试环境

创建数据库和表

TP5的配置

编写代码

测试秒杀效果

总结

上一篇文章介绍了安装redis,那么这篇文章就介绍一下利用redis做一个简单高并发案例

win10 64位专业版

小皮面板

tp5.0框架

mysql 高并发生成流水号 mysql 高并发写_ci

先新建名字为redistest的数据库

再将sql语句导入创建数据表和插入数据

/*
Navicat Premium Data Transfer
Source Server : 本地
Source Server Type : MySQL
Source Server Version : 50726
Source Host : localhost:3306
Source Schema : redistest
Target Server Type : MySQL
Target Server Version : 50726
File Encoding : 65001
Date: 29/05/2020 14:55:19
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for ih_goods
-- ----------------------------
DROP TABLE IF EXISTS `ih_goods`;
CREATE TABLE `ih_goods` (
`goods_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`cat_id` int(11) NOT NULL,
`goods_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`goods_id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `ih_goods` VALUES ('1', '0', '小米手机');
-- ----------------------------
-- Table structure for ih_log
-- ----------------------------
DROP TABLE IF EXISTS `ih_log`;
CREATE TABLE `ih_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`event` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`type` tinyint(4) NOT NULL DEFAULT 0,
`addtime` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 16894 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for ih_order
-- ----------------------------
DROP TABLE IF EXISTS `ih_order`;
CREATE TABLE `ih_order` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`order_sn` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`user_id` int(11) NOT NULL,
`status` tinyint(1) UNSIGNED NOT NULL DEFAULT 0,
`goods_id` int(11) NOT NULL,
`sku_id` int(11) UNSIGNED DEFAULT NULL,
`price` decimal(10, 2) DEFAULT NULL,
`addtime` int(11) UNSIGNED DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 2369 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for ih_store
-- ----------------------------
DROP TABLE IF EXISTS `ih_store`;
CREATE TABLE `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) UNSIGNED NOT NULL DEFAULT 0,
`freez` int(11) NOT NULL DEFAULT 0 COMMENT '虚拟库存',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '库存' ROW_FORMAT = Dynamic;
INSERT INTO `ih_store` VALUES ('1', '1', '11', '1000', '0');
-- ----------------------------
-- Table structure for ih_test
-- ----------------------------
DROP TABLE IF EXISTS `ih_test`;
CREATE TABLE `ih_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
INSERT INTO `ih_test` VALUES ('1', '张三');
INSERT INTO `ih_test` VALUES ('2', '王五');

将上面的sql语句导入或者选择redistest数据库选择视图->新建视图,把sql语句放上去再点预览进行导入

mysql 高并发生成流水号 mysql 高并发写_ci_02

先把application/config.php配置的调试打开

‘app_debug’的false改成true

然后配置数据库连接

正确配置 application/database.php里面mysql的账号密码和数据库名字

mysql 高并发生成流水号 mysql 高并发写_ci_03

namespace app\index\controller;
use think\cache\driver\Redis;
use think\Db;
class Index {
public function rudui(){
$store=1000;
$redis=new \Redis();
$redis->connect('127.0.0.1', 6379);
$res=$redis->llen('goods_store');
$count=$store-$res;
for($i=0;$i
$redis->lpush('goods_store',1);
}
}
public function buy () {
//下单前判断redis队列库存量
$redis=new \Redis();
$redis->connect('127.0.0.1',6379);
$count=$redis->lpop('goods_store');
if(!$count){
$this->insertLog('error:no store redis');
return;
}
$price=10;
$user_id=1;
$goods_id=1;
$sku_id=11;
//生成订单
$order_sn=$this->build_order_no();
Db::table('ih_order') ->data(['order_sn'=>$order_sn,'user_id'=>$user_id,'goods_id'=>$goods_id,'sku_id'=>$sku_id,'price'=>$price,'addtime'=>time()])->insert();
$status = Db::table('ih_store')->where('sku_id', $sku_id)->setDec('number');
if($status > 0){

echo "库存减少成功";

$this->insertLog('库存减少成功');

}else{

echo "库存减少成功";

$this->insertLog('库存减少失败');

}

}

//生成唯一订单号

public function build_order_no(){

return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);

}

//记录日志

public function insertLog($event,$type=0){

Db::table('ih_log') ->data(['event'=>$event,'type'=>$type]) ->insert();

}

}

注意:先执行一下rudui方法 进行入列,这个后面整合的话可以初始化这个方法,因为这个入列只需要一次就好 博主这里就分开两个方法进行演示,小伙伴们可以自行进行整合优化

用apache自带的ab压力测试工具进行测试

这里模拟一万个请求5000个并发

ab -n 1000 -c http://www.redis233.com/index.php?s=/index/index/buy

1

2

mysql 高并发生成流水号 mysql 高并发写_高并发_04

mysql 高并发生成流水号 mysql 高并发写_mysql 高并发生成流水号_05

mysql 高并发生成流水号 mysql 高并发写_ci mysql高并发_06

可以看到是成功了!

上述只是简单模拟高并发下的抢购,真实场景要比这复杂很多,很多注意的地方如抢购页面做成静态的,通过ajax调用接口,再如上面的会导致一个用户抢多个,思路:需要一个排队队列和抢购结果队列及库存队列。高并发情况,先将用户进入排队队列,用一个线程循环处理从排队队列取出一个用户,判断用户是否已在抢购结果队列,如果在,则已抢购,否则未抢购,库存减1,写数据库,将用户入结果队列。