前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • HTTP

  • 浏览器相关

  • 计算机基础

    • Linux
    • Network
    • OS
    • SQL
    • MySQL
    • Redis
    • Socket
    • 攻击技术
    • 缓存
    • 集群

# 一、概述

30 秒速记

  • Redis = 内存键值数据库,快在内存访问 + 高效数据结构 + 单线程执行命令没有锁竞争
  • 键是字符串,值有五种基础类型:STRING / LIST / SET / HASH / ZSET(后来又有 Stream、Bitmap、HyperLogLog 等)
  • 持久化防重启丢数据(RDB 快照 / AOF 日志),复制扩展读,分片扩展写,三者各管各的
  • 单条命令原子,多条命令组合不原子:先 GET 再 SET 照样会并发出错
  • 快不代表所有命令都快:KEYS *、大 key 删除、HGETALL 大哈希都会卡住整个实例

Redis 是一个把数据放在内存里的键值数据库,最大的特点就是快,而且值不只是字符串,还有列表、集合、哈希、有序集合这些现成的数据结构。 快主要因为三点:数据在内存,常用操作是 O(1) 或 O(log N),命令由一个线程顺序执行,没有锁开销(Redis 6 加的多线程只用在网络读写上)。前端最常接触的用法是缓存接口数据、存登录会话、做计数和排行榜。它能把数据持久化到磁盘、用主从复制扩展读、用分片扩展写。踩坑最多的是单线程这一点:一条慢命令会让所有客户端一起等。

Redis 是速度非常快的非关系型(NoSQL)内存键值数据库,可以存储键和五种不同类型的值之间的映射。

键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合。

Redis 支持很多特性,例如将内存中的数据持久化到硬盘中,使用复制来扩展读性能,使用分片来扩展写性能。

原理拆解: 一次访问通常经历客户端编码命令、网络传输、服务端查找键、执行数据结构操作、返回结果几个环节。多数普通命令以单条命令为原子执行单位,因此并发客户端不会观察到某条命令执行到一半的状态;但由多条命令组成的“先读后写”流程仍可能发生竞争,需要使用原子命令、事务或脚本合并操作。Redis 的低延迟来自内存访问和高效数据结构,并不意味着所有命令都是常数复杂度,遍历大集合、删除超大键或返回大量数据仍可能拉高延迟。

最小验证: 输入一个计数键、散列表和有序集合,验证原子自增、字段级更新、按分值排序及过期时间。以下命令要求本机已有正在运行的 Redis,可直接交给 redis-cli 执行:

redis-cli <<'EOF'
DEL demo:count demo:user:1 demo:rank
SET demo:count 0
INCR demo:count
INCRBY demo:count 4
HSET demo:user:1 name Alice level 3
HINCRBY demo:user:1 level 1
ZADD demo:rank 80 Alice 95 Bob 88 Carol
GET demo:count
HGETALL demo:user:1
ZREVRANGE demo:rank 0 1 WITHSCORES
EXPIRE demo:count 60
TTL demo:count
EOF
@前端进阶之旅: 代码已经复制到剪贴板

INCR 与 INCRBY 直接在服务端完成读改写,计数结果应为 5;HINCRBY 后 level 应为 4;排行榜前两名应为 Bob、Carol;最后的 TTL 通常接近 60。若键原先保存的不是可解析整数,增量命令会报类型或整数错误,而不会静默覆盖原值。

边界与排查: 过期时间只表达缓存生命周期,不能替代持久化;内存达到上限后的行为还取决于 maxmemory 与淘汰策略。排查性能时应同时观察慢命令、键和值大小、客户端连接数、网络往返和热点键分布。批量请求可用流水线减少往返,但不会降低命令本身的计算量;分片也只能分散不同键,单个高频键仍需通过拆键、本地缓存或限流治理。验证可靠性时还应分别模拟进程退出、主从延迟和节点故障,因为“命令已返回”与“数据已持久化或已复制”并非天然等价。

面试官追问

追问 1库存扣减写成先 GET、算完再 SET,压测时超卖了,单条命令不是原子的吗?
参考回答

单条是原子的,两条之间不是。两个请求可能都读到 1,都算出 0 写回去。改成一条原子命令:

需要判断加更新的复杂逻辑就用 Lua 脚本,整个脚本执行期间不会被其他命令插队。

