51mee - AI智能招聘平台Logo
模拟面试题目大全招聘中心会员专区

设计一个支持百万级用户、每秒处理数千订单的电商订单系统,需要考虑限流、缓存、数据库分库分表、消息队列等,请描述核心模块的架构设计,并说明各组件的作用和关键技术点。

信步科技产品难度:困难

答案

1) 【一句话结论】
针对百万级用户、千级订单/秒的电商场景,设计基于微服务解耦的订单系统,通过API网关统一入口,结合限流(令牌桶算法)、Redis缓存(含预热)、数据库分库分表(水平分库+时间分表+垂直拆分+读写分离)、Kafka异步消息队列(带幂等性),以及最终一致性方案(ACK+重试+补偿),实现高并发、高可用,核心是资源隔离、异步化与数据一致性保障。

2) 【原理/概念讲解】
老师讲解各组件作用与关键技术:

  • 限流组件:采用令牌桶算法(Token Bucket),类比“水龙头”控制流量,每秒生成固定数量令牌,请求消耗令牌,超限拒绝。桶容量(如1000)和速率(如1000/s)根据压力测试设定,平滑突发流量,限制最大并发数。
  • 缓存策略:Redis缓存订单状态(如订单创建成功标志)和库存数据(如SKU库存量)。缓存穿透:空值查询用互斥锁+缓存空值(设置过期时间);缓存雪崩:热点key失效,用热点key永不过期或随机过期;缓存击穿:热点key失效,用互斥锁+缓存结果。缓存预热:凌晨0点执行定时任务,批量加载热门SKU库存到Redis(如前1000个热门SKU),减少订单创建时的缓存查询压力。
  • 分库分表:水平分库(订单表按订单ID取模,如订单ID % 8,分到8个库);分表(按时间,如按月,如orders_202408);垂直拆分(订单表与库存表分离,订单表存储订单信息,库存表存储SKU库存)。读写分离:主库写订单、库存数据,从库读查询,提升查询性能。索引设计:订单表按(订单ID, 创建时间)建复合索引,提升按订单ID查询和按时间分表查询效率。
  • 消息队列:Kafka用于订单状态变更(如支付成功后发送“库存扣减”消息),解耦订单服务与库存服务,支持异步处理,提高吞吐。幂等性:通过检查消息唯一标识(如订单ID+时间戳)或订单状态(如已处理标记)实现,避免重复消费。
  • 分布式事务:采用最终一致性方案,订单服务写入订单表后,发送消息到Kafka,库存服务消费消息后更新库存表。消息消费采用ACK机制(确认消息已处理),若库存扣减失败则重试(最多3次),重试失败则触发补偿逻辑(恢复库存,发送重试消息到重试队列,由补偿服务处理)。

3) 【对比与适用场景】

  • 限流算法对比(令牌桶 vs 漏桶):
    算法定义特性使用场景注意点
    令牌桶维持固定大小桶,按固定速率放令牌,请求消耗令牌限制最大并发数,平滑流量电商首页、登录接口(需要平滑流量)需设置桶容量和速率,避免突发流量
    漏桶桶以固定速率流出水滴,流入水滴若超过桶容量则溢出限制最大流量,突发流量被丢弃API接口(如短信验证码)、严格限制流量丢弃突发流量,可能导致业务中断
  • 消息队列选型(Kafka vs RabbitMQ):
    组件KafkaRabbitMQ适用场景
    特性高吞吐、持久化、分区、支持集群队列模型、支持多种消息模式电商订单状态变更(高吞吐、持久化)

4) 【示例】
订单创建流程(伪代码):

# 订单服务处理订单创建
def create_order(order_data):
    # 1. 限流检查(按用户维度)
    if not rate_limiter.allow(order_data['user_id']):
        return {"code": 429, "msg": "请求太频繁"}
    
    # 2. 查询库存(缓存优先)
    stock_key = f"stock:{order_data['sku_id']}"
    stock = redis.get(stock_key)
    if stock is None:
        # 缓存穿透处理:加互斥锁
        with redis.lock(stock_key, timeout=10):
            stock = redis.get(stock_key)
            if stock is None:
                stock = mysql.query_one("SELECT stock FROM inventory WHERE sku_id = ?", order_data['sku_id'])
                redis.setex(stock_key, 3600, stock)  # 缓存有效时间
    if int(stock) < order_data['quantity']:
        return {"code": 400, "msg": "库存不足"}
    
    # 3. 生成订单(写入数据库)
    order_id = mysql.insert("INSERT INTO orders (order_id, user_id, sku_id, quantity, total_price) VALUES (?, ?, ?, ?, ?)",
                            order_data['order_id'], order_data['user_id'], order_data['sku_id'], order_data['quantity'], order_data['total_price'])
    
    # 4. 发送消息到Kafka(异步通知库存服务)
    kafka_producer.send("order-created", value=order_data, key=order_id)
    
    return {"code": 200, "order_id": order_id}

