← 学习门户 每日面试题 学习看板

2026-09-14 · 高阶题

高阶题 · Vibe Coding / 数据库优化 / 系统设计

3 题 · 平均 3.63/5 · 高阶题

#1 Vibe Coding 进阶 practice

题目

Vibe Coding

我的回答

暂无回答记录

点评

暂无点评

完整讲解

子问题 1:整体架构设计

# 1.1 架构分层

```
用户 → CDN → 接入层(Nginx)→ 应用层(秒杀服务)
↓
缓存层(Redis 集群)
↓
消息层(RocketMQ)
↓
数据层(MySQL 分库分表)
```

**各层职责**:

| 层级 | 职责 | 技术选型 | QPS 承载 |
|------|------|---------|---------|
| CDN | 静态资源、边缘缓存 | Cloudflare/阿里云 CDN | 100 万+ |
| 接入层 | 负载均衡、限流、防刷 | Nginx + Sentinel | 20 万 |
| 应用层 | 业务逻辑、秒杀编排 | Spring Boot | 10 万 |
| 缓存层 | 库存预扣减、热点数据 | Redis Cluster | 50 万 |
| 消息层 | 异步下单、削峰填谷 | RocketMQ | 10 万 |
| 数据层 | 订单存储、最终确认 | MySQL 分库分表 | 5 万 |

# 1.2 核心流程

```
1. 用户点击秒杀 → CDN 边缘节点
2. CDN 检查库存(本地缓存)→ 有库存则放行
3. 接入层限流(Sentinel)→ 超过阈值直接拒绝
4. 应用层校验(用户资格、风控)→ 通过则继续
5. Redis 预扣减库存(Lua 脚本原子操作)
6. 扣减成功 → 发送 MQ 消息(异步下单)
7. MQ 消费者创建订单(写 DB)
8. 支付成功后确认库存
9. 支付超时自动回补库存
```

**时序图**:

```
用户 → CDN → Nginx → 秒杀服务 → Redis → MQ → 订单服务 → MySQL
↓ ↓ ↓ ↓ ↓ ↓
静态缓存 限流 资格校验 扣库存 异步下单 最终确认
```

---

子问题 2:库存扣减方案

# 2.1 Redis 预扣减(核心)

**Lua 脚本(原子扣减)**:

```lua
-- KEYS[1]: stock:{skuId}
-- ARGV[1]: quantity
-- ARGV[2]: userId(防刷,同一用户只能扣一次)

local stock = tonumber(redis.call('GET', KEYS[1]))
if stock == nil then
return -1
end

if stock < tonumber(ARGV[1]) then
return -1
end

local userKey = 'seckill:user:' .. ARGV[2] .. ':' .. KEYS[1]
if redis.call('EXISTS', userKey) == 1 then
return -2
end

redis.call('DECRBY', KEYS[1], ARGV[1])
redis.call('SET', userKey, '1', 'EX', 3600)

return stock - tonumber(ARGV[1])
```

**Java 调用**:

```java
@Service
public class SeckillStockService {

@Autowired
private StringRedisTemplate redisTemplate;

private final DefaultRedisScript<Long> stockScript = new DefaultRedisScript<>(
luaScript, Long.class
);

public boolean preDeductStock(Long skuId, int quantity, Long userId) {
String stockKey = "stock:" + skuId;

Long result = redisTemplate.execute(
stockScript,
Collections.singletonList(stockKey),
String.valueOf(quantity),
String.valueOf(userId)
);

if (result == null || result < 0) {
return false;
}

// 扣减成功,发送 MQ 消息
mqProducer.send("seckill_order", buildMessage(skuId, quantity, userId));
return true;
}
}
```

# 2.2 热点 SKU 优化

**本地缓存 + 分片扣减**:

```java
@Service
public class HotStockService {

private final Cache<Long, AtomicLong> localStockCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(Duration.ofSeconds(5))
.build();

public boolean deductStock(Long skuId, int quantity, Long userId) {
// 1. 先查本地缓存(热点 SKU)
AtomicLong localStock = localStockCache.getIfPresent(skuId);
if (localStock != null) {
while (true) {
long current = localStock.get();
if (current < quantity) {
return false;
}
if (localStock.compareAndSet(current, current - quantity)) {
asyncSyncToRedis(skuId, quantity);
return true;
}
}
}

// 2. 本地缓存 miss,扣 Redis
return preDeductStock(skuId, quantity, userId);
}

@Async
public void asyncSyncToRedis(Long skuId, int quantity) {
redisTemplate.opsForValue().decrement("stock:" + skuId, quantity);
}

@Scheduled(fixedDelay = 5000)
public void loadHotStock() {
List<Long> hotSkuIds = redisTemplate.keys("stock:*");
for (Long skuId : hotSkuIds) {
Long stock = redisTemplate.opsForValue().get("stock:" + skuId);
localStockCache.put(skuId, new AtomicLong(stock));
}
}
}
```

---

子问题 3:防超卖 + 兜底方案

# 3.1 多层防超卖

```
┌─────────────────────────────────────┐
│ Layer 1: 前端防刷 │
│ - 按钮置灰、验证码、滑块验证 │
├─────────────────────────────────────┤
│ Layer 2: CDN 边缘限流 │
│ - 按 IP 限流、按用户限流 │
├─────────────────────────────────────┤
│ Layer 3: 接入层限流(Sentinel) │
│ - QPS 限流、并发数限流 │
├─────────────────────────────────────┤
│ Layer 4: 应用层校验 │
│ - 用户资格、风控、黑名单 │
├─────────────────────────────────────┤
│ Layer 5: Redis 原子扣减 │
│ - Lua 脚本保证原子性 │
├─────────────────────────────────────┤
│ Layer 6: DB 乐观锁兜底 │
│ - version 字段防止超卖 │
└─────────────────────────────────────┘
```

# 3.2 接入层限流(Sentinel)

```java
@SentinelResource(
value = "seckill",
fallback = "seckillFallback",
blockHandler = "seckillBlockHandler",
flowControl = @FlowControl(
count = 100000, // QPS 限制 10 万
grade = 1, // QPS 模式
controlBehavior = 0 // 直接拒绝
)
)
public Result seckill(Long skuId, Long userId) {
return seckillService.doSeckill(skuId, userId);
}

public Result seckillBlockHandler(Long skuId, Long userId, BlockException ex) {
return Result.fail("系统繁忙,请稍后再试");
}

public Result seckillFallback(Long skuId, Long userId, Throwable ex) {
log.error("Seckill fallback", ex);
return Result.fail("系统异常");
}
```

# 3.3 DB 乐观锁兜底

```java
@Transactional
public void createOrder(Long skuId, int quantity, Long userId) {
// 1. 查询库存
Stock stock = stockDao.selectById(skuId);

// 2. 检查库存
if (stock.getStock() < quantity) {
throw new InsufficientStockException("库存不足");
}

// 3. 乐观锁扣减
int affected = stockDao.deductStockWithVersion(
skuId, quantity, stock.getVersion()
);

if (affected == 0) {
throw new OptimisticLockException("并发冲突");
}

// 4. 创建订单
orderDao.insert(buildOrder(skuId, quantity, userId));
}
```

# 3.4 兜底方案

**缓存雪崩防护**:

```java
// 1. 随机过期时间
public void setStockWithRandomExpire(Long skuId, long stock) {
String key = "stock:" + skuId;
long baseExpire = 7200;
long randomExpire = baseExpire + ThreadLocalRandom.current().nextLong(600);
redisTemplate.opsForValue().set(key, stock, randomExpire, TimeUnit.SECONDS);
}

// 2. 热点 key 永不过期(本地缓存兜底)
@Scheduled(fixedDelay = 3600000)
public void refreshHotKeys() {
Set<String> keys = redisTemplate.keys("stock:*");
for (String key : keys) {
redisTemplate.expire(key, 4, TimeUnit.HOURS);
}
}
```