追问 2用 pipeline 批量发 20 条命令,能降低每条命令的执行开销吗?
参考回答

不能,它省的是网络往返,20 次 RTT 变成 1 次,每条命令在服务端该花多少时间还是多少。pipeline 也不保证原子,中间可能插进别的客户端的命令。

追问 3单节点写入扛不住了,加从库还是做分片?
参考回答

做分片。从库只能分担读,写还是全打在主库上。Redis Cluster 把 key 按 CRC16 映射到 16384 个槽,分散到多个节点;但如果压力集中在一个热点 key 上,分片也救不了,得拆 key 或者加本地缓存。

追问 4接口延迟突然升高,Redis 在内存里应该不是它的问题吧?
参考回答

不能这么排除。先看 SLOWLOG GET,常见元凶是 KEYS *、一次性取上万成员的集合、DEL 一个几百 MB 的大 key。单线程模型下一条慢命令会让后面所有请求排队。

追问 5SET 返回了 OK,进程立刻崩了,数据一定还在吗?
参考回答

不一定。OK 只说明写进了内存。RDB 是隔一段时间拍快照,会丢最后一段;AOF 默认 appendfsync everysec,最多丢约 1 秒;主从复制是异步的,主库挂了从库可能还没收到。

# 二、数据类型

30 秒速记

  • 选类型看要做什么操作,不看「功能多不多」
  • STRING:缓存 JSON、计数器(INCR)、分布式锁(SET NX EX)
  • LIST:按顺序进出,消息队列、最新动态;SET:去重、判断存在、交并差、随机抽取
  • HASH:对象按字段读写,比如用户资料只改昵称;ZSET:带分数排序,排行榜、延时队列
  • 同一个 key 只能是一种类型,用错命令会报 WRONGTYPE

Redis 五种基础类型各管一类场景:STRING 存单个值,LIST 管顺序,SET 管去重,HASH 管对象字段,ZSET 管排名。 选型我一般反过来想,先问业务要做什么操作:要按写入顺序消费就是 LIST;要判断用户有没有点过赞、两个活动重合了哪些人就是 SET;用户资料经常只改一个字段就用 HASH,比整个 JSON 序列化成 STRING 再覆盖省事,也不怕并发时互相覆盖;积分排行榜取前十就是 ZSET。别因为 ZSET 能力最多就什么都用它,内存和复杂度都更高。

数据类型 可以存储的值 操作
STRING 字符串、整数或者浮点数 对整个字符串或者字符串的其中一部分执行操作
对整数和浮点数执行自增或者自减操作
LIST 列表 从两端压入或者弹出元素
对单个或者多个元素进行修剪,
只保留一个范围内的元素
SET 无序集合 添加、获取、移除单个元素
检查一个元素是否存在于集合中
计算交集、并集、差集
从集合里面随机获取元素
HASH 包含键值对的无序散列表 添加、获取、移除单个键值对
获取所有键值对
检查某个键是否存在
ZSET 有序集合 添加、获取、删除元素
根据分值范围或者成员来获取元素
计算一个键的排名

What Redis data structures look like

面试官追问

追问 1积分排行榜,有人说用 SET 存用户,因为不会重复,可以吗?
参考回答

不行,SET 是无序的,没法按积分排。用 ZSET:ZINCRBY rank 10 user:1 加分,ZRANGE rank 0 9 REV WITHSCORES 取前十(Redis 6.2 之前用 ZREVRANGE)。分数相同时按成员名字典序排。

追问 2用户资料每次只改一个字段,却整个对象 JSON.stringify 后 SET 覆盖,有什么问题?
参考回答

两个接口同时改不同字段,后写的会把先写的覆盖掉,每次还要传整个对象。改成 HASH,HSET user:1 nickname Tom 只动一个字段。如果对象总是整体读写,STRING 存 JSON 反而更简单。

追问 3消息队列要求同一消息只保留一次,继续用 LIST 会怎样?
参考回答

LIST 不去重,同一条消息推两次就有两份。要顺序加去重,可以用 ZSET 把时间戳当分数,同一成员再写只会更新分数;或者 LIST 加一个 SET 记录已入队的 ID,两步操作要放进 Lua 脚本里保证原子。

追问 4随机抽奖加计算两个活动的重合用户,用什么?
参考回答

