Java面试系统设计案例

wen java案例 1

本文目录导读:

Java面试系统设计案例

  1. 系统需求分析
  2. 架构设计
  3. 核心技术实现
  4. 数据库优化设计
  5. 流量削峰策略
  6. 面试核心考点总结
  7. 实战建议

我来为你提供一个完整的高并发秒杀系统设计案例,这是Java面试中最经典的系统设计题目之一。

系统需求分析

核心业务场景

  • 限时抢购商品
  • 高并发访问(可能达到10万+ QPS)
  • 超卖问题必须避免
  • 系统需要稳定可靠

核心功能

商品管理:上架、库存管理
2. 秒杀活动:活动配置、时间控制
3. 下单流程:秒杀请求处理
4. 订单系统:订单生成、状态管理
5. 支付系统:支付对接(可选)

架构设计

整体架构图

graph TB
    A[客户端] --> B[CDN]
    B --> C[负载均衡 Nginx/LVS]
    C --> D[Web服务器集群]
    D --> E[缓存层 Redis集群]
    D --> F[消息队列 RabbitMQ/Kafka]
    F --> G[订单服务]
    G --> H[数据库 MySQL主从]
    E --> H

详细架构设计

┌─────────────────────────────────────────────┐
│                 客户端层                     │
│      Web端 / App端 / H5页面                  │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│               接入层                         │
│    CDN + 负载均衡 + 网关 + 限流              │
│   (Nginx + Sentinel/Guava RateLimiter)      │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│               业务层                         │
│   秒杀接口 + 订单服务 + 支付服务              │
│   (SpringBoot 集群)                          │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│              中间件层                        │
│   Redis集群 + MQ队列 + 任务调度              │
└─────────────────┬───────────────────────────┘
                  │
┌─────────────────▼───────────────────────────┐
│               数据层                         │
│   MySQL主从 + ES + 分库分表                  │
└─────────────────────────────────────────────┘

核心技术实现

前端优化

// 1. 静态资源CDN加速
- 商品详情页静态化处理
- 使用CDN分发静态资源
- 按钮置灰,禁止重复提交
// 2. 后端接口动静分离
@Controller
public class SeckillController {
    @GetMapping("/seckill/{goodsId}")
    public String seckillPage(@PathVariable("goodsId") Long goodsId, 
                              Model model) {
        // 静态化页面数据
        model.addAttribute("goods", goodsService.getGoodsInfo(goodsId));
        model.addAttribute("startTime", seckillService.getStartTime(goodsId));
        model.addAttribute("endTime", seckillService.getEndTime(goodsId));
        return "seckill";
    }
}

缓存设计(Redis)

@Service
public class SeckillService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    // 商品库存缓存Key
    private static final String STOCK_KEY = "seckill:stock:";
    // 用户购买记录Key
    private static final String USER_KEY = "seckill:user:";
    // 秒杀结果Key
    private static final String RESULT_KEY = "seckill:result:";
    /**
     * 秒杀核心逻辑
     */
    public SeckillResult seckill(Long userId, Long goodsId) {
        // 1. 校验用户是否已购买过
        if (redisTemplate.hasKey(USER_KEY + userId + ":" + goodsId)) {
            return SeckillResult.error("您已参与过秒杀");
        }
        // 2. 预扣库存(Redis原子操作)
        Long stock = redisTemplate.opsForValue()
            .decrement(STOCK_KEY + goodsId);
        if (stock < 0) {
            // 库存不足,回补库存
            redisTemplate.opsForValue().increment(STOCK_KEY + goodsId);
            return SeckillResult.error("商品已售罄");
        }
        // 3. 记录用户购买信息
        redisTemplate.opsForValue()
            .set(USER_KEY + userId + ":" + goodsId, "1", 10, TimeUnit.MINUTES);
        // 4. 发送消息到MQ,异步处理订单
        sendOrderMessage(userId, goodsId);
        return SeckillResult.success("秒杀成功");
    }
    /**
     * 使用Lua脚本确保原子性
     */
    public boolean seckillWithLua(Long userId, Long goodsId) {
        String script = 
            "local stock = redis.call('get', KEYS[1])" +
            "if tonumber(stock) <= 0 then return 0 end" +
            "redis.call('decr', KEYS[1])" +
            "redis.call('set', KEYS[2], 1)" +
            "return 1";
        DefaultRedisScript<Long> redisScript = 
            new DefaultRedisScript<>(script, Long.class);
        Long result = redisTemplate.execute(redisScript, 
            Arrays.asList(STOCK_KEY + goodsId, USER_KEY + userId + ":" + goodsId));
        return result == 1;
    }
}

