2026/2/19 6:01:19
网站建设
项目流程
定制软件下载,外贸网站seo博客,wordpress电子书与公众号,电子商务网站建设有哪些流程图一、秒杀系统核心挑战分析
1. 技术挑战矩阵
text 复制 下载
┌─────────────────┬─────────────────────────────────────┐
│ 挑战维度 │ 具体问题 │
├─────────…一、秒杀系统核心挑战分析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; MapString, 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; } }方案2Redis原子操作扣减java复制下载Service public class InventoryServiceV2 { Autowired private RedisTemplateString, 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; MapString, 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 -- L2. 完整代码实现java复制下载/** * 秒杀系统核心服务 */ Service Slf4j public class SeckillService { Autowired private RedisTemplateString, 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 MapLong, AtomicInteger tokenBuckets new ConcurrentHashMap(); // 上次补充时间 private final MapLong, 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() { // 查询热点商品 ListHotProduct 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 RedisCallbackObject() { Override public Object doInRedis(RedisConnection connection) { connection.openPipeline(); // 设置库存 connection.stringCommands().set( stockKey.getBytes(), String.valueOf(product.getStock()).getBytes() ); // 设置商品详情 Mapbyte[], 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 CompletableFutureString 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(ListOrderRequest requests) { // 使用Disruptor或批量处理框架 ListCompletableFutureVoid 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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需要全套面试笔记及答案【点击此处即可/免费获取】进阶问题准备Q1Redis扣减成功但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原子操作保证扣减的原子性通过队列削峰保护后端系统通过分层校验尽早过滤无效请求。同时需要完善的监控告警和故障应急方案确保系统在高并发下的稳定性和数据一致性。掌握这些内容你就能全面回答秒杀系统相关的面试问题了