/***
* 减库存
* @return void
*/
public function stockLuaDecr($goodsList = [])
{
//class上面自行引用一下 use Redis;
$redis = new Redis();
#先将用户提交订单的商品列表处理成以下格式
$goodsList = [
[
'nums' => 2,//购买的数量
'redisKey' => 'goods_sku:1'//Redis的key
],
[
'nums' => 2,//购买的数量
'redisKey' => 'goods_sku:2'//Redis的key
],
[
'nums' => 1,//购买的数量
'redisKey' => 'goods_sku:3'//Redis的key
]
];
//这里的<<<是必须的,后面lua大小写都可以(LUA),包括用script(SCRIPT)也一样能执行,
//只需保证头尾标签一致就行
$lua = <<<lua
--KEYS[1]就是eval方法的第二参数传过来的数组,用json_encode做了转换
--lua中数组的下标是从1开始,跟php有区别,KEYS[0]是错误的,都是从KEYS[1]开始,
--相当于eval第二个参数中的数组下标的0
--可以把local理解为js里面的var,就是声明局部变量用的
local data = KEYS[1]
--将json格式转化成数组,同理还可以将数组转化成json用于返回(cjson.encode(数组))
local data = cjson.decode(data)
local success = 1
--k和v就相当于是foreach的key和value,循环的时候可以直接用v["字段名"]
--in pairs(数组) do 是固定写法,结尾用end,if (条件) then 代码 end 也是固定写法
--校验key是否存在
for k, v in pairs(data) do
--如果key不存在,直接返回false,如果想返回具体内容,可以对return返回值进行修改
--,这里不再举例
if (redis.call("exists",v["redisKey"]) == 0)
then
return false
end
end
--减全部购买的库存
for k, v in pairs(data) do
--如果decrby之后的库存变成负数了,就把定义的'success'这个变量改成0
--暂时不用立刻加回来,反正只要有1个失败了就都要回滚
if (redis.call("decrby", v["redisKey"], v["nums"]) < 0)
then
success = 0
end
end
--如果存在库存小于0的商品,再将全部商品库存加回来,相当于回滚操作
if (success == 0)
then
for k, v in pairs(data) do
redis.call("incrby", v["redisKey"], v["nums"])
end
return false
end
return true
lua;
//eval方法第一个参数是脚本,第二个参数是数组,里面放一个json_decode的数组就可了
//第三个参数是第二个参数数组元素的个数,本例中第二个参数数组只有一个元素,所以第三个参数就是1
if (!$redis->eval($lua, [json_encode($goodsList, JSON_UNESCAPED_UNICODE)], 1)) {
return false;
}
return true;
}
/**
* 回滚加库存
* @param $goodsList
* @return void
*/
public function stockLuaIncr($goodsList = [])
{
$redis = new Redis();
$goodsList = [
[
'nums' => 2,
'redisKey' => 'goods_sku:1'
],
[
'nums' => 2,
'redisKey' => 'goods_sku:2'
],
[
'nums' => 1,
'redisKey' => 'goods_sku:3'
]
];
$lua = <<<lua
local data = KEYS[1]
local data = cjson.decode(data)
for k, v in pairs(data) do
redis.call("incrby", v["redisKey"], v["nums"])
end
lua;
$redis->eval($lua, [json_encode($goodsList, JSON_UNESCAPED_UNICODE)], 1);
}
public function createOrder()
{
$goodsList = input('goods_list');
//执行lua库存校验和减库存
if (!$this->stockLuaDecr($goodsList)) {
$this->error('库存不足');
}
try {
//DB层业务操作逻辑
} catch (\Exception $e) {
//如果DB层出问题了,需要把上面减掉的redis库存再加回来
$this->stockLuaIncr($goodsList);
$this->error('下单失败');
}
$this->success('下单成功');
}
$lua = <<<lua
redis.call('lpush', KEYS[1],ARGV[1])//队列1(test:a:)
redis.call('lpush1', KEYS[2],ARGV[2])//队列2(test:b:)
redis.call('lpush', KEYS[3],ARGV[3])//队列3(test:c:)
return 1
lua;
$ret = redis()->eval($lua, ['test:a:', 'test:b:', 'test:c:', date('Y-m-d H:i:s'), date('Y-m-d H:i:s'), date('Y-m-d H:i:s')], 3);
var_dump($ret);
本文为big4ever.com原创文章,转载无需和我联系,但请注明来自www.big4ever.com
最新评论