2)Redis
🌱 第 1 题(基础认知)
Q1:你能简单说一下 Redis 是做什么的吗?它为什么快?
内存型键值数据库(NoSQL)。
✅ 预期回答方向:
- Redis 是一个开源的内存数据结构存储系统;
- 常用作缓存、中间件;
- 快的原因主要是内存 + 单线程 + 高效的数据结构。
- (6.0+ 支持多线程 I/O)
⭐ 加分点:
- 提到 Redis 支持多种数据结构;
- 提及“IO 多路复用”机制。
❌ 减分项:
- 回答模糊,如“就是数据库”;
- 不知道 Redis 和 MySQL 的区别。
📝 标准参考答案:
Redis 是一个基于内存的、支持多种数据结构的高性能 Key-Value 存储系统。
它常用于缓存加速、分布式锁、消息队列、排行榜、会话存储等场景。它之所以快,主要有以下几点原因:
- 数据存储在内存中,访问速度远高于磁盘 IO;
- 使用单线程模型,避免了上下文切换带来的开销;
- 基于高效的数据结构(如跳表、哈希表、压缩列表等);
- 使用 IO 多路复用技术(epoll)提高了并发性能。
🌿 第 2 题(数据结构与应用)
Q2:Redis 支持哪些常用数据结构?你用过它们在哪些场景?
✅ 预期回答方向:
- 讲出 5 种核心数据结构:String、List、Set、Hash、ZSet;
- 能结合业务场景。
⭐ 加分点:
- 额外提到 BitMap、HyperLogLog、Geo、Stream;
- 举出多个具体场景。
❌ 减分项:
- 不知道 Set 和 ZSet 的区别;
- 场景模糊,如“都用来存缓存”。
📝 标准参考答案:
Redis 支持多种数据结构,我在项目中常用的有:
- String:最常见的键值对形式,用于缓存页面数据、Token、验证码等;
- Hash:类似对象结构,用于缓存用户信息,如
user:123 => {name:张三, age:20}
;- List:有序列表,可用作消息队列,如异步日志、任务列表;
- Set:无序集合,常用于去重,如“每日签到用户 ID 集合”;
- ZSet(有序集合):支持按照分数排序,比如用于“积分排行榜”、“活跃度榜单”;
进阶结构中,我还用过:
- BitMap:用于统计用户登录状态(0/1);
- HyperLogLog:用于网页的 UV 去重计数;
- Stream:用于日志流/消息流处理。
对比表格
结构 | 有序性 | 唯一性 | 核心能力 | 时间复杂度 | 典型场景 |
---|---|---|---|---|---|
String | 无 | 键唯一 | 存储简单数据、原子操作 | O(1) | 缓存、计数器 |
List | 是(顺序) | 元素可重复 | 双向操作、顺序访问 | 头尾操作 O(1),中间 O(n) | 消息队列、操作日志 |
Set | 无 | 元素唯一 | 集合运算(交并差) | 添加/查询 O(1) | 标签、去重 |
Hash | 无 | 字段唯一 | 结构化数据存储、字段级操作 | 字段操作 O(1) | 用户信息、商品属性 |
ZSet | 是(分数) | 元素唯一 | 按分数排序、范围查询 | 插入/查询 O(log n) | 排行榜、优先级队列 |
选择建议
- 需要简单键值存储 → String。
- 维护顺序且允许重复 → List。
- 去重且无需顺序 → Set。
- 存储对象属性 → Hash。
- 按分数排序或范围查询 → ZSet。
🌳 第 3 题(缓存机制)
Q3:Redis 做缓存时,如何防止缓存穿透、击穿、雪崩?
✅ 预期回答方向:
- 逐个解释三种问题;
- 给出具体解决方案。
⭐ 加分点:
- 使用布隆过滤器、互斥锁、预热、降级等策略;
- 有实战经验。
❌ 减分项:
- 混淆三个概念;
- 策略空泛,不落地。
📝 标准参考答案:
这三个是 Redis 缓存的常见问题,解决方案如下:
- 缓存穿透:指请求的数据在数据库也不存在,导致每次都打数据库。
解决方案:
- 布隆过滤器:提前记录所有合法 Key;
- 缓存空值:如设置“null”值 + 短 TTL;
- 缓存击穿:某个热点 key 正好过期,瞬间大量请求打向数据库。
解决方案:
- 加互斥锁(互斥缓存);
- 设置随机过期时间,避免同一时间过期;
- 缓存雪崩:大量 key 同时过期,数据库被压垮。
解决方案:
- 给 key 设置随机 TTL(如 60s ± 10s);
- 分布式预热 / 双写缓存 / 降级处理;
在项目中,我们就遇到过缓存击穿问题,通过使用 Redisson 分布式锁进行互斥构建解决了数据库瞬时压力问题。
名词解析
1. 分布式预热
就像在寒冬来临前,所有供暖站提前烧热管道,分布式预热是系统启动时,将高频数据预先加载到各个节点的缓存中,避免用户首次访问时集体“挤爆”数据库。如同提前给每台电梯预调至早高峰楼层,减少等待时间。
2. 双写缓存
类似寄快递时同时填写电子单和纸质单,双写缓存要求每次更新数据时,同时写入缓存和数据库。这就像餐厅换菜单时,服务员(缓存)和后厨(数据库)同步更新菜名,避免客人点到已下架的菜,但需警惕“手忙脚乱写错单”的一致性问题。
3. 降级处理
如同暴雨天关闭公园游乐设施、只保留避雨亭,降级处理是在系统过载时,暂时关闭非核心功能(如商品评论),优先保障核心服务(如支付)。就像节日期间快递公司暂停“次日达”,确保普通包裹不积压,虽不完美,但能“活着撑过洪峰”。
🌲 第 4 题(高并发场景)
Q4:Redis 是单线程的,为什么还能支撑高并发?有没有并发访问下的问题?
✅ 预期回答方向:
- 内存操作+高效架构带来的并发能力;
- 单线程指的是“主线程处理命令”;
- 并发问题如并发写冲突、事务不原子等。
⭐ 加分点:
- 提到 IO 多路复用;
- 提及 Lua 脚本保证原子性;
- 引用真实场景(如并发秒杀)。
❌ 减分项:
- 完全不了解线程模型;
- 以为 Redis 没有并发问题。
📝 标准参考答案:
Redis 使用的是单线程模型(指命令处理线程是单线程的),但它本身非常快,主要因为:
- 所有数据都在内存中,访问速度极快;
- 单线程避免了加锁开销,指令串行执行;
- 使用了 epoll 等 IO 多路复用技术处理网络请求;
所以尽管是单线程,也能支撑数万 QPS。
但 Redis 在高并发下也会有一些问题,比如:
- 并发写冲突:多个客户端同时更新同一个 key 时可能会出现“丢更新”;
- 原子性问题:多个操作组合执行可能不是原子操作;
解决方式包括:
- 使用 事务(MULTI/EXEC);
- 使用 Lua 脚本,保障逻辑原子性;
- 使用 Redisson 分布式锁,控制访问同步。
🪵 第 5 题(系统设计)
Q5:假设你要用 Redis 实现一个“商品秒杀系统”,你会如何设计?有哪些点是关键的?
✅ 预期回答方向:
- 控制库存扣减;
- 高并发控制;
- 数据一致性与最终落库。
⭐ 加分点:
- 使用 Lua 脚本原子扣减;
- 消息队列异步下单;
- 限流、幂等、日志等细节齐全。
❌ 减分项:
- 直接从 Redis 扣库存写数据库;
- 没有幂等设计。
📝 标准参考答案:
商品秒杀系统的核心挑战是高并发下的库存扣减和下单控制。我的设计如下:
初始化库存:将商品库存预加载到 Redis 中,如
goods_stock:10001 = 500
;请求入口限流:Nginx 或网关层用令牌桶/漏斗算法限流,避免 Redis 瞬时压力过大;
库存扣减(原子操作):
- 使用 Lua 脚本在 Redis 中进行库存判断和扣减,确保操作原子;
- 脚本逻辑:
if stock > 0 then decr stock else return 0
;
- 异步下单:
- 将成功抢购的用户写入消息队列(如 Kafka);
- 后端消费者异步消费并落库,确保数据库不被打挂;
幂等校验:每个用户只能抢一次,用 Redis Set 或布隆过滤器做幂等控制;
失败处理和监控:
- 秒杀失败的返回友好提示;
- 加日志监控、报警和黑名单机制;
实战中,我们还使用了 Redis Stream 做消息缓冲和回溯,整体秒杀系统可以稳定支持 10W+ QPS 并发。
名词解析
幂等性(Idempotence)在数学中,指的是一个操作多次执行所产生的影响与一次执行的影响相同。比如,绝对值函数就是幂等的,因为 abs(abs(x))和 abs(x)结果一样。在计算机领域,特别是在网络请求或数据库操作中,幂等性意味着同样的请求执行一次或多次的结果是一致的,不会因为重复执行而导致意外变化。例如,HTTP 的 GET 请求是幂等的,因为它只是获取数据,而 POST 请求通常不是,因为多次提交可能会创建多个资源。
示例代码(伪代码)
友好提示
public Response handleSeckillRequest(User user, Item item) {
// 预检库存
if (item.getStock() <= 0) {
return Response.fail("已售罄,下次早点来哦~");
}
// 其他校验(如黑名单、限流)
if (isUserBlocked(user)) {
return Response.fail("请求过于频繁,请稍后再试");
}
// 执行秒杀逻辑...
}
黑名单机制
技术实现(Redis 为例)
// 记录用户失败次数
String key = "seckill:block:user:" + userId;
long count = redis.incr(key);
redis.expire(key, 10, TimeUnit.SECONDS); // 10秒窗口
if (count > 5) {
// 加入黑名单,封禁5分钟
redis.set("seckill:blacklist:user:" + userId, "1", 5, TimeUnit.MINUTES);
}
总结
通过 友好提示 → 日志埋点 → 监控报警 → 黑名单拦截 的闭环设计,秒杀系统可实现:
- 用户侧:明确反馈,减少无效流量。
- 系统侧:快速定位问题,防御恶意攻击。
- 业务侧:保障核心交易链路,提升整体稳定性。
最终效果类比:
像地铁高峰期的限流措施——
- 显示屏提示“当前站台拥挤”(友好提示);
- 监控摄像头统计人流量(日志监控);
- 触发警报时关闭部分入口(黑名单);
- 引导乘客乘坐公交接驳车(降级方案)。