消息队列异步处理

@Slf4j
@Component
public class OrderConsumer {
    @Autowired
    private OrderService orderService;
    /**
     * 监听订单消息
     */
    @RabbitListener(queues = "seckill.order.queue")
    public void handleOrderMessage(SeckillMessage message) {
        try {
            // 创建订单
            Order order = orderService.createOrder(
                message.getUserId(), 
                message.getGoodsId()
            );
            // 更新库存(数据库最终一致性)
            goodsService.updateStock(message.getGoodsId());
            // 记录秒杀结果
            redisTemplate.opsForValue().set(
                RESULT_KEY + message.getUserId() + ":" + message.getGoodsId(),
                JSON.toJSONString(order)
            );
            log.info("订单创建成功: userId={}, orderId={}", 
                message.getUserId(), order.getId());
        } catch (Exception e) {
            log.error("订单创建失败", e);
            // 失败重试或补偿处理
            sendCompensationMessage(message);
        }
    }
}

分布式锁应用

@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
            .setAddress("redis://localhost:6379")
            .setDatabase(0);
        return Redisson.create(config);
    }
}
@Service
public class SeckillLockService {
    @Autowired
    private RedissonClient redissonClient;
    public boolean seckillWithLock(Long userId, Long goodsId) {
        String lockKey = "seckill:lock:" + goodsId;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 尝试获取分布式锁
            if (!lock.tryLock(500, 20000, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("系统繁忙,请稍后重试");
            }
            // 校验库存
            int stock = goodsDao.getStock(goodsId);
            if (stock <= 0) {
                return false;
            }
            // 扣减库存
            goodsDao.decreaseStock(goodsId);
            // 创建订单
            orderService.createOrder(userId, goodsId);
            return true;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        } finally {
            lock.unlock();
        }
    }
}

限流控制

@Component
public class RateLimitInterceptor implements HandlerInterceptor {
    private static final Map<String, RateLimiter> RATE_LIMITERS = 
        new ConcurrentHashMap<>();
    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception {
        String uri = request.getRequestURI();
        // 每用户每秒最多5个请求
        String userId = request.getHeader("userId");
        String key = uri + ":" + userId;
        RateLimiter rateLimiter = RATE_LIMITERS.computeIfAbsent(
            key, k -> RateLimiter.create(5)
        );
        if (!rateLimiter.tryAcquire()) {
            response.setStatus(429);
            response.getWriter().write("请求过于频繁");
            return false;
        }
        return true;
    }
}

数据库优化设计

数据库表设计

-- 秒杀商品表
CREATE TABLE `seckill_goods` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `goods_name` VARCHAR(100) NOT NULL COMMENT '商品名称',
  `seckill_price` DECIMAL(10,2) NOT NULL COMMENT '秒杀价格',
  `original_price` DECIMAL(10,2) NOT NULL COMMENT '原价',
  `stock_count` INT NOT NULL COMMENT '库存数量',
  `start_time` DATETIME NOT NULL COMMENT '开始时间',
  `end_time` DATETIME NOT NULL COMMENT '结束时间',
  `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0未开始 1进行中 2已结束',
  `version` INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
  PRIMARY KEY (`id`),
  KEY `idx_start_time` (`start_time`),
  KEY `idx_end_time` (`end_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 秒杀订单表
