一、秒杀系统核心挑战分析

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

四、面试回答要点

问题:如何解决秒杀系统的超卖问题?

回答结构:

  1. 问题定义

    • "超卖是指卖出数量大于实际库存,主要原因是高并发下多个请求同时读到相同库存"

  2. 解决方案分层

    • 前端层:

      • "页面静态化,减少服务端压力"

      • "按钮防重复提交,倒计时控制"

      • "验证码/滑块,防止脚本攻击"

    • 网关层:

      • "Nginx限流,IP/用户级别限制"

      • "API网关过滤恶意请求"

      • "请求染色,标记不同来源"

    • 应用层:

      • 库存预扣:"Redis原子操作扣减库存"

      • 队列削峰:"请求放入队列,异步处理"

      • 令牌桶限流:"控制并发处理速率"

      • 分层校验:"多层过滤,尽早失败"

    • 数据层:

      • Redis原子操作:"Lua脚本保证原子性"

      • 数据库乐观锁:"版本号控制并发"

      • 分段库存:"减少锁竞争"

      • 异步同步:"最终一致性保证"

  3. 核心代码示例

    java

    复制

    下载
    // 1. Redis Lua脚本原子扣减
    // 2. 分布式锁+库存分段
    // 3. 异步订单处理
  4. 监控与降级

    • "实时监控库存扣减成功率和响应时间"

    • "设置库存告警阈值,自动降级"

    • "熔断机制防止雪崩"

  5. 数据一致性

    • "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原子操作保证扣减的原子性,通过队列削峰保护后端系统,通过分层校验尽早过滤无效请求。同时需要完善的监控告警和故障应急方案,确保系统在高并发下的稳定性和数据一致性。"

掌握这些内容,你就能全面回答秒杀系统相关的面试问题了!

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