Appearance
建行市场结算平台(亮点与难点)
上面这位学员简历的项目没什么亮点和难点,投出去面试机会可能不多,经过和老师沟通后,着重优化了第二个项目建行惠市宝项目,这个项目本质上就是一个银行结算平台,优化如下:
项目名称:中国建设银行惠市宝(专业市场结算平台)
项目描述:
该项目是建设银行面向全国专业市场的统一支付结算平台,日交易金额过亿,服务数百家专业市场、上万商户的核心系统。项目采用微服务架构,主要包含合约、支付、清算、对账调账四大核心模块。
技术栈:
SpringBoot + SpringCloud + Redis + Kafka + Quartz + Oracle
核心职责与技术难点:
- 架构设计与性能优化
- 实现了基于本地消息表+Kafka的事务最终一致性方案,确保支付、分账等核心场景的数据一致性
- 采用多级缓存架构(本地缓存+Redis集群),将商户信息查询性能提升5倍,系统整体响应时间降低到200ms以内
- 设计并实现了基于分库分表的高性能支付模块,通过分库策略和索引优化,使得单表数据量控制在千万级别,查询性能提升300%
- 支付流程优化与安全性提升
- 设计了支付防重复提交机制,通过分布式锁+幂等性检查,有效防止重复支付
- 实现了基于风控规则引擎的交易监控系统,支持灵活配置风控规则,有效拦截异常交易
- 开发了支付结果实时通知重试机制,通过指数退避算法,确保通知成功率达到99.99%
- 清算系统设计与优化
- 设计了支持多维度的分账规则引擎,可灵活配置商户分账比例、阶梯分账等复杂场景
- 实现了分布式任务调度系统,通过Quartz集群确保大批量清算任务的可靠执行
- 开发了智能对账系统,支持T+1差异化对账,对账准确率达99.999%,大幅减少人工对账工作量
- 系统监控与运维
- 构建了基于ELK的统一日志收集分析平台,实现系统运行状态实时监控
- 设计了服务降级和熔断机制,通过Sentinel实现系统限流保护
- 实现了分布式链路追踪,通过SkyWalking快速定位系统瓶颈
技术亮点:
- 采用SpringCloud微服务架构,实现系统高可用性,服务可用性达到99.99%
- 使用分布式事务确保支付环节的数据一致性
- 实现了支付、清算等核心模块的高并发处理能力,系统TPS达到3000+
- 通过分库分表、缓存优化等手段,有效支持海量数据处理
项目成果:
- 系统成功上线后支持日均交易量10万+,峰值TPS 3000+
- 平台运行稳定,核心接口响应时间<200ms,系统可用性99.99%
- 获得建行总行年度优秀项目奖,并在多个分行成功推广使用
这样的项目描述既保留了原有业务特点,又突出了技术难点和解决方案,同时加入了具体的性能指标和业务成果,使项目更具说服力和亮点。在面试时,你可以重点展开讨论系统架构、业务设计亮点、分布式事务处理、性能优化等技术难点的具体实现方案。
面试如何回答
一、实现了基于本地消息表+Kafka的最终一致性方案,确保支付、分账等核心场景的数据一致性
针对这个点,可以从以下几个方面来回答:
- 业务场景说明:
在惠市宝项目中,支付和分账是两个核心场景。比如当一个商户收款1000元时,可能需要按照合同约定将这笔钱分给多个相关方,如市场方抽成10%,平台方抽成5%等。这个过程涉及多个账户的资金变动,必须保证数据一致性。 - 架构设计:
我们采用了本地消息表+Kafka的最终一致性方案,具体架构如下:- 本地消息表用于记录事务消息,包含消息ID、业务类型、消息内容、状态、重试次数等字段
- Kafka用于可靠的消息投递
- 定时任务用于消息重试
- 各个微服务通过消费Kafka消息来完成业务处理
- 具体实现流程:
以支付成功后进行分账为例:
(1) 支付服务收到支付成功通知:
- 开启本地事务
- 更新支付订单状态
- 写入本地消息表,状态为待发送
- 提交事务
(2) 消息发送机制:
- 专门的消息发送线程扫描本地消息表
- 将待发送的消息投递到Kafka
- 投递成功后更新消息状态为已发送
(3) 分账服务消费消息:
- 消费Kafka消息
- 执行分账业务逻辑
- 更新分账结果
- 确认消息消费完成,更新消息状态为已处理
- 可靠性保证:
我们通过以下机制确保消息可靠性:- 本地消息表和业务操作在同一个事务内
- 定时任务定期扫描未成功发送的消息进行重试
- 消息发送采用至少一次投递策略
- 消费端保证幂等性处理
- 引入消息状态机制,记录消息完整生命周期
- 异常处理:
系统考虑了各种异常情况:- 消息发送失败:通过定时任务重试机制处理
- 消息消费失败:通过消费重试机制处理
- 重复消息:通过业务唯一标识保证幂等性
- 消息积压:这个点要做好比较复杂,可以参考直播课《双十一高并发抢购(秒杀)系统三高架构实战》
- 监控和运维:
我们还实现了完善的监控机制:- 监控消息发送、消费状态
- 统计消息处理时延
- 异常消息告警
- 提供消息补偿接口
二、采用多级缓存架构(本地缓存+Redis集群),将商户信息查询性能提升5倍,系统整体响应时间降低到200ms以内
- 业务背景:
在支付系统中,商户信息查询是一个非常高频的操作。每次支付、分账、退款等操作都需要查询商户信息,日访问量在百万级别。原来直接查询数据库的方式已经无法满足性能要求,所以我们设计了多级缓存方案。 - 架构设计:
我们采用了两级缓存架构:- 一级缓存:本地缓存(Caffeine)
- 二级缓存:Redis集群
- 最后是MySQL/Oracle数据库持久层
- 具体实现:
(1) 缓存架构:
- 本地缓存:使用Caffeine,设置最大容量10000条,过期时间15分钟
- Redis集群:采用哨兵模式,确保高可用,存储全量商户信息,过期时间30分钟
- 采用二级缓存的读写策略:
* 读取:本地缓存 -> Redis集群 -> 数据库
* 写入:数据库 -> 失效Redis -> 失效本地缓存
(2) 缓存更新策略:
- 采用Cache-Aside模式
- 更新数据库后,删除Redis缓存
- 通过消息队列通知其他节点清除本地缓存
PS:如果有对多级缓存架构代码实现不清楚的可以参考直播课《京东生产环境Redis高并发缓存架构实战》
- 性能保障:
我们采取了多项措施确保性能:- 本地缓存采用LRU淘汰算法,保留最常访问的商户信息
- Redis集群使用哨兵模式,保证高可用
- 使用布隆过滤器防止缓存穿透
- 采用分布式锁防止缓存击穿
- 不同级别缓存采用不同过期时间,防止雪崩
- 监控和运维:
实现了完善的监控机制:- 缓存命中率监控
- 缓存延迟监控
- 缓存容量监控
- 异常告警机制
- 实际效果:
优化后达到以下效果:- 本地缓存命中率达到85%
- Redis缓存命中率达到95%
- 平均响应时间从1s降到200ms以内
- 系统QPS提升5倍以上
- 问题处理:
在实施过程中解决了以下问题:- 缓存一致性:通过消息队列通知机制解决
- 缓存穿透:使用布隆过滤器
- 缓存击穿:使用分布式锁
- 缓存雪崩:错开缓存过期时间
三、设计并实现了基于分库分表的高性能支付模块,通过分库策略和索引优化,使得单表数据量控制在千万级别,查询性能提升300%
- 业务背景:
在支付系统中,订单数据增长非常快,日订单量百万级别。随着业务发展,单表数据量已经超过5000万,查询性能下降严重,特别是在商户对账、订单查询等场景下,响应时间达到秒级,严重影响用户体验。因此我们进行了分库分表改造。 - 整体方案:
我们采用了分库分表+索引优化的综合方案:- 分库:按照商户ID范围进行水平分库,分为8个库
- 分表:按照订单时间范围进行分表,每个库按月分表
- 索引优化:基于业务查询特点优化索引结构
- 具体实现:
(1) 分片策略:
- 分库键选择:选择商户ID作为分库键
- 原因:商户维度查询频繁
- 实现:merchant_id % 8 确定库位置
- 分表策略:按月份分表
- 格式:t_order_yyyyMM
- 优势:便于历史数据归档和清理
PS:关于分库分表分片策略选择的更多场景参考直播课《面试必问海量数据分库分表架构实战》
(2) 数据路由实现:
java
public class OrderRouter {
public String getTargetDatabase(Long merchantId) {
int dbIndex = merchantId % 8;
return "db_" + dbIndex;
}
public String getTargetTable(Date orderTime) {
return "t_order_" + DateUtil.format(orderTime, "yyyyMM");
}
}(3) 索引优化:
- 商户维度查询:merchant_id + create_time复合索引
- 订单查询:order_no唯一索引
- 支付状态查询:pay_status + create_time复合索引
- 性能优化措施:
除了分库分表,我们还采取了以下优化措施:- 引入分布式主键生成器,避免跨库ID冲突
- 优化SQL语句,避免跨库关联查询
- 添加适当的冗余字段,减少关联查询
- 实现异步订单归档机制,定期归档历史订单
- 数据迁移方案:
采用了平滑迁移策略:- 准备阶段:预先创建新库表结构
- 双写阶段:新订单双写新旧库
- 存量迁移:按照商户维度分批迁移历史数据
- 切换阶段:验证数据一致性后切换读写
PS:关于分库分表数据迁移的完整方案实现参考直播课《面试必问海量数据分库分表架构实战》
- 异常处理:
设计了完善的异常处理机制:- 统一异常错误处理
- 路由失败:降级到默认库集群(备份)
这个点一般在大厂高可用系统里用得比较多,我举个详细场景说明:
某电商平台的订单支付系统,商户ID为88888888的订单支付请求到来时,恰好目标分库db_0发生故障。
- 正常路由过程:
// 计算目标分库 Long merchantId = 88888888L; int dbIndex = merchantId % 8; // 结果为0 String targetDb = "db_0";
// 发现db_0不可用,触发降级
if (!isDatabaseAvailable("db_0")) {
return "db_default";
}
- 默认库的数据结构:
-- db_default库中的表结构(与原表完全一致)
CREATE TABLE t_order (
id BIGINT PRIMARY KEY,
merchant_id BIGINT,
order_amount DECIMAL(10,2),
pay_status VARCHAR(20),
create_time DATETIME,
-- 额外增加的字段,用于追踪
target_db VARCHAR(20), -- 记录原目标库
target_table VARCHAR(50), -- 记录原目标表
sync_status VARCHAR(20) -- 同步状态
);- 数据处理流程:
@Transactional
public void handleOrderPayment(Order order) {
try {
// 1. 尝试写入目标库
String targetDb = orderRouter.getTargetDatabase(order.getMerchantId());
if (!isDatabaseAvailable(targetDb)) {
// 2. 目标库不可用,写入默认库
saveToDefaultDatabase(order);// 3. 记录同步信息 markForSync(order, targetDb); // 4. 触发异步同步任务 asyncSyncTask.submit(order.getId()); }
} catch (Exception e) {
log.error("Order payment failed", e);
throw new PaymentException("Payment processing failed");
}
}
- 数据同步机制:
@Scheduled(fixedDelay = 5000) // 每5秒执行一次
public void syncFromDefaultDatabase() {
// 1. 查询待同步的订单
List<Order> pendingOrders = findPendingOrders();for (Order order : pendingOrders) { try { // 2. 检查目标库是否恢复 if (isDatabaseAvailable(order.getTargetDb())) { // 3. 同步到目标库 syncToTargetDatabase(order); // 4. 更新同步状态 updateSyncStatus(order, "SYNCED"); } } catch (Exception e) { // 5. 同步失败,记录失败次数,后续重试 updateSyncStatus(order, "SYNC_FAILED"); log.error("Sync failed for order: " + order.getId(), e); } }
}
默认库的关键特点:
- 数据临时性:
- 仅在目标库不可用时使用
- 数据最终会同步到目标库
- 同步完成后可以清理
- 完整性:
- 与原表结构完全一致
- 增加同步相关的管理字段
- 保存完整的业务数据
- 同步机制:
- 异步同步到目标库
- 定时检查目标库可用性
- 失败重试机制
- 同步状态追踪
这种机制的优势:
- 保证业务连续性:即使分库出现故障,订单支付仍能正常进行
- 数据不会丢失:所有数据都会被妥善保存
- 最终一致性:通过同步机制确保数据最终一致
- 可追踪性:完整的同步状态记录
PS:如果用了分库分表的中间件,比如shardingsphere等,这些中间件一般是会给目标库(主库)配置从库,从库会保持跟主库同步一致,当目标库出问题时,通过shardingsphere的配置可以让操作故障转移到从库去。
7. 监控运维:
实现了全方位监控:
- SQL执行时间监控
- 分片路由成功率监控
- 表容量监控
- 查询性能监控
8. 实际效果:
改造后达到以下效果:
- 单表数据量控制在1000万以内
- 查询响应时间从800ms降到200ms以内
- 系统吞吐量提升3倍
- 支持水平扩展,可以通过增加分库数量提升容量
四、实现了基于风控规则引擎的交易监控系统,支持灵活配置风控规则,有效拦截异常交易
业务背景:
作为支付公司,我们每天要处理数百万笔交易,需要在毫秒级别完成风控决策。风控规则涉及交易金额、频次、商户画像、用户行为等多个维度,且规则需要能够快速调整以应对新的风险。
关于规则引擎的详细代码实现可以参考直播课《传统CRUD保险系统亮点与难点优化实战》
五、开发了支付结果实时通知重试机制,通过指数退避算法,确保通知成功率达到99.99%
这个点面试官可能会问到指数退避算法,解释如下:
指数退避是一种在重试操作时,能够自动调整等待间隔的算法。每次重试失败后,等待时间会以指数形式增加,同时添加一定随机性,避免多个客户端同时重试导致的'惊群效应'。(这个随机性可以避免大量客户端同时重试,导致服务器在固定时间点承压过重)
六、服务器部署情况
参考保险分销平台的例子
更新: 2025-01-21 21:30:42
原文: https://www.yuque.com/tulingzhouyu/db22bv/fhtiwxnmcgg0nztu