SET。SRANDMEMBER 随机抽不删除,SPOP 抽完删除,SINTER 算交集。不需要分数排序就没必要上 ZSET。

追问 5某个 key 执行 HGET 报 WRONGTYPE,怎么回事?
参考回答

这个 key 实际存的不是 HASH,多半是另一处代码用 SET 把它覆盖成了字符串。TYPE key 先确认实际类型,再找哪条写入路径改了它。key 命名带上业务前缀,比如 user:profile:1,能减少这种撞名。

# STRING

30 秒速记

  • SET key value 写入返回 OK,GET 读出值,键不存在返回 (nil)
  • DEL 返回的是删掉了几个 key,不是旧值
  • (nil) = 键不存在,和空字符串 "" 不是一回事;Node.js 客户端里分别对应 null 和 ''
  • 常用参数:SET k v EX 60 设过期,SET k v NX 不存在才写,合起来就是简易分布式锁
  • STRING 存的是二进制安全的字节,单个值最大 512MB;能解析成整数时可以 INCR

STRING 是 Redis

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • HTTP

  • 浏览器相关

  • 计算机基础

    • Linux
    • Network
    • OS
    • SQL
    • MySQL
    • Redis
    • Socket
    • 攻击技术
    • 缓存
    • 集群
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

Redis|计算机基础篇

# 一、概述

30 秒速记

  • Redis = 内存键值数据库,快在内存访问 + 高效数据结构 + 单线程执行命令没有锁竞争
  • 键是字符串,值有五种基础类型:STRING / LIST / SET / HASH / ZSET(后来又有 Stream、Bitmap、HyperLogLog 等)
  • 持久化防重启丢数据(RDB 快照 / AOF 日志),复制扩展读,分片扩展写,三者各管各的
  • 单条命令原子,多条命令组合不原子:先 GET 再 SET 照样会并发出错
  • 快不代表所有命令都快:KEYS *、大 key 删除、HGETALL 大哈希都会卡住整个实例

Redis 是一个把数据放在内存里的键值数据库,最大的特点就是快,而且值不只是字符串,还有列表、集合、哈希、有序集合这些现成的数据结构。 快主要因为三点:数据在内存,常用操作是 O(1) 或 O(log N),命令由一个线程顺序执行,没有锁开销(Redis 6 加的多线程只用在网络读写上)。前端最常接触的用法是缓存接口数据、存登录会话、做计数和排行榜。它能把数据持久化到磁盘、用主从复制扩展读、用分片扩展写。踩坑最多的是单线程这一点:一条慢命令会让所有客户端一起等。

Redis 是速度非常快的非关系型(NoSQL)内存键值数据库,可以存储键和五种不同类型的值之间的映射。

键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合。

Redis 支持很多特性,例如将内存中的数据持久化到硬盘中,使用复制来扩展读性能,使用分片来扩展写性能。

原理拆解: 一次访问通常经历客户端编码命令、网络传输、服务端查找键、执行数据结构操作、返回结果几个环节。多数普通命令以单条命令为原子执行单位,因此并发客户端不会观察到某条命令执行到一半的状态;但由多条命令组成的“先读后写”流程仍可能发生竞争,需要使用原子命令、事务或脚本合并操作。Redis 的低延迟来自内存访问和高效数据结构,并不意味着所有命令都是常数复杂度,遍历大集合、删除超大键或返回大量数据仍可能拉高延迟。

最小验证: 输入一个计数键、散列表和有序集合,验证原子自增、字段级更新、按分值排序及过期时间。以下命令要求本机已有正在运行的 Redis,可直接交给 redis-cli 执行:

redis-cli <<'EOF'
DEL demo:count demo:user:1 demo:rank
SET demo:count 0
INCR demo:count
INCRBY demo:count 4
HSET demo:user:1 name Alice level 3
HINCRBY demo:user:1 level 1
ZADD demo:rank 80 Alice 95 Bob 88 Carol
GET demo:count
HGETALL demo:user:1
ZREVRANGE demo:rank 0 1 WITHSCORES
EXPIRE demo:count 60
TTL demo:count
EOF
@前端进阶之旅: 代码已经复制到剪贴板