**限流 + 熔断 + 降级**:

```java
@SentinelResource(
value = "seckill",
flowControl = @FlowControl(count = 100000, grade = 1),
circuitBreaker = @SentinelCircuitBreaker(
slowRatioThreshold = 0.5,
slowRatioMaxRt = 100,
minRequestAmount = 10,
timeWindow = 10
)
)
```

**对账 + 自动修复**:

```java
@Scheduled(cron = "0 0 * * * ?")
public void reconcileStock() {
List<Long> skuIds = stockDao.selectAllSkuIds();
for (Long skuId : skuIds) {
Long redisStock = redisTemplate.opsForValue().get("stock:" + skuId);
Long dbStock = stockDao.getStock(skuId);

if (redisStock != null && !redisStock.equals(dbStock)) {
redisTemplate.opsForValue().set(
"stock:" + skuId, dbStock, 4, TimeUnit.HOURS
);
}
}
}
```

**库存回补(支付超时)**:

```java
@Scheduled(fixedDelay = 60000)
public void rollbackTimeoutOrders() {
List<Order> timeoutOrders = orderDao.selectTimeout(30, TimeUnit.MINUTES);
for (Order order : timeoutOrders) {
redisTemplate.opsForValue().increment(
"stock:" + order.getSkuId(), order.getQuantity()
);
orderDao.updateStatus(order.getId(), OrderStatus.CANCELLED);
}
}
```

---

#2 数据库优化 进阶 practice

题目

数据库优化

我的回答

暂无回答记录

点评

暂无点评

完整讲解

子问题 1:整体架构设计

# 1.1 架构分层

```
用户 → CDN → 接入层(Nginx)→ 应用层(秒杀服务)
↓
缓存层(Redis 集群)
↓
消息层(RocketMQ)
↓
数据层(MySQL 分库分表)
```

**各层职责**:

| 层级 | 职责 | 技术选型 | QPS 承载 |
|------|------|---------|---------|
| CDN | 静态资源、边缘缓存 | Cloudflare/阿里云 CDN | 100 万+ |
| 接入层 | 负载均衡、限流、防刷 | Nginx + Sentinel | 20 万 |
| 应用层 | 业务逻辑、秒杀编排 | Spring Boot | 10 万 |
| 缓存层 | 库存预扣减、热点数据 | Redis Cluster | 50 万 |
| 消息层 | 异步下单、削峰填谷 | RocketMQ | 10 万 |
| 数据层 | 订单存储、最终确认 | MySQL 分库分表 | 5 万 |

# 1.2 核心流程

```
1. 用户点击秒杀 → CDN 边缘节点
2. CDN 检查库存(本地缓存)→ 有库存则放行
3. 接入层限流(Sentinel)→ 超过阈值直接拒绝
4. 应用层校验(用户资格、风控)→ 通过则继续
5. Redis 预扣减库存(Lua 脚本原子操作)
6. 扣减成功 → 发送 MQ 消息(异步下单)
7. MQ 消费者创建订单(写 DB)
8. 支付成功后确认库存
9. 支付超时自动回补库存
```

**时序图**:

```
用户 → CDN → Nginx → 秒杀服务 → Redis → MQ → 订单服务 → MySQL
↓ ↓ ↓ ↓ ↓ ↓
静态缓存 限流 资格校验 扣库存 异步下单 最终确认
```

---

子问题 2:库存扣减方案

# 2.1 Redis 预扣减(核心)

**Lua 脚本(原子扣减)**:

```lua
-- KEYS[1]: stock:{skuId}
-- ARGV[1]: quantity
-- ARGV[2]: userId(防刷,同一用户只能扣一次)

local stock = tonumber(redis.call('GET', KEYS[1]))
if stock == nil then
return -1
end

if stock < tonumber(ARGV[1]) then
return -1
end

local userKey = 'seckill:user:' .. ARGV[2] .. ':' .. KEYS[1]
if redis.call('EXISTS', userKey) == 1 then
return -2
end

redis.call('DECRBY', KEYS[1], ARGV[1])
redis.call('SET', userKey, '1', 'EX', 3600)

return stock - tonumber(ARGV[1])
```

