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

学而思的竞赛题库中,某些题目(如经典例题、高频练习题)会被大量学生访问,如何设计缓存策略,提高访问速度?请说明缓存层的选择(如Redis vs Memcached),以及缓存更新机制(如写时刷新,读时过期)。

学而思竞赛教练(C++)难度:中等

答案

1) 【一句话结论】采用本地缓存+Redis多级缓存架构,结合写时刷新(Write-Through)与读时过期(Read-Expire)策略,以Redis作为主缓存(支持持久化与事务),通过分布式锁处理并发写,布隆过滤器防缓存穿透,热点数据预热防雪崩,提升高频题目访问速度。

2) 【原理/概念讲解】缓存的核心作用是加速数据访问,减少数据库压力。对于高频题目访问,需设计分层缓存:本地缓存(如Java的ConcurrentHashMap)作为第一级,处理最频繁的访问,减少网络延迟;Redis作为第二级,处理复杂查询(如按标签搜索题目)和持久化。写时刷新(Write-Through):数据更新时,先更新数据库,再同步更新Redis缓存;读时过期(Read-Expire):缓存数据设置过期时间,过期后自动失效,触发数据库查询。类比:缓存像“快速响应的临时仓库”,比数据库(仓库)快,但需通过更新机制管理库存,避免“缺货”(数据不一致)。

3) 【对比与适用场景】

特性RedisMemcached
定义支持数据持久化(RDB/AOF)、事务、多种数据结构(哈希、列表、集合等)纯内存键值存储,无持久化
特性持久化、事务、Lua脚本、高并发读写简单键值存储,无持久化,支持简单操作
使用场景需要持久化、复杂数据结构(如题目信息包含内容、标签、难度等字段)、高并发读写简单键值缓存、临时数据、快速读写(如用户会话)
注意点持久化配置复杂,事务需谨慎;可能影响性能无持久化,数据丢失风险;不支持复杂数据结构

4) 【示例】(伪代码):
读取题目数据:

def get_problem(problem_id):
    # 检查本地缓存
    if local_cache.get(problem_id):
        return local_cache[problem_id]
    # 检查Redis缓存
    cache_key = f"problem:{problem_id}"
    data = redis.get(cache_key)
    if data:
        local_cache[problem_id] = json.loads(data)  # 写入本地缓存
        return data
    # 数据库查询
    data = db.query(f"SELECT * FROM problems WHERE id = {problem_id}")
    if data:
        # 写入Redis(写时刷新)
        redis.setex(cache_key, 86400, json.dumps(data))
        local_cache[problem_id] = data  # 写入本地缓存
    return data

# 更新题目数据(并发锁+写时刷新)
def update_problem(problem_id, new_data):
    # 分布式锁(SETNX保证原子性)
    lock_key = f"lock:problem:{problem_id}"
    if redis.setnx(lock_key, 1, ex=10):  # 10秒过期锁
        try:
            # 更新数据库
            db.update(f"UPDATE problems SET content = ?, tags = ? WHERE id = ?", 
                      (new_data['content'], new_data['tags'], problem_id))
            # 更新Redis缓存
            cache_key = f"problem:{problem_id}"
            redis.setex(cache_key, 86400, json.dumps(new_data))
            # 释放锁
            redis.delete(lock_key)
        except:
            redis.delete(lock_key)
            raise
    else:
        # 锁被占用,重试或等待
        time.sleep(1)
        update_problem(problem_id, new_data)

5) 【面试口播版答案】
“面试官您好,针对高频题目访问的缓存优化,核心方案是多级缓存架构(本地缓存+Redis),结合写时刷新与读时过期策略。首先,缓存层选择:由于竞赛题库数据需要持久化且包含复杂结构(如题目内容、标签、难度等),我们选择Redis作为主缓存,因为它支持RDB/AOF持久化、事务操作,能保证数据一致性;而本地缓存(如ConcurrentHashMap)作为第一级,减少网络延迟。对于缓存更新,采用写时刷新(Write-Through):题目数据更新时,先更新数据库,再通过分布式锁(Redis SETNX)保证原子性,同步更新Redis缓存。读时过期策略:对于不常变动的题目(如经典例题),设置较长的过期时间(如24小时),减少数据库查询。同时,为应对缓存失效问题,使用布隆过滤器预判热点数据,避免缓存穿透;提前加载热点数据(如每日访问量前100的题目)到缓存,防止缓存雪崩。这样既能快速响应高频访问,又能保证数据一致性和系统稳定性。”

6) 【追问清单】

  1. 缓存失效时如何处理?
    回答要点:设置过期时间,或用布隆过滤器防缓存穿透(查询不存在的数据直接返回空),热点数据预热(提前加载)防雪崩。
  2. 多级缓存中本地缓存和Redis的职责划分?
    回答要点:本地缓存处理最频繁的访问(如用户最近查看的题目),减少网络延迟;Redis处理复杂查询(如按标签搜索题目)和持久化。
  3. 并发写操作如何保证原子性?
    回答要点:使用Redis的SETNX命令实现分布式锁,确保多个线程更新时只有一个能成功,避免数据不一致。
  4. 如何预防缓存雪崩?
    回答要点:对热点数据(如高频访问题目)进行预热,设置不同的过期时间(随机或阶梯式),避免大量数据同时过期。
  5. 缓存穿透如何解决?
    回答要点:布隆过滤器预判热点数据,当查询不存在的数据时,直接返回空,避免缓存和数据库都返回空,浪费资源。

7) 【常见坑/雷区】

  1. 忽略并发锁导致数据不一致(如多个线程同时更新题目数据,导致缓存与数据库数据不同步)。
  2. 未考虑缓存雪崩导致数据库压力激增(大量数据同时过期,触发大量数据库查询,造成系统崩溃)。
  3. 过度依赖缓存导致缓存失效频繁(如缓存命中率低,频繁查询数据库,反而降低性能)。
  4. 未区分多级缓存职责(如本地缓存与Redis功能重叠,增加系统复杂度)。
  5. 缓存更新策略选择不当(如读时过期导致频繁查询数据库,反而增加数据库压力)。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1