INCR 与 INCRBY 直接在服务端完成读改写,计数结果应为 5;HINCRBY 后 level 应为 4;排行榜前两名应为 Bob、Carol;最后的 TTL 通常接近 60。若键原先保存的不是可解析整数,增量命令会报类型或整数错误,而不会静默覆盖原值。

边界与排查: 过期时间只表达缓存生命周期,不能替代持久化;内存达到上限后的行为还取决于 maxmemory 与淘汰策略。排查性能时应同时观察慢命令、键和值大小、客户端连接数、网络往返和热点键分布。批量请求可用流水线减少往返,但不会降低命令本身的计算量;分片也只能分散不同键,单个高频键仍需通过拆键、本地缓存或限流治理。验证可靠性时还应分别模拟进程退出、主从延迟和节点故障,因为“命令已返回”与“数据已持久化或已复制”并非天然等价。

面试官追问

追问 1库存扣减写成先 GET、算完再 SET,压测时超卖了,单条命令不是原子的吗?
参考回答

单条是原子的,两条之间不是。两个请求可能都读到 1,都算出 0 写回去。改成一条原子命令:

需要判断加更新的复杂逻辑就用 Lua 脚本,整个脚本执行期间不会被其他命令插队。

追问 2用 pipeline 批量发 20 条命令,能降低每条命令的执行开销吗?
参考回答

不能,它省的是网络往返,20 次 RTT 变成 1 次,每条命令在服务端该花多少时间还是多少。pipeline 也不保证原子,中间可能插进别的客户端的命令。

追问 3单节点写入扛不住了,加从库还是做分片?
参考回答

做分片。从库只能分担读,写还是全打在主库上。Redis Cluster 把 key 按 CRC16 映射到 16384 个槽,分散到多个节点;但如果压力集中在一个热点 key 上,分片也救不了,得拆 key 或者加本地缓存。

追问 4接口延迟突然升高,Redis 在内存里应该不是它的问题吧?
参考回答

不能这么排除。先看 SLOWLOG GET,常见元凶是 KEYS *、一次性取上万成员的集合、DEL 一个几百 MB 的大 key。单线程模型下一条慢命令会让后面所有请求排队。

追问 5SET 返回了 OK,进程立刻崩了,数据一定还在吗?
参考回答

不一定。OK 只说明写进了内存。RDB 是隔一段时间拍快照,会丢最后一段;AOF 默认 appendfsync everysec,最多丢约 1 秒;主从复制是异步的,主库挂了从库可能还没收到。

# 二、数据类型

30 秒速记

  • 选类型看要做什么操作,不看「功能多不多」
  • STRING:缓存 JSON、计数器(INCR)、分布式锁(SET NX EX)
  • LIST:按顺序进出,消息队列、最新动态;SET:去重、判断存在、交并差、随机抽取
  • HASH:对象按字段读写,比如用户资料只改昵称;ZSET:带分数排序,排行榜、延时队列
  • 同一个 key 只能是一种类型,用错命令会报 WRONGTYPE

Redis 五种基础类型各管一类场景:STRING 存单个值,LIST 管顺序,SET 管去重,HASH 管对象字段,ZSET 管排名。 选型我一般反过来想,先问业务要做什么操作:要按写入顺序消费就是 LIST;要判断用户有没有点过赞、两个活动重合了哪些人就是 SET;用户资料经常只改一个字段就用 HASH,比整个 JSON 序列化成 STRING 再覆盖省事,也不怕并发时互相覆盖;积分排行榜取前十就是 ZSET。别因为 ZSET 能力最多就什么都用它,内存和复杂度都更高。

数据类型 可以存储的值 操作
STRING 字符串、整数或者浮点数 对整个字符串或者字符串的其中一部分执行操作
对整数和浮点数执行自增或者自减操作
LIST 列表 从两端压入或者弹出元素
对单个或者多个元素进行修剪,
只保留一个范围内的元素
SET 无序集合 添加、获取、移除单个元素
检查一个元素是否存在于集合中
计算交集、并集、差集
从集合里面随机获取元素
HASH 包含键值对的无序散列表 添加、获取、移除单个键值对
获取所有键值对
检查某个键是否存在
ZSET 有序集合 添加、获取、删除元素
根据分值范围或者成员来获取元素
计算一个键的排名

What Redis data structures look like

面试官追问

追问 1积分排行榜,有人说用 SET 存用户,因为不会重复,可以吗?
参考回答

