数据结构与命令
001 用 Redis 统计页面访问次数,最合适的数据类型与命令是?
难度: 基础
- A. String 配合 INCR/DECR 原子自增
- B. List 配合 LPUSH 每次记一条
- C. Hash 每次 HSET 一个新字段
- D. Set 每次 SADD 相同成员
查看答案与解析
正确答案A
正确原因: INCR 是原子操作,天然适合计数器场景,读写都是 O(1)。
关键边界: INCR 对不存在的 key 从 0 开始,值必须是 64 位有符号整数。
错误选项辨析: List、Hash、Set 会累积大量元素或字段,需要额外聚合,语义不匹配计数。
002 缓存用户对象(多字段)时,推荐的数据类型是?
难度: 基础
- A. Hash,字段级读写,内存占用通常优于多个 String
- B. 用多个独立 String 拼 JSON
- C. 用 Set 存字段名
- D. 用 ZSet 按字段权重排序
查看答案与解析
正确答案A
正确原因: Hash 可按字段读写,避免整对象序列化与反序列化,内存更紧凑。
关键边界: 字段过多或值过大时可能触发编码升级,需评估容量。
错误选项辨析: 多 String 增加 key 数量与网络开销,Set 与 ZSet 的集合语义不符合对象存储。
003 实现“最新消息列表 / 消息队列”常见使用什么?
难度: 基础
- A. String 的 APPEND
- B. List 的 LPUSH 加 RPOP(或 BRPOP)
- C. Set 的 SADD
- D. ZSet 的 ZADD 带随机分数
查看答案与解析
正确答案B
正确原因: List 是双向链表,一端写入另一端取出即为队列,BRPOP 支持阻塞等待。
关键边界: 简单任务队列可用 List,复杂消费确认场景建议引入 Stream。
错误选项辨析: String 是单值,Set 无序去重,ZSet 按分数排序,都不是队列语义。
004 实现“抽奖去重 / 共同好友”适合用什么?
难度: 基础
- A. String 的 GETSET
- B. Set 的 SADD、SISMEMBER 与 SINTER
- C. List 的 LRANGE
- D. ZSet 的 ZSCORE
查看答案与解析
正确答案B
正确原因: Set 天然去重且支持集合运算,SINTER 求交集可实现共同关注。
关键边界: 大集合的运算复杂度与集合大小相关,可考虑分片。
错误选项辨析: String 单值、List 有序可重复、ZSet 按分数排序,都不适合去重与集合运算。
005 实时排行榜(按分数排序且高频更新)适合用什么?
难度: 基础
- A. List 按插入顺序排序
- B. Hash 存储后应用层排序
- C. ZSet,ZADD 更新分数,ZREVRANGE 取 Top N
- D. String 存 JSON 每次全量排序
查看答案与解析
正确答案C
正确原因: ZSet 以 score 排序且成员唯一,ZINCRBY 可原子更新积分,查询复杂度 O(log N)。
关键边界: 相同分数按字典序排列,需注意排行榜平局策略。
错误选项辨析: List 无序、Hash 无排序能力、String 无法高效排序更新。
006 String 类型的底层编码机制是?
难度: 基础
- A. 总是使用 skiplist
- B. 总是使用 quicklist
- C. 整数用 int,短字符串用 embstr,长字符串用 raw
- D. 总是使用 hashtable
查看答案与解析
正确答案C
正确原因: Redis 根据内容与长度选择编码:整数用 int,小于阈值的短字符串用 embstr,更长的用 raw。
关键边界: 可通过 OBJECT ENCODING 查看,编码是内部实现细节。
错误选项辨析: skiplist 用于有序集合,quicklist 用于列表,hashtable 用于哈希与集合。
007 Redis List 的底层编码通常是?
难度: 基础
- A. 只用单条连续内存
- B. 只用跳表
- C. 只存哈希桶
- D. quicklist(由紧凑节点组成的链表)
查看答案与解析
正确答案D
正确原因: quicklist 结合了紧凑存储与两端操作效率,是 List 的默认底层结构。
关键边界: 旧版本曾使用 linkedlist 与 ziplist 组合,实现随版本演进。
错误选项辨析: 单条连续内存无法支持任意位置插入,跳表与哈希桶是其他结构的实现。
008 小规模 Hash 与 ZSet 的底层编码何时升级?
难度: 基础
- A. 一旦创建就是 hashtable,永不改变
- B. 编码由客户端指定,服务端不管理
- C. 小数据也使用 skiplist,浪费内存
- D. 元素少且值小时用紧凑编码,达到阈值后转为 hashtable 或 skiplist
查看答案与解析
正确答案D
正确原因: 为节省内存,小数据用 ziplist/listpack 紧凑编码,超过阈值自动转换。
关键边界: 阈值由 hash-max-listpack-entries 等配置控制,转换不可逆。
错误选项辨析: 编码由服务端自动管理,小数据不会直接用 skiplist。
009 SETNX 在分布式锁中的角色是?
难度: 基础
- A. 只在 key 不存在时设置成功,作为“加锁”的原子原语
- B. 无论 key 是否存在都覆盖值
- C. 只能用于普通计数
- D. 会自动为 key 设置过期时间
查看答案与解析
正确答案A
正确原因: SETNX 返回 1 表示抢锁成功,0 表示已存在,是原子加锁原语。
关键边界: 需用 SET key value NX EX seconds 同时设置过期,避免锁永不释放。
错误选项辨析: SETNX 不覆盖已存在值,不自动设过期,用途是互斥而非计数。
010 EXPIRE 命令与 TTL 返回值的关系是?
难度: 基础
- A. EXPIRE 设置 key 的过期秒数;TTL 返回剩余秒,-1 表示无过期,-2 表示 key 不存在
- B. TTL 返回 -1 表示 key 不存在
- C. EXPIRE 后 key 立即被删除
- D. 过期时间只能设置一次,无法修改
查看答案与解析
正确答案A
正确原因: TTL 自 Redis 2.8 起用 -2 表示 key 不存在,-1 表示无过期时间。
关键边界: EXPIRE 可重复设置并支持 NX/XX/GT/LT 条件选项。
错误选项辨析: -1 表示无过期而非不存在,EXPIRE 不会立即删除,过期可被修改或清除。
011 对 key 执行 INCR 后,该 key 的过期时间会怎样?
难度: 进阶
- A. 自动清除,key 变为永久
- B. 保持不变,因为 INCR 是修改值而非覆盖值
- C. 自动延长一倍
- D. 自动缩短为原来一半
查看答案与解析
正确答案B
正确原因: 只有 DEL、SET、GETSET 等覆盖类命令会清除过期时间,修改类命令保留 TTL。
关键边界: LPUSH、HSET 等修改操作同样不会清除过期时间。
错误选项辨析: 清除、延长、缩短都是对过期语义的误读。
012 ZADD 添加 n 个成员的时间复杂度是?
难度: 进阶
- A. O(1),与成员数无关
- B. O(log N),每个成员 O(log N)(N 为成员数)
- C. O(N 平方),随数据量爆炸
- D. O(N),但 N 指网络节点数
查看答案与解析
正确答案B
正确原因: ZSet 底层使用跳表与哈希表,插入需按分数定位,单个成员 O(log N)。
关键边界: 批量添加 M 个成员的复杂度为 O(M log N)。
错误选项辨析: O(1) 与 O(N²) 都不符合跳表结构,N 指集合成员数而非节点数。
013 OBJECT ENCODING 命令的作用是?
难度: 进阶
- A. 修改 key 的编码
- B. 查看 key 的过期时间
- C. 查看 key 的底层编码(如 embstr、int、quicklist、ziplist)
- D. 统计 key 的访问次数
查看答案与解析
正确答案C
正确原因: OBJECT ENCODING 是只读内省命令,用于排查内存与编码转换问题。
关键边界: 编码是内部实现细节,业务代码不应依赖具体编码值。
错误选项辨析: 它不修改任何状态,与过期时间、访问统计无关。
014 SINTER 与 SUNION 的区别是?
难度: 进阶
- A. 两者都只返回第一个集合
- B. SINTER 返回并集,SUNION 返回交集
- C. SINTER 求交集,SUNION 求并集
- D. 两者都会修改原集合
查看答案与解析
正确答案C
正确原因: SINTER 返回多个集合的交集,SUNION 返回并集。
关键边界: 普通集合操作不修改原集合,SINTERSTORE、SUNIONSTORE 才写入目标 key。
错误选项辨析: 交集并集的定义不会互换,只读操作不修改原集合。
015 INCR 的原子性体现在哪里?
难度: 进阶
- A. INCR 依赖客户端加锁
- B. INCR 通过 Lua 脚本模拟实现
- C. INCR 先返回旧值再异步累加
- D. 单命令在服务端串行执行,读-改-写不会被其他命令穿插
查看答案与解析
正确答案D
正确原因: Redis 命令在单线程中原子执行,INCR 的读取、累加、写回不会被打断。
关键边界: 原子性限于单个命令,跨命令逻辑需要 Lua 或事务。
错误选项辨析: INCR 无需客户端锁、不是 Lua 模拟,也不存在异步返回旧值。
016 使用 GETRANGE / SETRANGE 操作大字符串的隐患是?
难度: 进阶
- A. 字符串可以无限大且操作免费
- B. 这两个命令会自动压缩数据
- C. 字符串不能分段读写,必须整体替换
- D. 可能引发大 value 复制与传输开销,误用会放大内存与带宽
查看答案与解析
正确答案D
正确原因: GETRANGE 可能返回大块数据,SETRANGE 可能触发扩展分配,大字符串操作成本高。
关键边界: 字符串上限 512MB,应避免产生大 key。
错误选项辨析: 字符串有限且操作有成本,不会自动压缩,支持分段读写。
017 业务中出现单个 50MB 的 String(大 key),主要风险是?
难度: 实战
- A. 阻塞单线程命令执行、内存碎片、复制与持久化放大
- B. 大 key 对性能没有影响
- C. 大 key 会自动分片到多节点
- D. 大 key 只能删除,无法拆分
查看答案与解析
正确答案A
正确原因: 大 key 的读写、删除、迁移都可能阻塞单线程,并放大复制与持久化开销。
关键边界: 应拆分为 Hash 字段或分段存储,控制单 key 大小。
错误选项辨析: 大 key 有明显性能影响,不会自动分片,可以拆分治理。
018 key 命名的最佳实践是?
难度: 实战
- A. 无规则随机命名,越短越好
- B. 使用“业务:对象:id”风格的分层短命名,便于维护与 SCAN 匹配
- C. 用空格和换行分隔多个 key 名
- D. 所有 key 必须使用同一个名字
查看答案与解析
正确答案B
正确原因: 冒号分层的命名便于人工识别与批量管理,配合 SCAN 通配匹配。
关键边界: 命名要有统一规范并写入文档,避免团队各自为政。
错误选项辨析: 随机或冲突的命名会带来维护灾难,key 名是二进制安全的字符串但应遵循规范。
019 MULTI/EXEC 事务与 Pipeline 的区别是?
难度: 基础
- A. Pipeline 比事务更严格,具备回滚
- B. MULTI/EXEC 可以跨客户端共享
- C. 事务保证命令连续执行(EXEC 前不执行);Pipeline 只批量发送减少 RTT,不保证原子
- D. 两者完全相同,只是名字不同
查看答案与解析
正确答案C
正确原因: 事务在 EXEC 时整体提交,命令间不穿插;Pipeline 只是网络层优化。
关键边界: Redis 事务不支持回滚,命令错误时其余命令仍会执行。
错误选项辨析: Pipeline 无原子保证,事务绑定单个客户端连接,两者机制不同。
020 为什么生产环境避免使用 KEYS 命令?
难度: 进阶
- A. KEYS 只能查一个 key,SCAN 才能查多个
- B. KEYS 会自动删除过期 key
- C. KEYS 比 SCAN 更高效且不阻塞
- D. KEYS 全量扫描所有 key 并阻塞单线程,SCAN 以游标分批返回更安全
查看答案与解析
正确答案D
正确原因: KEYS 在超大 key 空间中会长时间阻塞服务,SCAN 每次返回少量结果。
关键边界: SCAN 结果不保证是单次一致快照,迭代期间新增 key 可能漏报。
错误选项辨析: KEYS 支持模式匹配多个 key,不会删除 key,且大数据量下阻塞严重。