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

设计一个高可用、高并发的审核接口,处理每日数百万条不良资产处置记录的审核请求,请说明技术选型、架构设计及性能优化策略。

中国长城资产管理股份有限公司审核岗难度:中等

答案

1) 【一句话结论】采用微服务+异步消息队列(如Kafka)+分布式缓存(Redis)+数据库分库分表(读写分离)的架构,通过解耦、缓存加速、水平扩展,确保高可用与高并发处理数百万条不良资产处置记录。

2) 【原理/概念讲解】
解释核心概念,避免空话与模板化类比:

  • 高可用:系统故障时仍能提供服务,通过集群冗余(服务实例多副本、数据库主从复制)实现,故障时自动切换(如银行系统主库故障时从库接管)。
  • 高并发:同时处理大量请求,核心手段包括负载均衡(分发请求到多实例)、缓存(减少数据库压力)、异步处理(削峰填谷,如订单系统订单推入队列后异步处理)。
  • 消息队列(如Kafka):解耦系统,缓冲请求,当请求量超过处理能力时,队列暂存请求,消费者异步处理,避免接口阻塞(如快递中转站分批处理包裹,避免快递员超负荷)。
  • 分布式缓存(如Redis):存储热点不良资产信息(如资产ID、状态),减少数据库查询次数,提升响应速度(如电商首页商品信息缓存,减少网络请求)。
  • 数据库分库分表:将数据水平切分到多个数据库实例(按资产ID哈希分片),增加读写能力,避免单库性能瓶颈(如大型超市分多个货架,每个货架负责不同商品,避免拥挤)。

3) 【对比与适用场景】
以消息队列(Kafka)与数据库的对比为例:

对比项消息队列(如Kafka)数据库(如MySQL)
定义分布式消息系统,用于异步通信和解耦关系型数据库,用于数据持久化
核心特性异步、解耦、高吞吐、持久化同步、事务一致、成熟
优点可水平扩展、削峰填谷、解耦系统事务强一致性、成熟生态
缺点需要消费者处理,可能丢失(需重试)扩展性差(垂直扩展有限)、写入慢
适用场景高并发、异步处理(如审核请求)、解耦系统事务敏感、数据量小、实时查询

缓存穿透解决方案:

  • 空值缓存:缓存null值,避免查询空数据导致数据库压力(如查询不存在的资产ID时,缓存null并设置过期时间)。
  • 布隆过滤器:预过滤不存在的key,减少数据库压力(如判断资产ID是否存在,先查布隆过滤器,存在再查数据库)。

4) 【示例】(伪代码,含连接池、事务管理)

  • 审核接口:
public class AssetReviewController {
    @Autowired
    private KafkaProducer kafkaProducer;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DataSource dataSource; // 数据库连接池

    @PostMapping("/review")
    public ResponseDTO review(@RequestBody AssetReviewRequest request) {
        String requestId = UUID.randomUUID().toString();
        if (!validateRequest(request)) {
            return new ResponseDTO(400, "参数无效");
        }
        // 幂等性检查
        String checkKey = "review:unique:" + requestId;
        if (redisTemplate.opsForValue().get(checkKey) != null) {
            return new ResponseDTO(200, "请求已处理");
        }
        redisTemplate.opsForValue().set(checkKey, "pending", 10L);
        // 推入消息队列
        kafkaProducer.send("asset-review-topic", requestId, request.toJSONString());
        return new ResponseDTO(200, "审核请求已提交,将异步处理");
    }
}
  • 审核服务(带事务与连接池):
public class ReviewService {
    @Autowired
    private ReviewRepository reviewRepository;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DistributedLock distributedLock;