不行,SET 是无序的,没法按积分排。用 ZSET:ZINCRBY rank 10 user:1 加分,ZRANGE rank 0 9 REV WITHSCORES 取前十(Redis 6.2 之前用 ZREVRANGE)。分数相同时按成员名字典序排。

追问 2用户资料每次只改一个字段,却整个对象 JSON.stringify 后 SET 覆盖,有什么问题?
参考回答

两个接口同时改不同字段,后写的会把先写的覆盖掉,每次还要传整个对象。改成 HASH,HSET user:1 nickname Tom 只动一个字段。如果对象总是整体读写,STRING 存 JSON 反而更简单。

追问 3消息队列要求同一消息只保留一次,继续用 LIST 会怎样?
参考回答

LIST 不去重,同一条消息推两次就有两份。要顺序加去重,可以用 ZSET 把时间戳当分数,同一成员再写只会更新分数;或者 LIST 加一个 SET 记录已入队的 ID,两步操作要放进 Lua 脚本里保证原子。

追问 4随机抽奖加计算两个活动的重合用户,用什么?
参考回答

SET。SRANDMEMBER 随机抽不删除,SPOP 抽完删除,SINTER 算交集。不需要分数排序就没必要上 ZSET。

追问 5某个 key 执行 HGET 报 WRONGTYPE,怎么回事?
参考回答

这个 key 实际存的不是 HASH,多半是另一处代码用 SET 把它覆盖成了字符串。TYPE key 先确认实际类型,再找哪条写入路径改了它。key 命名带上业务前缀,比如 user:profile:1,能减少这种撞名。

# STRING

30 秒速记

  • SET key value 写入返回 OK,GET 读出值,键不存在返回 (nil)
  • DEL 返回的是删掉了几个 key,不是旧值
  • (nil) = 键不存在,和空字符串 "" 不是一回事;Node.js 客户端里分别对应 null 和 ''
  • 常用参数:SET k v EX 60 设过期,SET k v NX 不存在才写,合起来就是简易分布式锁
  • STRING 存的是二进制安全的字节,单个值最大 512MB;能解析成整数时可以 INCR

STRING 是 Redis 最基础的类型,一个键对应一个值,SET 写、GET 读、DEL 删。 这段命令里有两个返回值值得记:DEL hello 返回 1 表示删掉了一个键,不是返回旧值;删完再 GET 得到 (nil),表示这个键不存在,到了 Node.js 里就是 null,和存了空字符串完全是两回事,前端渲染时要区分开。实际用的时候我很少只写 SET k v,缓存都会带 EX 设过期时间,做锁会用 SET k v NX EX 10,一条命令完成「不存在才写」和设过期,是原子的。

> set hello world
OK
> get hello
"world"
> del hello
(integer) 1
> get hello
(nil)
@前端进阶之旅: 代码已经复制到剪贴板

面试官追问

追问 1GET 返回 (nil),前端直接显示成空字符串可以吗?
参考回答

语义上不对。(nil) 是键不存在,空字符串是存了但内容为空。比如缓存场景下,null 应该去查数据库,'' 说明数据库里就是空的,混在一起会导致缓存穿透或者显示错误。

追问 2DEL hello 返回 1,日志里记成「旧值为 1」,哪里错了?
参考回答

DEL 返回的是删除的键个数。想要旧值,Redis 6.2 起有 GETDEL hello,读取和删除一步完成,还是原子的。

追问 3只在键不存在时才写入,先 GET 判断再 SET 行吗?
参考回答

有并发窗口,两个请求可能都看到不存在然后都写。用一条命令:

追问 4一个 key 原来存文本,现在想改成计数器 INCR,直接用会怎样?
参考回答

值是 world 这种不能解析成整数的,INCR 会报 ERR value is not an integer or out of range,不会覆盖原值。别让两个业务共用一个 key,换个新 key 名做计数最干净。

追问 5缓存 key 用 SET 写进去后忘了设过期,会有什么后果?
参考回答

这个 key 会一直留在内存里,数据变了也不会自动失效,maxmemory 满了还要看淘汰策略能不能淘汰它(volatile-* 系列只淘汰设了过期的 key)。另外注意:对已有过期时间的 key 再执行普通 SET 会把过期时间清掉。

# LIST

← MySQLSocket →

fe