**Java 调用**:

```java
@Service
public class SeckillStockService {

@Autowired
private StringRedisTemplate redisTemplate;

private final DefaultRedisScript<Long> stockScript = new DefaultRedisScript<>(
luaScript, Long.class
);

public boolean preDeductStock(Long skuId, int quantity, Long userId) {
String stockKey = "stock:" + skuId;

Long result = redisTemplate.execute(
stockScript,
Collections.singletonList(stockKey),
String.valueOf(quantity),
String.valueOf(userId)
);

if (result == null || result < 0) {
return false;
}

// 扣减成功,发送 MQ 消息
mqProducer.send("seckill_order", buildMessage(skuId, quantity, userId));
return true;
}
}
```

# 2.2 热点 SKU 优化

**本地缓存 + 分片扣减**:

```java
@Service
public class HotStockService {

private final Cache<Long, AtomicLong> localStockCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(Duration.ofSeconds(5))
.build();

public boolean deductStock(Long skuId, int quantity, Long userId) {
// 1. 先查本地缓存(热点 SKU)
AtomicLong localStock = localStockCache.getIfPresent(skuId);
if (localStock != null) {
while (true) {
long current = localStock.get();
if (current < quantity) {
return false;
}
if (localStock.compareAndSet(current, current - quantity)) {
asyncSyncToRedis(skuId, quantity);
return true;
}
}
}

// 2. 本地缓存 miss,扣 Redis
return preDeductStock(skuId, quantity, userId);
}

@Async
public void asyncSyncToRedis(Long skuId, int quantity) {
redisTemplate.opsForValue().decrement("stock:" + skuId, quantity);
}

@Scheduled(fixedDelay = 5000)
public void loadHotStock() {
List<Long> hotSkuIds = redisTemplate.keys("stock:*");
for (Long skuId : hotSkuIds) {
Long stock = redisTemplate.opsForValue().get("stock:" + skuId);
localStockCache.put(skuId, new AtomicLong(stock));
}
}
}
```

---

子问题 3:防超卖 + 兜底方案

# 3.1 多层防超卖

```
┌─────────────────────────────────────┐
│ Layer 1: 前端防刷 │
│ - 按钮置灰、验证码、滑块验证 │
├─────────────────────────────────────┤
│ Layer 2: CDN 边缘限流 │
│ - 按 IP 限流、按用户限流 │
├─────────────────────────────────────┤
│ Layer 3: 接入层限流(Sentinel) │
│ - QPS 限流、并发数限流 │
├─────────────────────────────────────┤
│ Layer 4: 应用层校验 │
│ - 用户资格、风控、黑名单 │
├─────────────────────────────────────┤
│ Layer 5: Redis 原子扣减 │
│ - Lua 脚本保证原子性 │
├─────────────────────────────────────┤
│ Layer 6: DB 乐观锁兜底 │
│ - version 字段防止超卖 │
└─────────────────────────────────────┘
```

# 3.2 接入层限流(Sentinel)

```java
@SentinelResource(
value = "seckill",
fallback = "seckillFallback",
blockHandler = "seckillBlockHandler",
flowControl = @FlowControl(
count = 100000, // QPS 限制 10 万
grade = 1, // QPS 模式
controlBehavior = 0 // 直接拒绝
)
)
public Result seckill(Long skuId, Long userId) {
return seckillService.doSeckill(skuId, userId);
}

public Result seckillBlockHandler(Long skuId, Long userId, BlockException ex) {
return Result.fail("系统繁忙,请稍后再试");
}

public Result seckillFallback(Long skuId, Long userId, Throwable ex) {
log.error("Seckill fallback", ex);
return Result.fail("系统异常");
}
```

# 3.3 DB 乐观锁兜底

