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

设计一个高并发的课程搜索系统,用户输入关键词可以快速返回匹配的课程,请说明系统架构(如ES索引、Redis缓存、数据库分库分表),并解释如何处理搜索请求的流量峰值(如开学季)。

好未来Golang难度:困难

答案

1) 【一句话结论】采用分层架构,通过Redis缓存、ES全文检索、分库分表数据库,结合布隆过滤器防缓存穿透、消息队列异步更新ES、Nginx负载均衡、熔断限流,有效应对开学季流量峰值。

2) 【原理/概念讲解】老师讲解时,先明确核心需求——快速响应、高并发读写。逐一解释各组件:

  • Redis:作为缓存层,存储热门搜索结果(如“Python入门”),利用内存存储和纳秒级读写特性,大幅降低ES和数据库压力。类比“超市热销商品货架”,高频查询直接从缓存返回。
  • Elasticsearch (ES):通过倒排索引技术,索引课程标题、描述等文本字段,支持模糊搜索、多条件组合(如“Python入门+免费”)。类比“图书馆索引卡片系统”,快速定位文本内容。
  • 数据库分库分表:按课程类型分库(如编程、数学),按ID范围分表(如编程库中课程ID 1-10000分表1),避免单表过大导致性能瓶颈。类比“分馆管理书籍”,减少查询延迟。
  • 布隆过滤器:存储热门课程ID,检查请求ID是否在布隆过滤器中,若不在再查询ES,防缓存穿透。类比“商品标签验证”,过滤无效请求。
  • 消息队列:异步更新ES索引(如课程变更事件),减少实时性压力。类比“快递分拣中心”,批量处理更新。
  • Nginx负载均衡:四层负载,配置轮询或ip_hash策略,分发请求到多个实例,均摊流量。类比“交通枢纽调度”,避免单点过载。
  • 熔断器(Hystrix)+限流(令牌桶):熔断器配置阈值(如3秒内10次失败),限流每秒1000请求,防雪崩。类比“安全阀”,保护系统稳定。

3) 【对比与适用场景】

组件定义特性使用场景注意点
Elasticsearch分布式搜索引擎支持全文检索、复杂查询、高并发读写、自动分词课程标题/描述的模糊搜索、多条件组合查询索引维护成本高,不适合事务性操作
关系型数据库结构化数据存储强一致性、事务支持、ACID课程基本信息(ID、价格、讲师ID)的增删改查查询复杂时性能下降
Redis内存数据库高速读写、缓存、消息队列热门搜索结果、布隆过滤器缓存穿透/雪崩风险,需设置过期时间
布隆过滤器哈希集合空间高效、可能误判防缓存穿透,检查ID是否存在存在误判率(如1%),需优化位数组长度
消息队列(如Kafka)异步通信解耦、高吞吐ES索引异步更新需确保消息不丢失,处理延迟

4) 【示例】用户搜索“Java基础”,系统处理流程:

  • Nginx负载均衡分发请求到API网关。
  • API网关检查Redis缓存(key: "search:java基础"),若存在,直接返回。
  • 否则,调用ES查询接口(参数:keyword="Java基础",字段:title, description),ES返回匹配课程列表。
  • 将结果存入Redis(key: "search:java基础",value: JSON结果),设置5分钟过期时间,并添加布隆过滤器(key: "bloom:course:java基础",课程ID列表)。
  • 返回结果给用户。

伪代码(简化):

func searchCourses(keyword string) ([]Course, error) {
    // 1. 检查Redis缓存
    cached, err := redisClient.Get("search:" + keyword).Result()
    if err == nil {
        return parseJSON(cached), nil
    }

    // 2. 检查布隆过滤器
    isExist, err := redisClient.PBool("bloom:course:" + keyword, true)
    if err != nil || !isExist {
        // 不存在,调用ES
        esResult, err := esClient.Search(keyword, "title", "description")
        if err != nil {
            return nil, err
        }
        // 存入Redis和布隆过滤器
        redisClient.Set("search:" + keyword, esResult, 5*time.Minute)
        redisClient.PSetBit("bloom:course:" + keyword, 0, 1) // 假设课程ID为0
        return esResult, nil
    }

    return parseJSON(cached), nil
}