# 库存服务消费消息扣减库存
def consume_order_created_message(message):
    order_data = message.value
    # 幂等性检查:检查是否已处理
    if redis.exists(f"order_processed:{order_data['order_id']}"):
        return  # 跳过处理
    # 扣减库存
    result = mysql.update("UPDATE inventory SET stock = stock - ? WHERE sku_id = ?", order_data['quantity'], order_data['sku_id'])
    if result > 0:
        redis.setex(f"order_processed:{order_data['order_id']}", 3600, "true")  # 标记已处理
    else:
        # 库存扣减失败,重试或补偿
        kafka_producer.send("retry-order-created", value=order_data, key=order_data['order_id'])

5) 【面试口播版答案】
“面试官您好,针对百万级用户、千级订单/秒的电商订单系统,我设计的核心架构是微服务解耦+分布式组件协同。首先,订单、库存、支付等模块通过API网关统一入口,部署限流组件(令牌桶算法)防止系统过载。订单服务读取库存时,优先从Redis缓存获取(缓存穿透用互斥锁+空值缓存解决),若缓存未命中则查询MySQL数据库,并将结果缓存。订单创建后,写入MySQL订单表,同时发送消息到Kafka,库存服务消费消息后更新库存表。数据库层面,订单表按订单ID取模分库(如订单ID % 8),按时间分表(如按月),库存表垂直拆分,并配置读写分离。消息队列用于异步处理,避免订单服务阻塞,提升吞吐。整体通过限流、缓存、分库分表(含读写分离)、消息队列及最终一致性方案(消息ACK+重试+补偿),实现高并发、高可用,满足百万级用户、千级订单/秒的处理需求。”

6) 【追问清单】

  • 问:限流具体参数如何设置?比如令牌桶的速率和容量?
    答:根据压力测试,系统每秒处理1000订单,令牌桶速率设为1000,桶容量设为1000,这样能平滑突发流量,避免系统崩溃。
  • 问:缓存预热的具体做法?比如凌晨加载热门SKU库存到缓存?
    答:系统在凌晨0点执行定时任务,批量查询热门SKU的库存数据,写入Redis缓存,减少订单创建时的缓存查询压力。
  • 问:分库分表后,订单与库存的事务如何保证?比如订单支付失败后库存如何回滚?
    答:采用最终一致性,订单服务写入订单表后发送消息到Kafka,库存服务消费消息扣减库存。若支付失败,订单服务发送消息到重试队列,库存服务消费后恢复库存(补偿逻辑)。
  • 问:消息队列幂等性如何实现?比如消息重复消费导致库存重复扣减?
    答:在库存服务中,消费消息前检查Redis中是否存在“订单已处理”标记(如key为order_processed:订单ID),若存在则跳过处理;或者消息头包含唯一标识(订单ID+时间戳),避免重复消费。
  • 问:分库分表后,订单表按订单ID建索引,具体索引设计?
    答:订单表按(订单ID, 创建时间)建复合索引,提升按订单ID查询和按时间分表查询的效率,避免全表扫描。

7) 【常见坑/雷区】

  • 限流位置错误:限流应放在API网关或服务入口,避免内部服务重复限流,导致流量控制失效。
  • 缓存击穿:未处理热点key失效,导致大量请求落库,可设置热点key永不过期或互斥锁,避免雪崩。
  • 分布式事务复杂性:避免使用两阶段提交,改用最终一致性,否则系统复杂且性能低。
  • 消息队列幂等性缺失:若消息重复消费,会导致库存重复扣减,需通过检查状态或唯一标识实现,否则业务数据错误。
  • 数据库索引优化不足:分库分表后,未按查询条件建索引,导致查询性能下降,需根据查询频率设计复合索引。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1