```java
@Transactional
public void createOrder(Long skuId, int quantity, Long userId) {
// 1. 查询库存
Stock stock = stockDao.selectById(skuId);

// 2. 检查库存
if (stock.getStock() < quantity) {
throw new InsufficientStockException("库存不足");
}

// 3. 乐观锁扣减
int affected = stockDao.deductStockWithVersion(
skuId, quantity, stock.getVersion()
);

if (affected == 0) {
throw new OptimisticLockException("并发冲突");
}

// 4. 创建订单
orderDao.insert(buildOrder(skuId, quantity, userId));
}
```

# 3.4 兜底方案

**缓存雪崩防护**:

```java
// 1. 随机过期时间
public void setStockWithRandomExpire(Long skuId, long stock) {
String key = "stock:" + skuId;
long baseExpire = 7200;
long randomExpire = baseExpire + ThreadLocalRandom.current().nextLong(600);
redisTemplate.opsForValue().set(key, stock, randomExpire, TimeUnit.SECONDS);
}

// 2. 热点 key 永不过期(本地缓存兜底)
@Scheduled(fixedDelay = 3600000)
public void refreshHotKeys() {
Set<String> keys = redisTemplate.keys("stock:*");
for (String key : keys) {
redisTemplate.expire(key, 4, TimeUnit.HOURS);
}
}
```

**限流 + 熔断 + 降级**:

```java
@SentinelResource(
value = "seckill",
flowControl = @FlowControl(count = 100000, grade = 1),
circuitBreaker = @SentinelCircuitBreaker(
slowRatioThreshold = 0.5,
slowRatioMaxRt = 100,
minRequestAmount = 10,
timeWindow = 10
)
)
```

**对账 + 自动修复**:

```java
@Scheduled(cron = "0 0 * * * ?")
public void reconcileStock() {
List<Long> skuIds = stockDao.selectAllSkuIds();
for (Long skuId : skuIds) {
Long redisStock = redisTemplate.opsForValue().get("stock:" + skuId);
Long dbStock = stockDao.getStock(skuId);

if (redisStock != null && !redisStock.equals(dbStock)) {
redisTemplate.opsForValue().set(
"stock:" + skuId, dbStock, 4, TimeUnit.HOURS
);
}
}
}
```

**库存回补(支付超时)**:

```java
@Scheduled(fixedDelay = 60000)
public void rollbackTimeoutOrders() {
List<Order> timeoutOrders = orderDao.selectTimeout(30, TimeUnit.MINUTES);
for (Order order : timeoutOrders) {
redisTemplate.opsForValue().increment(
"stock:" + order.getSkuId(), order.getQuantity()
);
orderDao.updateStatus(order.getId(), OrderStatus.CANCELLED);
}
}
```

---

#3 系统设计 高阶 practice

题目

系统设计

我的回答

暂无回答记录

点评

暂无点评

完整讲解

子问题 1:整体架构设计

# 1.1 架构分层

```
用户 → CDN → 接入层(Nginx)→ 应用层(秒杀服务)
↓
缓存层(Redis 集群)
↓
消息层(RocketMQ)
↓
数据层(MySQL 分库分表)
```

**各层职责**:

| 层级 | 职责 | 技术选型 | QPS 承载 |
|------|------|---------|---------|
| CDN | 静态资源、边缘缓存 | Cloudflare/阿里云 CDN | 100 万+ |
| 接入层 | 负载均衡、限流、防刷 | Nginx + Sentinel | 20 万 |
| 应用层 | 业务逻辑、秒杀编排 | Spring Boot | 10 万 |
| 缓存层 | 库存预扣减、热点数据 | Redis Cluster | 50 万 |
| 消息层 | 异步下单、削峰填谷 | RocketMQ | 10 万 |
| 数据层 | 订单存储、最终确认 | MySQL 分库分表 | 5 万 |

# 1.2 核心流程

