京东Java面试被问:秒杀系统的库存扣减和超卖问题解决!
一、秒杀系统核心挑战分析
1. 技术挑战矩阵
text
复制
下载
┌─────────────────┬─────────────────────────────────────┐ │ 挑战维度 │ 具体问题 │ ├─────────────────┼─────────────────────────────────────┤ │ 并发性能 │ 瞬时高并发(10万+ QPS),数据库压力 │ │ 数据一致性 │ 超卖(库存为负)、少卖(库存剩余) │ │ 系统可用性 │ 雪崩效应、服务降级、熔断机制 │ │ 用户体验 │ 排队等待、公平性、响应延迟 │ │ 安全防护 │ 黄牛刷单、脚本攻击、请求伪造 │ └─────────────────┴─────────────────────────────────────┘
2. 库存扣减的四种典型问题
java
复制
下载
// 问题1:超卖(库存为负) // 并发时多个请求读到相同库存,都认为可以购买 // 结果:卖出数量 > 实际库存 // 问题2:少卖(库存剩余) // 悲观锁导致并发性能差,部分请求失败 // 结果:卖出数量 < 实际库存 // 问题3:性能瓶颈 // 数据库行锁竞争激烈,连接池耗尽 // 结果:系统响应变慢,甚至宕机 // 问题4:数据不一致 // 缓存和数据库库存不一致 // 结果:显示有库存但实际无法购买
二、库存扣减解决方案对比
1. 解决方案演化路径
图表
代码
复制
下载
全屏
graph TD
A[简单方案: SQL更新] --> B[悲观锁方案]
B --> C[乐观锁方案]
C --> D[Redis原子操作]
D --> E[分布式锁方案]
E --> F[预扣库存方案]
F --> G[队列削峰方案]
G --> H[分层校验方案]
2. 各方案详细实现
方案1:数据库乐观锁
java
复制
下载
@Service
public class InventoryServiceV1 {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 乐观锁扣减库存
* 优点:实现简单,无锁竞争
* 缺点:高并发下大量失败重试
*/
public boolean deductStockWithOptimisticLock(Long productId, Integer quantity) {
int maxRetries = 3; // 最大重试次数
for (int i = 0; i < maxRetries; i++) {
// 1. 查询当前库存和版本号
String querySql = "SELECT stock, version FROM inventory " +
"WHERE product_id = ? FOR UPDATE";
Map<String, Object> result = jdbcTemplate.queryForMap(
querySql, productId
);
Integer stock = (Integer) result.get("stock");
Integer version = (Integer) result.get("version");
// 2. 检查库存是否充足
if (stock < quantity) {
return false; // 库存不足
}
// 3. 尝试更新(带版本校验)
String updateSql = "UPDATE inventory SET " +
"stock = stock - ?, " +
"version = version + 1 " +
"WHERE product_id = ? " +
"AND version = ? " +
"AND stock >= ?";
int rows = jdbcTemplate.update(updateSql,
quantity, productId, version, quantity
);
// 4. 判断是否更新成功
if (rows > 0) {
// 扣减成功,记录扣减流水
logDeduction(productId, quantity, "SUCCESS");
return true;
}
// 5. 版本冲突,等待后重试
try {
Thread.sleep(50 * (i + 1)); // 指数退避
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
logDeduction(productId, quantity, "FAILED_RETRY_EXCEEDED");
return false;
}
}
方案2:Redis原子操作扣减
java
复制
下载
@Service
public class InventoryServiceV2 {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* Redis Lua脚本原子扣减
* 优点:高性能,原子性保证
* 缺点:需要维护Redis与DB的数据同步
*/
private static final String DEDUCT_SCRIPT =
"local key = KEYS[1] " +
"local quantity = tonumber(ARGV[1]) " +
"local expire = tonumber(ARGV[2]) " +
"local stock = redis.call('get', key) " +
"if not stock then " +
"return -1 " + // key不存在
"end " +
"stock = tonumber(stock) " +
"if stock < quantity then " +
"return -2 " + // 库存不足
"end " +
"redis.call('decrby', key, quantity) " +
"local remaining = redis.call('get', key) " +
// 设置过期时间,防止脏数据
"if expire > 0 then " +
"redis.call('expire', key, expire) " +
"end " +
"return remaining ";
public DeductResult deductStockWithRedis(Long productId, Integer quantity) {
String key = "inventory:" + productId;
String[] keys = new String[]{key};
String[] args = new String[]{
String.valueOf(quantity),
"3600" // 1小时过期
};
// 执行Lua脚本
Long result = (Long) redisTemplate.execute(
new DefaultRedisScript<>(DEDUCT_SCRIPT, Long.class),
keys,
args
);
if (result == null) {
return DeductResult.error("扣减失败");
}
if (result == -1) {
return DeductResult.error("商品不存在");
}
if (result == -2) {
return DeductResult.error("库存不足");
}
// 扣减成功,异步更新数据库
asyncUpdateDatabase(productId, quantity, result);
return DeductResult.success(result);
}
@Async
public void asyncUpdateDatabase(Long productId, Integer quantity, Long remaining) {
try {
// 批量更新,减少数据库压力
String sql = "UPDATE inventory SET " +
"stock = ?, " +
"update_time = NOW() " +
"WHERE product_id = ?";
jdbcTemplate.update(sql, remaining, productId);
// 记录扣减流水
logDeduction(productId, quantity, "REDIS_SUCCESS");
} catch (Exception e) {
log.error("异步更新数据库失败,productId: {}", productId, e);
// 失败补偿:回滚Redis库存
rollbackRedisStock(productId, quantity);
}
}
/**
* 库存预热:活动开始前加载库存到Redis
*/
public void preheatInventory(Long productId, Integer totalStock) {
String key = "inventory:" + productId;
redisTemplate.opsForValue().set(
key,
String.valueOf(totalStock),
2, TimeUnit.HOURS // 设置过期时间
);
// 设置库存售罄标识
String soldOutKey = "sold_out:" + productId;
redisTemplate.opsForValue().set(soldOutKey, "0", 2, TimeUnit.HOURS);
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
方案3:分布式锁+分段库存
java
复制
下载
@Service
public class InventoryServiceV3 {
@Autowired
private RedissonClient redissonClient;
/**
* 分布式锁+库存分段扣减
* 优点:提高并发度,减少锁竞争
*/
public boolean deductStockWithSegmentation(Long productId, Integer quantity) {
// 1. 库存分段:将总库存分成多个段
int segmentSize = 100; // 每段100个库存
int segmentIndex = ThreadLocalRandom.current().nextInt(10);
String segmentKey = String.format("inventory:%d:segment:%d",
productId, segmentIndex);
// 2. 获取分段锁(细粒度锁,减少竞争)
RLock segmentLock = redissonClient.getLock(segmentKey + ":lock");
try {
// 尝试获取锁,最多等待100ms
boolean locked = segmentLock.tryLock(100, 5000, TimeUnit.MILLISECONDS);
if (!locked) {
return false; // 获取锁失败
}
try {
// 3. 检查分段库存
Integer segmentStock = getSegmentStock(segmentKey);
if (segmentStock < quantity) {
// 当前分段库存不足,尝试其他分段
return tryOtherSegments(productId, quantity);
}
// 4. 扣减分段库存
boolean success = deductSegmentStock(segmentKey, quantity);
if (!success) {
return false;
}
// 5. 扣减总库存(异步)
asyncDeductTotalStock(productId, quantity);
return true;
} finally {
segmentLock.unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 库存分桶策略
*/
public void initInventoryBuckets(Long productId, Integer totalStock) {
int bucketCount = 10; // 10个桶
int bucketSize = totalStock / bucketCount;
int remainder = totalStock % bucketCount;
Map<String, Integer> buckets = new HashMap<>();
// 初始化桶库存
for (int i = 0; i < bucketCount; i++) {
String bucketKey = String.format("inventory:%d:bucket:%d",
productId, i);
int stock = bucketSize;
if (i == 0) {
stock += remainder; // 第一个桶包含余数
}
buckets.put(bucketKey, stock);
redisTemplate.opsForValue().set(
bucketKey,
String.valueOf(stock),
2, TimeUnit.HOURS
);
}
// 存储桶映射关系
String mappingKey = "inventory_buckets:" + productId;
redisTemplate.opsForHash().putAll(mappingKey, buckets);
}
}
三、完整秒杀系统架构
1. 分层校验架构设计
图表
代码
复制
下载
全屏
graph TB
subgraph "客户端层"
A[静态资源CDN] --> B[请求频率限制]
B --> C[验证码/滑块]
end
subgraph "网关层"
D[Nginx限流] --> E[API网关]
E --> F[身份认证]
F --> G[请求染色]
end
subgraph "应用层"
H[库存预校验] --> I[队列削峰]
I --> J[令牌桶限流]
J --> K[业务处理]
end
subgraph "数据层"
L[Redis集群] --> M[数据库主从]
M --> N[库存流水表]
end
C --> D
G --> H
K --> L
2. 完整代码实现
java
复制
下载
/**
* 秒杀系统核心服务
*/
@Service
@Slf4j
public class SeckillService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private RedissonClient redissonClient;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Autowired
private InventoryMapper inventoryMapper;
// 分布式锁key
private static final String SECKILL_LOCK_PREFIX = "seckill:lock:";
// 库存key
private static final String STOCK_KEY_PREFIX = "seckill:stock:";
// 已售数量key
private static final String SOLD_KEY_PREFIX = "seckill:sold:";
// 用户购买记录key
private static final String USER_PURCHASE_PREFIX = "seckill:user:";
/**
* 完整秒杀流程
*/
@Transactional(rollbackFor = Exception.class)
public SeckillResponse seckill(SeckillRequest request) {
Long userId = request.getUserId();
Long productId = request.getProductId();
Integer quantity = request.getQuantity();
// === 第1层:基础校验 ===
if (!validateRequest(request)) {
return SeckillResponse.fail("请求参数不合法");
}
// === 第2层:库存预校验 ===
String soldOutKey = "sold_out:" + productId;
if ("1".equals(redisTemplate.opsForValue().get(soldOutKey))) {
return SeckillResponse.fail("商品已售罄");
}
// === 第3层:用户限流 ===
String userLimitKey = USER_PURCHASE_PREFIX + userId + ":" + productId;
if (!checkUserLimit(userId, productId)) {
return SeckillResponse.fail("您已购买过该商品");
}
// === 第4层:库存扣减 ===
DeductResult deductResult = deductStock(productId, quantity);
if (!deductResult.isSuccess()) {
return SeckillResponse.fail(deductResult.getMessage());
}
// === 第5层:生成订单 ===
try {
String orderId = generateOrder(userId, productId, quantity);
// === 第6层:记录购买记录 ===
recordPurchase(userId, productId, orderId);
// === 第7层:异步处理后续逻辑 ===
asyncProcessAfterSeckill(orderId, userId, productId);
return SeckillResponse.success(orderId);
} catch (Exception e) {
log.error("生成订单失败", e);
// 库存回滚
rollbackStock(productId, quantity);
throw new BusinessException("秒杀失败,请重试");
}
}
/**
* 库存扣减核心方法(Redis + DB双写)
*/
private DeductResult deductStock(Long productId, Integer quantity) {
String stockKey = STOCK_KEY_PREFIX + productId;
String lockKey = SECKILL_LOCK_PREFIX + productId;
// 获取分布式锁
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试加锁,最多等待50ms
boolean locked = lock.tryLock(50, 3000, TimeUnit.MILLISECONDS);
if (!locked) {
return DeductResult.error("系统繁忙,请重试");
}
try {
// 1. Redis库存扣减
Long remaining = redisTemplate.opsForValue().decrement(
stockKey, quantity
);
if (remaining == null) {
// 库存key不存在,从数据库加载
Integer dbStock = loadStockFromDB(productId);
if (dbStock == null || dbStock < quantity) {
return DeductResult.error("库存不足");
}
redisTemplate.opsForValue().set(
stockKey,
String.valueOf(dbStock - quantity),
1, TimeUnit.HOURS
);
} else if (remaining < 0) {
// Redis库存不足,回滚
redisTemplate.opsForValue().increment(stockKey, quantity);
return DeductResult.error("库存不足");
}
// 2. 异步更新数据库
CompletableFuture.runAsync(() -> {
updateStockInDB(productId, quantity);
}).exceptionally(e -> {
log.error("更新数据库库存失败", e);
// 失败补偿:记录异常,人工处理
recordStockSyncError(productId, quantity);
return null;
});
// 3. 检查是否售罄
if (remaining - quantity <= 0) {
markAsSoldOut(productId);
}
return DeductResult.success(remaining);
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return DeductResult.error("系统中断");
}
}
/**
* 令牌桶限流算法
*/
@Service
public class TokenBucketRateLimiter {
// 令牌桶:productId -> 令牌数
private final Map<Long, AtomicInteger> tokenBuckets =
new ConcurrentHashMap<>();
// 上次补充时间
private final Map<Long, Long> lastRefillTimes =
new ConcurrentHashMap<>();
/**
* 尝试获取令牌
* @param productId 商品ID
* @param capacity 桶容量
* @param refillRate 每秒补充速率
* @return 是否获取成功
*/
public boolean tryAcquire(Long productId, int capacity, int refillRate) {
long now = System.currentTimeMillis();
// 获取或初始化令牌桶
AtomicInteger bucket = tokenBuckets.computeIfAbsent(
productId, k -> new AtomicInteger(capacity)
);
// 获取上次补充时间
Long lastRefillTime = lastRefillTimes.computeIfAbsent(
productId, k -> now
);
// 计算需要补充的令牌数
long timePassed = now - lastRefillTime;
int tokensToAdd = (int) (timePassed * refillRate / 1000);
if (tokensToAdd > 0) {
// 补充令牌,但不超过容量
int currentTokens = bucket.get();
int newTokens = Math.min(capacity, currentTokens + tokensToAdd);
bucket.set(newTokens);
lastRefillTimes.put(productId, now);
}
// 尝试消费一个令牌
return bucket.getAndDecrement() > 0;
}
}
/**
* 库存预热服务
*/
@Service
public class InventoryPreheatService {
@Scheduled(cron = "0 0/5 * * * ?") // 每5分钟执行一次
public void preheatHotInventory() {
// 查询热点商品
List<HotProduct> hotProducts = findHotProducts();
for (HotProduct product : hotProducts) {
// 1. 加载库存到Redis
preheatToRedis(product);
// 2. 初始化限流器
initRateLimiter(product);
// 3. 预热本地缓存
preheatLocalCache(product);
}
}
private void preheatToRedis(HotProduct product) {
String stockKey = STOCK_KEY_PREFIX + product.getId();
String detailKey = "product:detail:" + product.getId();
// 使用Pipeline批量操作
redisTemplate.executePipelined(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) {
connection.openPipeline();
// 设置库存
connection.stringCommands().set(
stockKey.getBytes(),
String.valueOf(product.getStock()).getBytes()
);
// 设置商品详情
Map<byte[], byte[]> detailMap = new HashMap<>();
detailMap.put("name".getBytes(), product.getName().getBytes());
detailMap.put("price".getBytes(),
String.valueOf(product.getPrice()).getBytes());
connection.hashCommands().hMSet(detailKey.getBytes(), detailMap);
// 设置过期时间
connection.keyCommands().expire(
stockKey.getBytes(),
3600 // 1小时
);
connection.keyCommands().expire(
detailKey.getBytes(),
3600
);
return connection.closePipeline();
}
});
}
}
/**
* 异步订单处理服务
*/
@Service
public class AsyncOrderService {
@Autowired
private ThreadPoolTaskExecutor seckillExecutor;
/**
* 异步处理订单创建
*/
public CompletableFuture<String> createOrderAsync(OrderRequest request) {
return CompletableFuture.supplyAsync(() -> {
// 1. 验证库存(二次确认)
if (!confirmStock(request.getProductId(), request.getQuantity())) {
throw new BusinessException("库存不足");
}
// 2. 创建订单
Order order = buildOrder(request);
// 3. 保存订单
orderMapper.insert(order);
// 4. 扣减库存
inventoryMapper.deductStock(
request.getProductId(),
request.getQuantity()
);
// 5. 发送消息通知
sendOrderCreatedEvent(order);
return order.getOrderId();
}, seckillExecutor).exceptionally(e -> {
log.error("异步创建订单失败", e);
// 发送到死信队列进行补偿
sendToDLQ(request, e.getMessage());
return null;
});
}
/**
* 批量处理订单
*/
public void batchProcessOrders(List<OrderRequest> requests) {
// 使用Disruptor或批量处理框架
List<CompletableFuture<Void>> futures = requests.stream()
.map(request -> CompletableFuture.runAsync(() -> {
try {
processSingleOrder(request);
} catch (Exception e) {
log.error("处理订单失败", e);
}
}, seckillExecutor))
.collect(Collectors.toList());
// 等待所有任务完成
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRun(() -> log.info("批量订单处理完成"))
.exceptionally(e -> {
log.error("批量处理失败", e);
return null;
});
}
}
}
3. 数据库设计
sql
复制
下载
-- 库存表(分库分表) CREATE TABLE `inventory` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `product_id` bigint(20) NOT NULL COMMENT '商品ID', `stock` int(11) NOT NULL DEFAULT '0' COMMENT '总库存', `locked_stock` int(11) NOT NULL DEFAULT '0' COMMENT '锁定库存', `version` int(11) NOT NULL DEFAULT '0' COMMENT '乐观锁版本', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_product_id` (`product_id`), KEY `idx_update_time` (`update_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存表' PARTITION BY HASH(product_id) PARTITIONS 16; -- 分16个分区 -- 库存流水表(记录所有扣减操作) CREATE TABLE `inventory_flow` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `product_id` bigint(20) NOT NULL COMMENT '商品ID', `order_id` varchar(64) NOT NULL COMMENT '订单ID', `change_type` tinyint(4) NOT NULL COMMENT '变更类型:1扣减 2回滚 3增加', `quantity` int(11) NOT NULL COMMENT '变更数量', `before_stock` int(11) NOT NULL COMMENT '变更前库存', `after_stock` int(11) NOT NULL COMMENT '变更后库存', `remark` varchar(255) DEFAULT NULL COMMENT '备注', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_product_id` (`product_id`), KEY `idx_order_id` (`order_id`), KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存流水表'; -- 订单表 CREATE TABLE `seckill_order` ( `order_id` varchar(64) NOT NULL COMMENT '订单号', `user_id` bigint(20) NOT NULL COMMENT '用户ID', `product_id` bigint(20) NOT NULL COMMENT '商品ID', `quantity` int(11) NOT NULL COMMENT '购买数量', `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1待支付 2已支付 3已取消', `amount` decimal(10,2) NOT NULL COMMENT '订单金额', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`order_id`), UNIQUE KEY `uk_user_product` (`user_id`,`product_id`), KEY `idx_user_id` (`user_id`), KEY `idx_product_id` (`product_id`), KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='秒杀订单表'; -- 用户购买限制表 CREATE TABLE `user_purchase_limit` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` bigint(20) NOT NULL, `product_id` bigint(20) NOT NULL, `purchase_count` int(11) NOT NULL DEFAULT '0' COMMENT '已购买数量', `limit_count` int(11) NOT NULL DEFAULT '1' COMMENT '限制数量', `activity_id` bigint(20) NOT NULL COMMENT '活动ID', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_user_product_activity` (`user_id`,`product_id`,`activity_id`), KEY `idx_user_id` (`user_id`), KEY `idx_activity_id` (`activity_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户购买限制表';
4. 监控与降级配置
yaml
复制
下载
# application.yml
spring:
redis:
cluster:
nodes: redis1:6379,redis2:6379,redis3:6379
lettuce:
pool:
max-active: 1000
max-wait: 100ms
max-idle: 100
min-idle: 10
# Sentinel配置
feign:
sentinel:
enabled: true
# Hystrix配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
circuitBreaker:
requestVolumeThreshold: 20
sleepWindowInMilliseconds: 5000
errorThresholdPercentage: 50
# 线程池配置
seckill:
thread-pool:
core-size: 100
max-size: 500
queue-capacity: 1000
keep-alive-seconds: 60
thread-name-prefix: seckill-thread-
# 限流配置
rate-limit:
enabled: true
global:
qps: 10000
api:
seckill:
qps: 1000
order:
qps: 500
四、面试回答要点
问题:如何解决秒杀系统的超卖问题?
回答结构:
-
问题定义
-
"超卖是指卖出数量大于实际库存,主要原因是高并发下多个请求同时读到相同库存"
-
-
解决方案分层
-
前端层:
-
"页面静态化,减少服务端压力"
-
"按钮防重复提交,倒计时控制"
-
"验证码/滑块,防止脚本攻击"
-
-
网关层:
-
"Nginx限流,IP/用户级别限制"
-
"API网关过滤恶意请求"
-
"请求染色,标记不同来源"
-
-
应用层:
-
库存预扣:"Redis原子操作扣减库存"
-
队列削峰:"请求放入队列,异步处理"
-
令牌桶限流:"控制并发处理速率"
-
分层校验:"多层过滤,尽早失败"
-
-
数据层:
-
Redis原子操作:"Lua脚本保证原子性"
-
数据库乐观锁:"版本号控制并发"
-
分段库存:"减少锁竞争"
-
异步同步:"最终一致性保证"
-
-
-
核心代码示例
java
复制 下载// 1. Redis Lua脚本原子扣减 // 2. 分布式锁+库存分段 // 3. 异步订单处理
-
监控与降级
-
"实时监控库存扣减成功率和响应时间"
-
"设置库存告警阈值,自动降级"
-
"熔断机制防止雪崩"
-
-
数据一致性
-
"Redis与DB异步同步"
-
"库存流水记录所有操作"
-
"定时对账,修复差异"
-
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
进阶问题准备
Q1:Redis扣减成功但DB更新失败怎么办?
text
复制
下载
A:采用补偿机制: 1. 记录异常流水到消息队列 2. 定时任务扫描异常记录 3. 人工介入或自动修复 4. 最终一致性保证
Q2:如何保证公平性(先到先得)?
text
复制
下载
A:公平性方案: 1. 排队系统:用户进入队列,按顺序处理 2. 时间戳:精确到毫秒的时间戳排序 3. 预生成令牌:活动开始前分配购买资格 4. 抽签算法:随机但公平的分配
Q3:系统如何应对突增流量?
text
复制
下载
A:弹性伸缩策略: 1. 自动扩缩容:基于CPU/内存/QPS指标 2. 服务降级:关闭非核心功能 3. 流量控制:动态调整限流阈值 4. 缓存预热:提前加载热点数据
Q4:如何防止黄牛刷单?
text
复制
下载
A:风控策略: 1. 设备指纹:识别异常设备 2. 行为分析:检测异常购买模式 3. 关系图谱:识别团伙行为 4. 机器学习:动态调整风控规则
实战经验分享
-
压测数据:"单机Redis可支撑10万+ QPS库存扣减"
-
故障案例:"某次活动因DB连接池耗尽导致超卖"
-
优化效果:"引入分段库存后,TPS提升300%"
-
监控告警:"设置库存水位告警,提前扩容"
总结回答:
"解决秒杀超卖需要从架构设计、技术选型、代码实现、监控运维多个层面综合考虑。核心是通过Redis原子操作保证扣减的原子性,通过队列削峰保护后端系统,通过分层校验尽早过滤无效请求。同时需要完善的监控告警和故障应急方案,确保系统在高并发下的稳定性和数据一致性。"
掌握这些内容,你就能全面回答秒杀系统相关的面试问题了!
更多推荐
所有评论(0)