CREATE TABLE `seckill_order` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `user_id` BIGINT NOT NULL COMMENT '用户ID',
  `goods_id` BIGINT NOT NULL COMMENT '商品ID',
  `order_no` VARCHAR(32) NOT NULL COMMENT '订单号',
  `seckill_price` DECIMAL(10,2) NOT NULL COMMENT '秒杀价格',
  `status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0待支付 1已支付 2已取消',
  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `pay_time` DATETIME DEFAULT NULL COMMENT '支付时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_goods` (`user_id`, `goods_id`),
  KEY `idx_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

数据库优化策略

// 1. 分库分表设计
// 订单表按用户ID分表
@TableName("seckill_order")
@TableId(type = IdType.AUTO)
public class SeckillOrder {
    @TableId(type = IdType.INPUT)
    private Long id;
    @TableField("user_id")
    private Long userId;
    // ... 其他字段
}
// 2. 读写分离配置
@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties("spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean
    public DynamicDataSource dynamicDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER, masterDataSource());
        targetDataSources.put(DataSourceType.SLAVE, slaveDataSource());
        return new DynamicDataSource(masterDataSource(), targetDataSources);
    }
}

流量削峰策略

分层限流

@Configuration
public class FlowControlConfig {
    @Bean
    public RateLimiter rateLimiter() {
        // 每秒100个请求
        return RateLimiter.create(100);
    }
    @Component
    public static class SeckillRateLimitFilter implements Filter {
        @Autowired
        private RateLimiter rateLimiter;
        @Override
        public void doFilter(ServletRequest request, 
                            ServletResponse response, 
                            FilterChain chain) throws IOException, ServletException {
            if (!rateLimiter.tryAcquire()) {
                HttpServletResponse resp = (HttpServletResponse) response;
                resp.setStatus(503);
                resp.setContentType("application/json");
                resp.getWriter().write("{\"code\":503,\"message\":\"系统繁忙\"}");
                return;
            }
            chain.doFilter(request, response);
        }
    }
}

请求排队

@Service
public class SeckillQueueService {
    @Autowired
    private RedissonClient redissonClient;
    private static final String QUEUE_KEY = "seckill:queue";
    // 排队请求
    public Long enqueue(BigDecimal userId) {
        RBlockingQueue<Long> queue = redissonClient.getBlockingQueue(QUEUE_KEY);
        try {
            queue.offer(userId, 5, TimeUnit.SECONDS);
            return 1L;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return 0L;
        }
    }
}

面试核心考点总结

超卖问题解决方案

  • Redis原子操作:使用decr命令预扣库存
  • 分布式锁:Redisson实现分布式锁
  • 数据库乐观锁version字段防止并发更新
  • MQ串行化:消息队列串行处理订单

高并发优化策略

  • CDN加速:静态资源就近访问
  • 缓存预热:提前加载热点数据
  • 异步处理:消息队列削峰
  • 熔断降级:保护核心服务

数据一致性保障

  • 最终一致性:MQ异步处理订单
  • 主动回滚:失败时补偿库存
  • 定期核对:Redis与数据库库存核对

扩展性问题

  • 水平扩展:Web服务无状态化
  • Redis集群:主从+哨兵+Cluster
  • MQ集群:高可用消息队列

实战建议

面试回答思路

第一层:功能实现(怎么做)
- 前端限流、按钮置灰
- Redis预扣库存、MQ异步下单
第二层:架构设计(怎么扩展)
- 分布式架构、集群部署
- 缓存、MQ、分库分表
第三层:性能优化(怎么优化)
- 并发压测、瓶颈分析
- JVM调优、SQL优化
第四层:可用性保障(怎么保障)
- 容灾备份、限流降级
- 监控告警、日志排查

常见面试问题

如何防止库存超卖?
2. 秒杀系统为什么用MQ?
3. Redis如何保证库存原子性?
4. 如何设计高可用的架构?
5. 系统如何横向扩展?
6. 如何保证数据最终一致性?

系统指标参考

并发量:设计支持10万+ QPS
响应时间:页面加载 < 200ms
下单延迟:< 2秒
成功率:> 99.9%
数据一致性:最终一致

这个设计案例涵盖了Java面试中的核心技术点,包括高并发处理、分布式系统设计、缓存优化、消息队列等,面试时能够清晰表达这些设计和实现思路,会给面试官留下深刻印象。

抱歉,评论功能暂时关闭!