```
1. 用户点击秒杀 → CDN 边缘节点
2. CDN 检查库存(本地缓存)→ 有库存则放行
3. 接入层限流(Sentinel)→ 超过阈值直接拒绝
4. 应用层校验(用户资格、风控)→ 通过则继续
5. Redis 预扣减库存(Lua 脚本原子操作)
6. 扣减成功 → 发送 MQ 消息(异步下单)
7. MQ 消费者创建订单(写 DB)
8. 支付成功后确认库存
9. 支付超时自动回补库存
```

**时序图**:

```
用户 → CDN → Nginx → 秒杀服务 → Redis → MQ → 订单服务 → MySQL
↓ ↓ ↓ ↓ ↓ ↓
静态缓存 限流 资格校验 扣库存 异步下单 最终确认
```

---

子问题 2:库存扣减方案

# 2.1 Redis 预扣减(核心)

**Lua 脚本(原子扣减)**:

```lua
-- KEYS[1]: stock:{skuId}
-- ARGV[1]: quantity
-- ARGV[2]: userId(防刷,同一用户只能扣一次)

local stock = tonumber(redis.call('GET', KEYS[1]))
if stock == nil then
return -1
end

if stock < tonumber(ARGV[1]) then
return -1
end

local userKey = 'seckill:user:' .. ARGV[2] .. ':' .. KEYS[1]
if redis.call('EXISTS', userKey) == 1 then
return -2
end

redis.call('DECRBY', KEYS[1], ARGV[1])
redis.call('SET', userKey, '1', 'EX', 3600)

return stock - tonumber(ARGV[1])
```

**Java 调用**:

```java
@Service
public class SeckillStockService {

@Autowired
private StringRedisTemplate redisTemplate;

private final DefaultRedisScript<Long> stockScript = new DefaultRedisScript<>(
luaScript, Long.class
);

public boolean preDeductStock(Long skuId, int quantity, Long userId) {
String stockKey = "stock:" + skuId;

Long result = redisTemplate.execute(
stockScript,
Collections.singletonList(stockKey),
String.valueOf(quantity),
String.valueOf(userId)
);

if (result == null || result < 0) {
return false;
}

// 扣减成功,发送 MQ 消息
mqProducer.send("seckill_order", buildMessage(skuId, quantity, userId));
return true;
}
}
```

# 2.2 热点 SKU 优化

**本地缓存 + 分片扣减**:

```java
@Service
public class HotStockService {

private final Cache<Long, AtomicLong> localStockCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(Duration.ofSeconds(5))
.build();

public boolean deductStock(Long skuId, int quantity, Long userId) {
// 1. 先查本地缓存(热点 SKU)
AtomicLong localStock = localStockCache.getIfPresent(skuId);
if (localStock != null) {
while (true) {
long current = localStock.get();
if (current < quantity) {
return false;
}
if (localStock.compareAndSet(current, current - quantity)) {
asyncSyncToRedis(skuId, quantity);
return true;
}
}
}

// 2. 本地缓存 miss,扣 Redis
return preDeductStock(skuId, quantity, userId);
}

@Async
public void asyncSyncToRedis(Long skuId, int quantity) {
redisTemplate.opsForValue().decrement("stock:" + skuId, quantity);
}

@Scheduled(fixedDelay = 5000)
public void loadHotStock() {
List<Long> hotSkuIds = redisTemplate.keys("stock:*");
for (Long skuId : hotSkuIds) {
Long stock = redisTemplate.opsForValue().get("stock:" + skuId);
localStockCache.put(skuId, new AtomicLong(stock));
}
}
}
```

---

子问题 3:防超卖 + 兜底方案

# 3.1 多层防超卖

```
┌─────────────────────────────────────┐
│ Layer 1: 前端防刷 │
│ - 按钮置灰、验证码、滑块验证 │
├─────────────────────────────────────┤
│ Layer 2: CDN 边缘限流 │
│ - 按 IP 限流、按用户限流 │
├─────────────────────────────────────┤
│ Layer 3: 接入层限流(Sentinel) │
│ - QPS 限流、并发数限流 │
├─────────────────────────────────────┤
│ Layer 4: 应用层校验 │
│ - 用户资格、风控、黑名单 │
├─────────────────────────────────────┤
│ Layer 5: Redis 原子扣减 │
│ - Lua 脚本保证原子性 │
├─────────────────────────────────────┤
│ Layer 6: DB 乐观锁兜底 │
│ - version 字段防止超卖 │
└─────────────────────────────────────┘
```

