<?php
/**
* Created by PhpStorm.
* redis 销量超卖秒杀解决方案
* redis 文档:http://doc.redisfans.com/
* ab -n 10000 -c 3000 http://localhost/demo.php 模拟并发
*/
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
//1. 对某一个键加锁,这个键是我们自己设置,起到监视作业
$redis->watch('sales');
//获取销量,清空sales 为0
$sales = $redis->get('sales');
//总库存
$store = 4;
if($sales>=$store){
exit('已经被抢光了'); //跳转活动结束页面
}
//redis事务不会回滚, 开启事务
$redis->multi();
$redis->set('sales',$sales+1); //销量加1
$res = $redis->exec();
if($res){
//减库存
include db.php; //数据库连接
//执行sql ,减库存
}
exit;
=====================redis接口限流============================
非脚本实现
private boolean accessLimit(String ip, int limit, int time, Jedis jedis) {
boolean result = true;
String key = "rate.limit:" + ip;
if (jedis.exists(key)) {
long afterValue = jedis.incr(key);
if (afterValue > limit) {
result = false;
}
} else {
Transaction transaction = jedis.multi();
transaction.incr(key);
transaction.expire(key, time);
transaction.exec();
}
return result;
}
以上代码有两点缺陷
-
- 可能会出现竞态条件: 解决方法是用
WATCH
监控rate.limit:$IP
的变动, 但较为麻烦; - 以上代码在不使用
pipeline
的情况下最多需要向Redis请求5条指令, 传输过多.
- 可能会出现竞态条件: 解决方法是用
Lua脚本实现
Redis 允许将 Lua 脚本传到 Redis 服务器中执行, 脚本内可以调用大部分 Redis 命令, 且 Redis 保证脚本的原子性:
-
- 首先需要准备Lua代码: script.lua
--
-- Created by IntelliJ IDEA.
-- User: jifang
-- Date: 19/8/24
-- Time: 下午6:11
--
local key = "rate.limit:" .. KEYS[1]
local limit = tonumber(ARGV[1])
local expire_time = ARGV[2]
local is_exists = redis.call("EXISTS", key)
if is_exists == 1 then
if redis.call("INCR", key) > limit then
return 0
else
return 1
end
else
redis.call("SET", key, 1)
redis.call("EXPIRE", key, expire_time)
return 1
end
private boolean accessLimit(String ip, int limit, int timeout, Jedis connection) throws IOException {
List<String> keys = Collections.singletonList(ip);
List<String> argv = Arrays.asList(String.valueOf(limit), String.valueOf(timeout));
return 1 == (long) connection.eval(loadScriptString("script.lua"), keys, argv);
}
// 加载Lua代码
private String loadScriptString(String fileName) throws IOException {
Reader reader = new InputStreamReader(Client.class.getClassLoader().getResourceAsStream(fileName));
return CharStreams.toString(reader);
}
Lua 嵌入 Redis 优势:
-
- 减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求, 而脚本只需一次即可, 减少网络传输;
- 原子操作: Redis 将整个脚本作为一个原子执行, 无需担心并发, 也就无需事务;
- 复用: 脚本会永久保存 Redis 中, 其他客户端可继续使用.
以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要
PHP进阶架构师>>>视频、面试文档免费获取shimo.im![834a75949162e2fdc9529c26d35a5a79.png](https://i-blog.csdnimg.cn/blog_migrate/a33bcfe5aba3bf54606afed0479176e5.png)
或 者关注咱们下面的知乎专栏
PHP7进阶架构师zhuanlan.zhihu.com![7193657f412f1204ede77ce2ca973ff8.png](https://i-blog.csdnimg.cn/blog_migrate/3e11e317dd92c6bef93a4cbe8ba192bd.jpeg)