php+redis实现秒杀,当中有lua脚本
秒杀的步骤
1.设置库存
设置库存直接采用redis set 命令进行模拟
//设置 key 为 SP1001ID 的商品库存为10
set SP1001ID 10
1
2
2用户参与秒杀
用户参与秒杀本质上要做的事情就是,1.记录秒杀成功用户信息;2.商品库存减1;
涉及到的问题:
1.redis链接超时问题
解决方案:设置redis连接池
PHP设置连接池:https://www.cnblogs.com/daizhongxing/p/13840211.html
注意:PHP为脚本语言,脚本执行完毕Redis 连接自动关闭,所以连接池并不能跨脚本使用。
使用连接池的原因:在高并发情况下,频繁地创建和释放 Redis 连接会对性能有较大影响。
连接池的原理:预先创建多个 Redis 连接,在进行 Redis 操作时直接获取已经创建的连接进行操作,操作完成后不会释放,后续其他 Redis 操作可以继续使用。这样就避免了频繁的 Redis 连接和释放。
class RedisFunction
{
private static $connections = array(); //定义一个对象池
private static $servers = array(); //定义redis配置文件
public static function addServer($conf) //定义添加redis配置方法
{
foreach ($conf as $alias => $data){
self::$servers[$alias]=$data;
}
}
public static function getRedis($alias,$select = 0)//两个参数要连接的服务器KEY,要选择的库
{
if(!array_key_exists($alias,self::$connections)){ //判断连接池中是否存在
$redis = new Redis();
$redis->connect(self::$servers[$alias][0],self::$servers[$alias][1]);
self::$connections[$alias]=$redis;
if(isset(self::$servers[$alias][2]) && self::$servers[$alias][2]!=""){
self::$connections[$alias]->auth(self::$servers[$alias][2]);
}
}
self::$connections[$alias]->select($select);
return self::$connections[$alias];
}
}
2.超卖问题
解决方案为:超卖问题 —— 使用Redis的乐观锁机制 也就是设置一个watch事件去监听,然后开始Redis的事务
//对key进行监听
$redis->watch("SP1001ID");
if(!$goodsCount){
return false;
}
//获取用户是否重复秒杀
...
//查看商品库存是否为正
...
//秒杀
//开启事务 组成队列 商品减1 然后添加用户
$trans = $redis->multi();
$decr = $trans->decr("SP1001ID");
$addUser = $trans->sAdd('SP1001USER',$userId);
....
3.库存遗留问题
解决方案为:设置Lua脚本 通过redis直接调用lua脚本,相当于创建了一个不被干扰的队列依次执行脚本里面的redis语句。
lua脚本 保存在redis.lua中
local userId = KEYS[1]
local goodsId = KEYS[2]
local qtKey = 'SP'..goodsId..'ID'
local userKey = 'SP'..goodsId..'USER'
local userExists = redis.call('sismember',userKey,userId)
--判断用户是否存在 如果用户存在则返回状态码2
if tonumber(userExists) == 1 then
return "当前用户已存在"
end
-- --判断商品是否还有库存
local goodsNumber = redis.call('get',qtKey)
if tonumber(goodsNumber) <= 0="" then="">
return "商品库存为空"
else
--进行秒杀操作
redis.call('decr',qtKey)
redis.call('sadd',userKey,userId)
end
return "秒杀成功"
PHP调用
include_once "RedisFunction.php";
$userId = rand(100000,999999).time();
$redisFunction = new RedisFunction();
//使用redis连接池
$conf = array(
'RA' => array('127.0.0.1',6379) //定义Redis配置
);
$redisFunction::addServer($conf); //添加Redis配置
$redis = $redisFunction::getRedis('RA',0); //连接RA,使用默认0库
$redisLua =file_get_contents("./redis.lua");
$sha1 = $redis->script('load',$redisLua);
$res = $redis->evalSha($sha1,array($userId,1001),2);
//或者
//$redis->eval($redisLua,array($userId,1001),2);
通过这样就可以解决小并发下的库存遗留问题,在这里我进行测试的时候发现,当并发量很高的时候PHP会出现进程阻塞影响脚本执行
————————————————
版权声明:本文为CSDN博主「林笑卿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43193813/article/details/122001428