
1) 【一句话结论】采用本地缓存+Redis多级缓存架构,结合写时刷新(Write-Through)与读时过期(Read-Expire)策略,以Redis作为主缓存(支持持久化与事务),通过分布式锁处理并发写,布隆过滤器防缓存穿透,热点数据预热防雪崩,提升高频题目访问速度。
2) 【原理/概念讲解】缓存的核心作用是加速数据访问,减少数据库压力。对于高频题目访问,需设计分层缓存:本地缓存(如Java的ConcurrentHashMap)作为第一级,处理最频繁的访问,减少网络延迟;Redis作为第二级,处理复杂查询(如按标签搜索题目)和持久化。写时刷新(Write-Through):数据更新时,先更新数据库,再同步更新Redis缓存;读时过期(Read-Expire):缓存数据设置过期时间,过期后自动失效,触发数据库查询。类比:缓存像“快速响应的临时仓库”,比数据库(仓库)快,但需通过更新机制管理库存,避免“缺货”(数据不一致)。
3) 【对比与适用场景】
| 特性 | Redis | Memcached |
|---|---|---|
| 定义 | 支持数据持久化(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) 【追问清单】
7) 【常见坑/雷区】