5) 【面试口播版答案】(约90秒)
“面试官您好,针对高并发课程搜索系统,我的设计思路是分层架构,核心组件包括Redis缓存、Elasticsearch(ES)和分库分表的数据库。首先,Redis作为缓存层,存储热门搜索词的查询结果,比如“Python入门”这类高频请求,直接从Redis返回,避免ES和数据库压力。然后,ES负责全文检索,通过倒排索引技术快速匹配课程标题、描述等文本字段,支持模糊搜索和多条件组合。数据库方面,我们按课程类型分库,按课程ID范围分表,比如编程类课程放在编程库,数学类在数学库,每个库再分表,避免单表数据过大。流量峰值处理上,开学季时,我们会增加ES的分片数量(比如从5个增加到10个),扩展Redis集群节点,同时用API网关做限流(令牌桶算法,每秒1000请求),防止流量瞬间冲击。针对缓存穿透问题,我们引入布隆过滤器,在Redis中存储热门课程ID的布隆过滤器,检查请求的ID是否在布隆过滤器中,若不在再查询ES,减少无效请求。ES索引更新通过消息队列(如Kafka)接收课程变更事件,定时批量更新索引,或者调整refresh间隔(如1分钟),平衡实时性和性能。负载均衡使用Nginx四层负载均衡,配置轮询策略,将请求分发到多个API网关实例,再由API网关分发到后端服务。熔断器使用Hystrix,配置熔断阈值(3秒内10次失败),确保系统在异常时快速降级。这样整体架构既能保证搜索速度,又能应对开学季的流量峰值。”

6) 【追问清单】

  • 问题1:布隆过滤器如何处理误判?如何优化?
    回答要点:布隆过滤器存在误判率(如1%),可通过增加位数组长度降低误判率,或结合缓存穿透的降级策略,比如误判时直接查询ES。
  • 问题2:ES索引更新时,如何保证数据一致性?比如课程删除后,搜索结果是否立即消失?
    回答要点:通过消息队列异步更新,设置延迟(如1分钟),或者调整refresh间隔,确保数据一致性,避免实时性影响。
  • 问题3:分库分表后,跨库查询如何处理?比如搜索“Python+Java”需要多个库的数据?
    回答要点:使用数据库中间件(如ShardingSphere),配置多库查询,或者将搜索逻辑改为分步查询,先查编程库,再查其他库,合并结果。
  • 问题4:负载均衡中,Nginx的ip_hash和轮询策略分别适用于什么场景?如何配置?
    回答要点:ip_hash适用于会话保持,轮询适用于均摊流量。配置中,upstream backend { server 192.168.1.1:8080; server 192.168.1.2:8080; },使用轮询。
  • 问题5:熔断器中,熔断阈值和恢复时间如何设置?如何避免误熔断?
    回答要点:熔断阈值根据业务调整,比如3秒内10次失败,恢复时间根据服务恢复情况动态调整,避免误熔断影响可用性。

7) 【常见坑/雷区】

  • 缓存穿透未处理:热门课程ID不存在时,大量请求穿透缓存,冲击ES和数据库。
  • 布隆过滤器误判:导致少量无效请求,但影响较小,可通过优化位数组长度降低。
  • ES分片数量不足:开学季时未扩容,导致查询延迟,影响用户体验。
  • 分库分表的主从复制延迟:新课程添加后,从库未同步,导致搜索结果不完整。
  • 限流配置不当:限流阈值过低,导致正常请求被拒绝;过高则无法削峰。
  • 忽略ES索引更新机制:实时性要求高时,调整refresh间隔可能影响性能,需权衡。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1