您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# PHP中怎么利用Redis实现电商秒杀功能
## 一、秒杀场景的技术挑战
电商秒杀活动(如双11、618)通常面临三大技术难题:
1. **瞬时高并发**:数万QPS的访问压力
2. **库存超卖**:共享资源竞争导致数据不一致
3. **系统过载**:数据库可能成为性能瓶颈
传统MySQL方案难以应对,而Redis凭借以下特性成为理想解决方案:
- 单机10万+ QPS性能
- 原子操作保证数据一致性
- 丰富的数据结构支持
## 二、核心实现方案
### 1. 系统架构设计
用户请求 → 负载均衡 → PHP服务层 → Redis集群 → MySQL(异步同步)
### 2. Redis关键数据结构
```php
// 商品库存预加载
$redis->set('seckill:sku:123:stock', 100);
// 已购用户记录
$redis->sAdd('seckill:sku:123:users', $userID);
function handleSeckill($userId, $skuId) {
$redis = new Redis();
// 1. 校验用户是否重复购买
if ($redis->sIsMember("seckill:{$skuId}:users", $userId)) {
return ['code' => 400, 'msg' => '已参与活动'];
}
// 2. 原子性扣减库存
$remaining = $redis->decr("seckill:{$skuId}:stock");
if ($remaining < 0) {
$redis->incr("seckill:{$skuId}:stock"); // 回滚
return ['code' => 400, 'msg' => '已售罄'];
}
// 3. 记录购买行为
$redis->sAdd("seckill:{$skuId}:users", $userId);
// 4. 异步处理订单(RabbitMQ)
sendToQueue(['user_id' => $userId, 'sku_id' => $skuId]);
return ['code' => 200, 'msg' => '秒杀成功'];
}
令牌桶限流:
$rateLimiter = new TokenBucket(1000); // 每秒1000个令牌
if (!$rateLimiter->consume(1)) {
header('HTTP/1.1 429 Too Many Requests');
exit;
}
队列缓冲:使用Redis List作为排队系统
$redis->lPush('seckill:queue', json_encode(['user_id' => $userId, 'sku_id' => $skuId]));
// 将库存拆分为多个子库存
for ($i = 0; $i < 10; $i++) {
$redis->set("seckill:{$skuId}:stock_{$i}", 10);
}
// 随机选择子库存扣减
$slot = mt_rand(0, 9);
$redis->decr("seckill:{$skuId}:stock_{$slot}");
// IP限频(60秒内最多5次)
$key = "seckill:ip:" . md5($_SERVER['REMOTE_ADDR']);
if ($redis->incr($key) > 5 && $redis->ttl($key) > 0) {
return ['code' => 403, 'msg' => '操作过于频繁'];
}
$redis->expire($key, 60);
库存回滚机制:
try {
// 业务代码...
} catch (Exception $e) {
$redis->incr("seckill:{$skuId}:stock");
$redis->sRem("seckill:{$skuId}:users", $userId);
}
Redis集群方案:
方案 | QPS | 成功率 |
---|---|---|
纯MySQL | 1,200 | 68% |
Redis+队列 | 24,000 | 99.5% |
Redis+Lua脚本 | 38,000 | 99.9% |
<?php
class SeckillService {
private $redis;
public function __construct() {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}
public function process(Request $request) {
// 参数校验、限流等...
$luaScript = <<<LUA
local stockKey = KEYS[1]
local userKey = KEYS[2]
local userId = ARGV[1]
if redis.call('SISMEMBER', userKey, userId) == 1 then
return 2
end
local stock = redis.call('DECR', stockKey)
if stock < 0 then
return 0
end
redis.call('SADD', userKey, userId)
return 1
LUA;
$result = $this->redis->eval(
$luaScript,
[
"seckill:{$skuId}:stock",
"seckill:{$skuId}:users",
$userId
],
2
);
// 处理Lua脚本返回结果...
}
}
必做项:
推荐项:
扩展方向:
”`
实际部署时建议使用:Redis 6.2+版本、PHPRedis扩展7.0+、连接池配置(如Swoole)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。