    public void processReview(String requestId) {
        // 幂等性检查
        String checkKey = "review:unique:" + requestId;
        if (redisTemplate.opsForValue().get(checkKey) != null) {
            return;
        }

        // 缓存雪崩处理:本地缓存+分布式锁
        String assetKey = "asset:" + requestId;
        Asset asset = (Asset) redisTemplate.opsForValue().get(assetKey);
        if (asset == null) {
            // 从数据库获取(连接池管理连接)
            try (Connection conn = dataSource.getConnection();
                 PreparedStatement stmt = conn.prepareStatement("SELECT * FROM assets WHERE id = ?")) {
                stmt.setString(1, requestId);
                ResultSet rs = stmt.executeQuery();
                if (rs.next()) {
                    asset = new Asset(rs);
                    redisTemplate.opsForValue().set(assetKey, asset, 3600L); // 1小时过期
                }
            } catch (SQLException e) {
                // 异常处理
            }
        }

        if (asset != null) {
            // 热点数据更新:分布式锁
            String lockKey = "asset:lock:" + asset.getId();
            if (distributedLock.tryLock(lockKey, 10L)) {
                try {
                    boolean isApproved = checkApprovalCondition(asset);
                    // 事务操作(连接池+事务管理)
                    try (Connection conn = dataSource.getConnection();
                         TransactionManager tm = new TransactionManager(conn)) {
                        tm.begin();
                        reviewRepository.updateStatus(requestId, isApproved);
                        tm.commit();
                    } catch (Exception ex) {
                        tm.rollback();
                        throw new RuntimeException("更新失败", ex);
                    }
                } finally {
                    distributedLock.unlock(lockKey);
                }
            } else {
                waitAndRetry(requestId);
            }
        }
    }

    private void waitAndRetry(String requestId) {
        try {
            Thread.sleep(1000);
            processReview(requestId);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

5) 【面试口播版答案】(约90秒)
面试官您好,针对高可用、高并发的审核接口设计,我的核心思路是构建一个解耦、可扩展的架构。首先,采用微服务拆分审核服务,通过分布式消息队列(如Kafka)实现请求异步处理,避免接口直接阻塞;其次,利用Redis缓存热点不良资产信息,减少数据库压力;数据库层面采用分库分表(按资产ID哈希分片)并配置读写分离,提升读写能力;前端通过负载均衡(如Nginx)分发请求到多个服务实例,确保高可用。具体流程是:用户调用审核接口后,请求被推入消息队列,消费者异步处理,过程中从缓存获取数据,数据库分库分表存储,最终保证系统能稳定处理每日数百万条请求。同时,我们通过消息队列的重试机制(至少一次消费,失败后指数退避)、请求ID的幂等性检查(防止重复处理)、缓存雪崩的本地缓存+分布式锁方案(避免热点数据更新时的并发冲突),以及数据库分库分表的热点数据并发控制(乐观锁或分布式锁),确保系统在高并发下稳定运行。

6) 【追问清单】及回答要点:

  • 问:如何保证消息队列中的请求不丢失?
    答:采用Kafka持久化存储,结合消费端重试机制(如至少一次消费,失败后指数退避重试),并设置死信队列处理无法重试的消息。
  • 问:如何处理审核请求的幂等性?
    答:在消息队列中添加唯一标识(如请求ID),或数据库操作前检查状态,确保重复请求不重复处理。
  • 问:缓存雪崩如何解决?
    答:设置合理过期时间(如1小时),并采用分布式锁保证热点数据更新时的并发安全,或使用本地缓存+分布式缓存双写。
  • 问:数据库分库分表的具体策略?
    答:按资产ID哈希分库分表(如取模),读写分离(从库复制主库数据,提升读性能)。
  • 问:高可用如何实现?
    答:服务实例集群部署,通过负载均衡分发请求,数据库主从复制,消息队列集群,确保单点故障不影响整体服务。

7) 【常见坑/雷区】

  • 坑1:直接用同步处理审核请求,导致接口响应慢,无法处理高并发,应采用异步消息队列。
  • 坑2:缓存未设置过期策略,导致数据不一致或缓存穿透,应设置合理过期时间,并处理空值。
  • 坑3:消息队列未考虑重试机制,导致失败请求丢失,应配置重试策略(如指数退避)。
  • 坑4:数据库分库分表未考虑读写分离,导致读操作阻塞主库,应配置从库用于读操作。
  • 坑5:高可用只考虑单服务实例,未考虑集群部署,应通过负载均衡和冗余实例提升可用性。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1