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

设计一个高并发的证券交易撮合系统,要求支持每秒万级订单处理,毫秒级响应。请说明系统架构、核心模块设计、关键技术选型(如缓存、消息队列、数据库)以及如何保证数据一致性和系统稳定性。

中信证券培训生难度:困难

答案

1) 【一句话结论】

高并发证券交易撮合系统需采用分布式分层架构,以订单状态机为核心,通过多级缓存+消息队列解耦,分库分表存储,结合最终一致性策略,确保万级TPS与毫秒级响应。

2) 【原理/概念讲解】

系统分为接入层、匹配层、存储层、监控层四层:

  • 接入层(API网关):处理订单请求,校验用户身份(如登录凭证)、订单参数(如股票代码、价格、数量)及防攻击(如限流、防刷单)。
  • 匹配层(核心):通过订单状态机管理订单流转(如“已提交”→“已校验”→“待撮合”→“已成交”→“已撤单”),生成消息推送到消息队列。
  • 存储层:存储订单、成交、持仓等数据,采用分库分表提升读写性能。
  • 监控层:采集TPS、延迟、错误率等指标,实时告警。

订单状态机像业务流程的“交通灯”,每个状态转换有明确规则(如校验通过后状态变为“已校验”),确保业务逻辑完整性。

3) 【对比与适用场景】

缓存技术对比

技术组件RedisMemcached适用场景注意点
定义内存数据库,支持持久化、事务、发布订阅纯内存缓存,无持久化接入层限流、匹配层订单缓存、热点数据预热Redis需配置RDB(每分钟备份)和AOF(每秒追加),避免数据丢失;Memcached需定期手动备份,否则重启会丢失数据
特性高并发读写,数据持久化,支持复杂操作低延迟,简单缓存,无持久化限流、订单缓存、会话Redis事务(MULTI/EXEC)保证多操作原子性,Memcached仅缓存,无事务

消息队列技术对比

技术组件KafkaRabbitMQ适用场景注意点
定义分布式消息队列,高吞吐、持久化企业级消息队列,轻量、支持多种协议订单流(高吞吐,如每秒10万条)、日志、匹配后订单确认Kafka需管理分区(按股票代码分区,保证消息顺序),RabbitMQ需设置消息持久化(delivery mode=2),确保消息不丢失
特性高吞吐,持久化,分区复制轻量,支持工作流,消息确认订单流处理、异步通知Kafka需消费者组管理,RabbitMQ需交换机(如topic交换机)和队列绑定

数据库技术对比

技术组件分布式数据库(如TiDB)单体数据库(如MySQL)适用场景注意点
定义分布式架构,支持分库分表,分布式事务单机或集群,垂直扩展订单、成交、持仓等核心数据TiDB支持高并发读写(如百万级TPS),分库分表(按股票代码分表,按时间维度分库),分布式事务(两阶段提交,保证数据一致性);MySQL需主从复制,扩展性差
特性高并发,分库分表,分布式事务高并发,垂直扩展,事务核心业务数据TiDB需设计分片键(如stock_code作为分片键),避免热点数据集中;MySQL需主从同步,延迟可能较高

4) 【示例】

订单状态机状态转换表

当前状态触发操作下一个状态
已提交校验通过已校验
已校验加入撮合待撮合
待撮合撮合成功已撮合
已撮合更新持仓已成交
已成交用户撤单已撤单

撮合逻辑伪代码(时间优先+价格优先)

def match_orders(bids, asks):
    bids.sort(key=lambda x: x['price'], reverse=True)  # 买方价格高优先
    asks.sort(key=lambda x: x['price'])  # 卖方价格低优先
    matched = []
    while bids and asks:
        bid = bids[0]
        ask = asks[0]
        if bid['price'] >= ask['price']:
            quantity = min(bid['quantity'], ask['quantity'])
            matched.append({
                "order_id": bid['order_id'],
                "stock_code": bid['stock_code'],
                "price": ask['price'],
                "quantity": quantity,
                "type": "buy"
            })
            bid['quantity'] -= quantity
            ask['quantity'] -= quantity
            if bid['quantity'] == 0:
                bids.pop(0)
            if ask['quantity'] == 0:
                asks.pop(0)
        else:
            break
    return matched

缓存预热示例

冷启动时,定时任务(如每分钟)从数据库预加载前10个热门股票的订单数据(按成交量排序),存入Redis有序集合(key为“hot_orders”,按价格排序),减少后续请求的数据库压力。热点数据更新策略:当订单量超过阈值(如每秒1000条)时,动态更新缓存中的热点数据,确保缓存数据时效性。

5) 【面试口播版答案】

面试官好,我设计的系统核心是分布式分层架构,分接入、匹配、存储三层。接入层用API网关处理订单请求,校验用户身份和参数(如用户认证、订单参数有效性);匹配层通过订单状态机管理订单流转(如已提交→已校验→待撮合→已成交),生成消息推送到Kafka,再由消费端更新数据库。缓存用Redis缓存订单和成交记录,减少数据库压力;数据库分库分表(按股票代码分表,按时间维度分库),提升读写性能。数据一致性采用最终一致性,消息队列确保消息不丢失,数据库事务保证核心数据(如持仓)一致性。系统稳定性方面,接入层有熔断,匹配层降级,数据库主从复制,监控指标实时采集,故障自动切换。比如通过压测,系统在10万并发下延迟小于5ms,支持万级TPS。订单状态机通过Redis的SETNX实现分布式锁,确保状态转换原子性,避免并发下的数据不一致。缓存预热冷启动时预加载热门股票订单,减少缓存穿透。分库分表按股票代码分表,时间分库,避免热点数据集中。消息队列Kafka处理订单流,RabbitMQ处理状态变更,确保消息可靠。这样能支持万级TPS,毫秒级响应。

6) 【追问清单】

  • 问题:订单状态管理如何保证数据一致性?
    回答:通过订单状态机,每个状态转换有明确规则(如校验通过后状态变为“已校验”),结合Redis的SETNX实现分布式锁,确保状态转换的原子性,避免并发下的数据不一致。

  • 问题:缓存预热如何实现?
    回答:冷启动时预加载热门股票的订单数据(如前10个热门股票的订单),存入Redis有序集合;定期(如每分钟)更新热点数据,减少缓存穿透和击穿。

  • 问题:分库分表的键如何设计?
    回答:按股票代码分表(如每个股票一个表),按时间维度分库(如按年/月分库),结合时间戳分片,避免热点数据集中,提升读写性能。

  • 问题:消息队列如何保证消息不丢失?
    回答:Kafka采用持久化存储(日志文件),RabbitMQ设置消息持久化(delivery mode=2),结合消费端确认机制(acknowledgment),确保消息可靠传输。

  • 问题:系统如何处理订单超时或网络异常?
    回答:接入层设置超时重试,匹配层将异常订单放入重试队列,数据库回滚未完成事务,避免数据不一致。

7) 【常见坑/雷区】

  • 雷区1:忽略订单状态机原子性,导致并发下状态转换错误,数据不一致。
  • 雷区2:缓存未预热,导致热点数据缓存穿透,影响性能。
  • 雷区3:分库分表键设计不合理,导致热点数据集中,性能下降。
  • 雷区4:消息队列未区分场景,导致订单流和状态变更混合,影响吞吐。
  • 雷区5:追求强一致性,采用分布式事务(如两阶段提交),导致系统性能下降,适合最终一致性场景。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1