1) 【一句话结论】
采用微服务+事件驱动的分布式架构,通过TiDB(按账户ID哈希分片)、Kafka(100分区+100消费者)、Redis(启动时预热热点数据),结合Saga补偿事务,实现百万级TPS、毫秒级响应及7x24运行,支持快速水平扩容。
2) 【原理/概念讲解】
系统架构分层为三部分:
- 用户接入层(API网关):作为统一入口,负责请求路由、限流(令牌桶算法控制并发,如每秒1000请求)、认证(JWT验证用户权限),避免直接暴露后端服务。
- 业务逻辑层(微服务):拆分为独立服务(如汇率服务、风控服务、交易服务),每个服务聚焦单一业务,降低耦合。
- 数据服务层:负责数据持久化与缓存,包括分布式数据库(TiDB)、缓存(Redis)和消息队列(Kafka)。
核心组件选型逻辑:
- 数据库:选TiDB(分布式事务型数据库),支持水平分片(按账户ID哈希取模,如账户ID mod 8,分8个分片),避免单机性能瓶颈,满足百万级TPS。
- 消息队列:选Kafka(高吞吐、持久化),用于异步解耦服务间依赖,缓冲流量波动(如交易高峰期)。
- 缓存:选Redis(低延迟、高并发),缓存热点数据(如实时汇率、用户账户余额),降低数据库压力。
数据一致性保障:采用最终一致性,通过Saga补偿事务确保数据一致。交易服务发送Kafka消息,消费者处理时若数据库更新失败,则发送补偿消息到补偿主题,由补偿服务处理,最终确保数据一致。类比:消息队列像“快递中转站”,服务A发请求,服务B处理,失败后补偿服务处理,最终数据一致。
3) 【对比与适用场景】
数据库(MySQL vs TiDB)
| 类别 | MySQL(单机/主从) | TiDB(分布式) | 适用场景 | 注意点 |
|---|
| 定义 | 单机或主从架构,ACID强一致 | 分布式架构,支持分片、复制 | 低TPS、强一致性场景 | 单机性能瓶颈,分片复杂 |
| 特性 | 事务强一致,数据结构化 | 分布式事务,支持高并发 | 高TPS、水平扩展 | 需要分片键(如账户ID),运维复杂 |
| 使用场景 | 小规模交易系统 | 百万级TPS、7x24运行 | | |
消息队列(Kafka vs RabbitMQ)
| 类别 | Kafka | RabbitMQ | 适用场景 | 注意点 |
|---|
| 定义 | 高吞吐、持久化、分布式 | 中等吞吐、轻量 | 交易系统、高吞吐异步解耦 | Kafka延迟低,RabbitMQ延迟高 |
| 特性 | 持久化、高吞吐、多消费者 | 基于交换机/队列 | | Kafka需要磁盘空间,RabbitMQ内存占用低 |
| 使用场景 | 交易系统、日志、流处理 | 小型系统、简单队列 | | Kafka扩容复杂,RabbitMQ简单 |
4) 【示例】
用户买入100美元(账户ID: 1001),流程:
- API网关:限流(令牌桶),认证(JWT)。
- 业务逻辑层:
- 汇率服务:从Redis查实时汇率(若缓存未命中,从TiDB查并缓存到Redis)。
- 风控服务:从Redis查账户余额(若未命中,从TiDB查并缓存),若余额不足则返回错误。
- 交易处理服务:将交易信息(账户ID:1001, 金额:100, 汇率:1.2)发送到Kafka主题
trade-events(分区1,消息ID:1)。
- 消费者(1号消费者,处理分区1):
- 从TiDB执行分布式事务(插入交易记录,更新账户余额),若失败则回滚并发送补偿消息到
trade-compensation主题。
- 若成功,更新Redis(账户余额减100,插入交易记录)。
- 返回响应:毫秒级返回“交易成功”。
5) 【面试口播版答案】
(约90秒,自然表达)
“面试官您好,针对百万级TPS、毫秒级响应的外汇交易系统,我设计的架构是微服务+事件驱动的分布式系统。分层架构分为用户接入层(API网关)、业务逻辑层(微服务)、数据服务层(TiDB+Redis+Kafka)。用户层负责请求路由和限流,业务层处理交易逻辑,数据层负责数据持久化。核心组件选型:数据库用TiDB,按账户ID哈希分片(如账户ID mod 8,分8个分片),支持高并发读写;消息队列用Kafka,设置100分区,100消费者,每个分区由一个消费者处理,缓冲流量;缓存用Redis,启动时预加载实时汇率表(如美元、日元等货币对)和常用账户余额。数据一致性采用Saga补偿事务,交易失败时通过补偿消息确保最终数据一致。扩容方面,数据库增加分片节点,消息队列增加消费者,缓存集群扩容,支持快速水平扩展。这样能保证7x24运行,毫秒级响应,百万级TPS。”
6) 【追问清单】
- 问:如何保证交易金额的原子性?
- 回答要点:采用Saga模式,通过补偿事务确保最终一致性,结合消息确认(Kafka的
ack=1)和事务消息,减少分布式事务调用,提高吞吐。
- 问:消息队列的延迟和堆积如何处理?
- 回答要点:Kafka设置分区数与消费者数匹配(如100分区+100消费者),调整生产者批量大小(1MB),设置消息重试机制(重试3次后丢弃),避免堆积。
- 问:缓存预热的具体方法?
- 回答要点:启动时通过定时任务预加载热点数据(如实时汇率表,按时间窗口分批加载,每5分钟加载一次),减少缓存未命中时的数据库压力。
- 问:系统如何应对硬件资源限制?
- 回答要点:通过负载均衡(Nginx)分发请求,数据库分片增加节点,消息队列增加消费者,缓存集群扩容,确保在硬件资源有限时仍能支持高TPS。
- 问:容灾和故障恢复机制?
- 回答要点:数据库主从复制(读写分离),消息队列持久化(日志存储),服务熔断(Hystrix),日志监控(Prometheus+Grafana),快速故障切换(Zookeeper的Leader选举)。
7) 【常见坑/雷区】
- 直接用单机数据库:导致TPS瓶颈,无法支持百万级请求,单机性能无法满足高并发。
- 消息队列分区数设置不当(如分区数远小于消费者数):导致消费者空闲,资源浪费;或分区数远大于消费者数:导致消息堆积,延迟高。
- 数据一致性过度追求强一致:如分布式事务频繁调用,增加系统开销,降低吞吐。
- 缓存未做预加载:热点数据访问时数据库压力过大,导致响应延迟。
- 扩容时服务间通信问题:如服务注册发现(Nacos)配置错误,导致请求无法路由到新节点,影响系统可用性。