
采用分布式订单簿(Redis有序集合)+ 消息队列(Kafka)+ 多级缓存架构,通过事务/消息确认保证数据一致性,分片+过期清理控制内存,实现百万级并发下的低延迟、高可用及容错性。
股票交易撮合系统的核心是“订单簿”与“撮合引擎”。订单簿分为买方队列(按价格降序、时间升序排列)和卖方队列(按价格升序、时间升序排列),用于实时匹配买卖订单。撮合引擎按“价格优先、时间优先”规则匹配:当买方队列最低价≥卖方队列最高价时,触发成交。系统组件包括:订单接收层(处理请求、验证参数)、订单簿管理(Redis存储买方/卖方队列)、消息队列(Kafka处理订单流)、持久化存储(MySQL存储历史数据)、监控告警(实时监控延迟、错误率)。订单簿的有序集合结构类似数据库的索引,但支持实时更新和查询,好比超市货架的动态排位,买方按“高价优先”排,卖方按“低价优先”排,收银员(撮合引擎)按规则快速匹配,完成交易。同时,订单状态管理(未成交、已成交、已撤单)通过Redis的zrem操作实现,确保撤单时能及时更新订单簿。
| 对比项 | Redis(有序集合) | MySQL(InnoDB) | Kafka(消息队列) | RabbitMQ(队列) |
|---|---|---|---|---|
| 定义 | 内存数据库,支持有序集合(zset),用于实时存储订单簿 | 关系型数据库,事务支持,用于持久化历史数据 | 分布式消息队列,用于订单流传输 | 基于消息队列,用于小流量精确投递 |
| 读写性能 | 毫秒级读写(内存访问) | 磁盘I/O,秒级(复杂查询慢) | 低延迟(批量处理,毫秒级) | 中(单条处理,微秒级) |
| 数据持久化 | RDB/AOF(可选,保证数据不丢失) | InnoDB事务,持久化到磁盘 | 持久化日志(高可靠,保留策略) | 镜像队列(可靠投递,但延迟高) |
| 适用场景 | 订单簿(实时查询、更新) | 历史订单、用户信息、交易记录 | 订单流(大流量、顺序处理) | 小流量、精确投递(如通知) |
| 注意点 | 内存占用大,需分片/过期清理 | 事务支持,复杂查询慢;读写分离 | 分区导致消息乱序;需设置重试机制 | 队列模式复杂,延迟高;消息持久化不足 |
| 选择依据:Redis有序集合适合实时匹配(低延迟),Kafka适合高吞吐订单流(分区实现负载均衡),MySQL用于持久化历史数据(事务保证一致性)。 |
# 订单提交接口(含状态管理)
def submit_order(order_id, user_id, stock_code, price, quantity, order_type):
if not validate_order(user_id, stock_code, quantity):
return {"code": "invalid", "msg": "参数错误"}
# 1. 更新订单簿(事务保证原子性)
with redis.pipeline() as pipe:
pipe.zadd(f"buy-{stock_code}", {order_id: price}, order_type) # 买方队列
pipe.zadd(f"sell-{stock_code}", {order_id: price}, order_type) # 卖方队列
pipe.execute()
# 2. 发送订单消息
kafka_producer.send(topic="order-stream", key=stock_code, value=order_json)
return {"code": "success", "msg": "订单提交成功"}
# 订单撤单接口(仅未成交订单可撤单)
def cancel_order(order_id, stock_code):
order_status = redis.hget(f"order-{order_id}", "status")
if order_status == "unmatched":
with redis.pipeline() as pipe:
pipe.zrem(f"buy-{stock_code}", order_id) # 删除买方订单
pipe.zrem(f"sell-{stock_code}", order_id) # 删除卖方订单
pipe.hset(f"order-{order_id}", "status", "canceled")
pipe.execute()
kafka_producer.send(topic="cancel-stream", value=cancel_json)
return {"code": "success", "msg": "撤单成功"}
else:
return {"code": "failed", "msg": "订单已成交,无法撤单"}
# 撮合引擎(批量操作优化)
def match_orders():
while True:
orders = kafka_consumer.poll(timeout=100) # 批量消费
for order in orders.values():
order_data = json.loads(order.value)
stock_code = order_data["stock_code"]
# 获取订单簿(批量查询)
buy_queue = redis.zrange(f"buy-{stock_code}", 0, -1, withscores=True)
sell_queue = redis.zrange(f"sell-{stock_code}", 0, -1, withscores=True)
if buy_queue and sell_queue:
buy_price, _ = buy_queue[-1]
sell_price, _ = sell_queue[0]
if buy_price >= sell_price:
execute_trade(order_data) # 执行成交
# 批量更新订单簿(事务)
with redis.pipeline() as pipe:
pipe.zrem(f"buy-{stock_code}", buy_price) # 删除买方订单
pipe.zrem(f"sell-{stock_code}", sell_price) # 删除卖方订单
pipe.execute()
kafka_producer.send(topic="trade-result", value=trade_json,
callback=lambda m: log.info("成交消息已确认"))
面试官您好,设计百万级并发股票交易撮合系统,核心是构建分布式订单簿(用Redis有序集合存储买方/卖方队列),通过消息队列(Kafka)处理订单流,确保低延迟。订单接收层将订单发送到Kafka,撮合引擎消费订单后,实时更新Redis中的订单簿。当买方队列最低价≥卖方队列最高价时,触发成交,并通过Redis事务保证订单簿更新与消息发送的原子性,避免数据不一致。关键技术包括Redis的有序集合实现实时匹配,Kafka保证消息持久化与顺序,按股票代码分片控制内存,定期清理过期订单,避免OOM。系统还设计了主从复制、故障转移,确保高可用,监控延迟和错误率,实现百万级并发下的低延迟、高可用及容错性。
问:如何处理订单撤单?
答:订单状态管理(如未成交订单可撤单),通过Redis事务删除订单簿中的订单,并更新订单状态为“已撤单”,同时发送撤单消息。
问:系统如何应对网络分区?
答:消息持久化(Kafka日志保留策略),订单簿数据复制(Redis主从),故障转移(主从切换,切换时间<1秒)。
问:内存监控与自动扩容?
答:设置Redis内存阈值,当内存占用超过阈值时,触发扩容(如增加Redis实例分片,或清理过期订单),避免OOM。
问:消息队列分区策略?
答:按股票代码分区,每个分区对应一个Kafka分区,消费者组按分区消费,实现负载均衡,避免单分区过载。
问:如何避免消息乱序导致撮合错误?
答:Kafka按分区顺序消费,结合Redis有序集合(zset)保证价格优先,确保订单按时间顺序处理,避免乱序撮合。