redis利用lua脚本实现秒杀
时间: 2025-06-08 19:54:55 浏览: 36
### 使用 Redis 和 Lua 脚本实现秒杀功能
#### 思路概述
Redis 是一种高性能的键值存储数据库,支持原子操作和事务处理。通过结合 Lua 脚本,可以确保多个并发请求在同一时间内的执行逻辑一致,从而有效防止超卖现象的发生[^1]。
Lua 脚本可以在 Redis 中运行,并且具有原子性特性,这意味着整个脚本会在单线程环境中一次性完成,不会被其他命令打断。因此,在高并发场景下的秒杀活动可以通过 Lua 脚本来控制库存减少以及订单生成的过程[^2]。
---
#### 架构设计
为了实现完整的秒杀系统,通常会采用以下技术栈:
- **前端**:提供用户界面供客户参与秒杀。
- **后端服务**:基于 Spring Boot 开发接口,负责接收用户的请求并调用 Redis 进行业务逻辑处理。
- **数据层**:
- **Redis**:用于缓存商品库存信息、限流计数器等高频访问的数据。
- **MySQL**:作为持久化存储,保存最终的商品销售记录和订单详情。
表结构建议如下:
1. **产品表 (Product)**
| 字段名 | 类型 | 描述 |
|--------------|--------------|----------------|
| id | INT | 商品ID |
| name | VARCHAR(255) | 商品名称 |
| stock | INT | 库存数量 |
2. **订单表 (Order)**
| 字段名 | 类型 | 描述 |
|--------------|--------------|------------------|
| order_id | BIGINT | 订单唯一标识 |
| product_id | INT | 关联的产品ID |
| user_id | INT | 下单用户ID |
| create_time | DATETIME | 创建时间 |
---
#### 配置文件与依赖项
以下是 Maven `pom.xml` 文件中的部分依赖配置:
```xml
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- MyBatis Integration -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
```
---
#### 核心业务代码展示
##### 1. Lua 脚本编写 (`sha_2.lua`)
该脚本的主要作用是从 Redis 缓存中获取当前商品的剩余库存量,并判断是否允许下单。如果允许,则扣除库存并将订单信息写入队列以便后续同步至 MySQL 数据库。
```lua
local key = KEYS[1]
local userId = ARGV[1]
-- 获取当前库存
local stock = tonumber(redis.call('get', key))
if stock and stock > 0 then
-- 扣除库存
redis.call('decrby', key, 1)
-- 设置已购买标志(避免重复提交)
local purchasedKey = 'user:' .. userId .. ':bought'
if not redis.call('exists', purchasedKey) then
redis.call('setex', purchasedKey, 60, 1) -- 用户限购标记有效期为60秒
return 1 -- 返回成功状态码
end
end
return 0 -- 失败或无库存
```
##### 2. Java 控制器代码
在 Spring Boot 中定义一个控制器方法来触发 Redis 的 Lua 脚本执行。
```java
@RestController
@RequestMapping("/api/seckill")
public class SeckillController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Value("${lua.script.path}")
private String luaScriptPath;
/**
* 秒杀接口
*/
@PostMapping("/{productId}")
public ResponseEntity<String> seckill(@PathVariable Long productId,
@RequestParam Long userId) {
try {
// 加载 Lua 脚本
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setLocation(new ClassPathResource(luaScriptPath));
script.setResultType(Long.class);
List<String> keys = Arrays.asList("product:" + productId);
Object result = stringRedisTemplate.execute(script, keys, userId.toString());
if ("1".equals(result)) {
return ResponseEntity.ok("秒杀成功!");
}
} catch (Exception e) {
log.error("秒杀失败", e);
}
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("秒杀失败或库存不足!");
}
}
```
---
#### 测试方案
使用 JMeter 工具模拟大量并发请求验证系统的稳定性和性能表现。需要注意的是,在实际部署前应充分考虑以下几个方面可能存在的问题:
1. 如果未正确初始化 Redis 中的关键字及其对应的初始值可能导致异常行为发生[^3];
2. 当存在语法错误或者逻辑缺陷时可能会引发程序崩溃等问题[^4];
---
###
阅读全文
相关推荐


