# 3.2 接入层限流(Sentinel)

```java
@SentinelResource(
value = "seckill",
fallback = "seckillFallback",
blockHandler = "seckillBlockHandler",
flowControl = @FlowControl(
count = 100000, // QPS 限制 10 万
grade = 1, // QPS 模式
controlBehavior = 0 // 直接拒绝
)
)
public Result seckill(Long skuId, Long userId) {
return seckillService.doSeckill(skuId, userId);
}

public Result seckillBlockHandler(Long skuId, Long userId, BlockException ex) {
return Result.fail("系统繁忙,请稍后再试");
}

public Result seckillFallback(Long skuId, Long userId, Throwable ex) {
log.error("Seckill fallback", ex);
return Result.fail("系统异常");
}
```

# 3.3 DB 乐观锁兜底

```java
@Transactional
public void createOrder(Long skuId, int quantity, Long userId) {
// 1. 查询库存
Stock stock = stockDao.selectById(skuId);

// 2. 检查库存
if (stock.getStock() < quantity) {
throw new InsufficientStockException("库存不足");
}

// 3. 乐观锁扣减
int affected = stockDao.deductStockWithVersion(
skuId, quantity, stock.getVersion()
);

if (affected == 0) {
throw new OptimisticLockException("并发冲突");
}

// 4. 创建订单
orderDao.insert(buildOrder(skuId, quantity, userId));
}
```

# 3.4 兜底方案

**缓存雪崩防护**:

```java
// 1. 随机过期时间
public void setStockWithRandomExpire(Long skuId, long stock) {
String key = "stock:" + skuId;
long baseExpire = 7200;
long randomExpire = baseExpire + ThreadLocalRandom.current().nextLong(600);
redisTemplate.opsForValue().set(key, stock, randomExpire, TimeUnit.SECONDS);
}

// 2. 热点 key 永不过期(本地缓存兜底)
@Scheduled(fixedDelay = 3600000)
public void refreshHotKeys() {
Set<String> keys = redisTemplate.keys("stock:*");
for (String key : keys) {
redisTemplate.expire(key, 4, TimeUnit.HOURS);
}
}
```

**限流 + 熔断 + 降级**:

```java
@SentinelResource(
value = "seckill",
flowControl = @FlowControl(count = 100000, grade = 1),
circuitBreaker = @SentinelCircuitBreaker(
slowRatioThreshold = 0.5,
slowRatioMaxRt = 100,
minRequestAmount = 10,
timeWindow = 10
)
)
```

**对账 + 自动修复**:

```java
@Scheduled(cron = "0 0 * * * ?")
public void reconcileStock() {
List<Long> skuIds = stockDao.selectAllSkuIds();
for (Long skuId : skuIds) {
Long redisStock = redisTemplate.opsForValue().get("stock:" + skuId);
Long dbStock = stockDao.getStock(skuId);

if (redisStock != null && !redisStock.equals(dbStock)) {
redisTemplate.opsForValue().set(
"stock:" + skuId, dbStock, 4, TimeUnit.HOURS
);
}
}
}
```

**库存回补(支付超时)**:

```java
@Scheduled(fixedDelay = 60000)
public void rollbackTimeoutOrders() {
List<Order> timeoutOrders = orderDao.selectTimeout(30, TimeUnit.MINUTES);
for (Order order : timeoutOrders) {
redisTemplate.opsForValue().increment(
"stock:" + order.getSkuId(), order.getQuantity()
);
orderDao.updateStatus(order.getId(), OrderStatus.CANCELLED);
}
}
```

---

📚 查看全部面试记录