找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4682

积分

0

好友

641

主题
发表于 3 天前 | 查看: 16| 回复: 0

一个正在思考的像素风格表情包

这是一份精心整理的 Redis 核心知识盘点,涵盖了面试中经常被问及的 20 个关键问题。无论你是正在准备面试,还是希望巩固自己的 Redis 知识体系,相信这份指南都能为你提供清晰的思路和解答。

Redis是什么?

Redis(Remote Dictionary Server)是一个使用 C 语言编写的高性能非关系型键值对数据库。与传统数据库不同,Redis 的数据主要存储在内存中,因此读写速度极快,常被用作缓存。同时,Redis 支持将数据持久化到磁盘,保证了数据的安全性,并且其操作是原子性的。

Redis的优点有哪些?

  1. 基于内存操作:内存的读写速度远超磁盘,这是 Redis 高性能的基础。
  2. 单线程模型:避免了多线程环境下的上下文切换和竞争问题,简化了设计。需要注意的是,这里的“单线程”主要指处理网络请求的核心模块,像持久化等操作会由其他线程执行。
  3. 丰富的数据类型:支持 String、Hash、List、Set、SortedSet 等多种数据结构,适应不同的业务场景。
  4. 支持持久化:提供了 RDB 和 AOF 两种持久化机制,有效防止服务重启或宕机导致的数据丢失。
  5. 支持事务:所有操作都是原子性的,并且支持将多个命令打包后原子性执行。
  6. 支持主从复制:主节点数据可自动同步至从节点,便于实现读写分离和高可用架构。

Redis为什么这么快?

  • 基于内存:数据存储在内存中,直接避免了磁盘 I/O 的瓶颈。
  • 单线程模型(Redis 6.0以前):单线程处理客户端请求,消除了多线程切换和锁竞争带来的开销。
  • I/O 多路复用:采用 epoll、kqueue 等 I/O 多路复用技术,使单个线程能高效处理大量并发连接。
  • 高效的数据结构:每种数据类型底层都经过精心设计和优化,以追求极致的存取速度。

Redis为何选择单线程?

在早期版本中,Redis 选择单线程主要基于以下几点考虑:

  • 避免上下文切换开销:单线程运行,没有多线程切换的性能损耗。
  • 避免同步机制开销:如果采用多线程,就必须引入锁等同步机制来保证数据一致性,这会增加复杂性和性能开销。
  • 实现和维护简单:单线程模型使得内部数据结构的实现无需考虑线程安全问题,代码更简洁,易于维护。

    注:Redis 6.0 引入了多线程 I/O(处理网络读写),但核心的命令执行模块仍然是单线程的,这可以看作是在保持核心简单性的前提下对网络处理瓶颈的优化。

Redis应用场景有哪些?

  1. 缓存热点数据:这是最经典的用法,将数据库中的热点数据缓存在 Redis 中,大幅减轻数据库压力。
  2. 计数器:利用 INCR 等原子操作,可以方便地实现点赞数、网站访问量等统计功能。
  3. 简单消息队列:使用 List 的 LPUSH/BRPOP 或发布/订阅(Pub/Sub)模式,可以实现异步任务队列。
  4. 限速器:结合 INCREXPIRE 命令,可以限制用户在某段时间内的操作频率,常用于登录、秒杀等场景。
  5. 社交关系:利用 Set 提供的交集、并集等操作,可以轻松实现“共同好友”、“共同关注”等功能。

Memcached和Redis的区别?

  1. 线程模型:Redis 主要使用单核(单线程处理命令),Memcached 支持多线程。
  2. 数据结构:Memcached 仅支持简单的 key-value,而 Redis 支持丰富的数据类型。
  3. 持久化:Memcached 不支持数据持久化,重启后数据丢失;Redis 支持 RDB 和 AOF 两种持久化方式。
  4. 高可用:Redis 原生支持主从复制、哨兵和集群模式;Memcached 需要依靠客户端实现数据分片,无内置高可用方案。
  5. 性能:在单核场景下,Redis 的速度通常更快。
  6. 网络模型:Redis 采用单线程的多路 I/O 复用模型,Memcached 使用多线程的非阻塞 I/O 模型。

Redis 数据类型有哪些?

基本数据类型

  1. String:最基础的类型,值可以是字符串、数字或二进制,最大 512MB。
  2. Hash:字段值对(field-value)的集合,适合存储对象。
  3. Set:无序且元素唯一的集合,支持交并差等集合运算。
  4. List:有序、元素可重复的列表,底层是双向链表,支持栈和队列操作。
  5. SortedSet (ZSet):有序集合,每个元素关联一个 score 分值用于排序,适用于排行榜等场景。

特殊数据类型

  1. Bitmap:位图,本质上是二进制位数组,用于状态标记、统计等,非常节省空间。
  2. HyperLogLog:用于基数统计(估算集合中不重复元素的数量),占用空间固定且极小。
  3. Geospatial:用于存储地理坐标信息,支持计算距离、范围查询等。

Redis事务

事务的原理是将多个命令打包,然后让 Redis 一次性、顺序地执行。但它不保证原子性,即中间某条命令失败不会导致已执行命令的回滚。
事务的生命周期:

  1. 使用 MULTI 开启事务。
  2. 后续的命令会被放入队列,不会立即执行。
  3. 使用 EXEC 提交事务,队列中的命令将被依次执行。

Redis事务MULTI/EXEC执行过程示意图

示例:即使中间命令语法错误,其他命令仍会执行。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> set b 1 2 # 语法错误
QUEUED
127.0.0.1:6379> set c 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR syntax error
3) OK

WATCH命令
WATCH 命令用于实现乐观锁。它可以监视一个或多个 key,如果在事务执行前这些 key 被其他命令改动,则当前事务将被取消。

127.0.0.1:6379> watch name # 开始监视name
OK
127.0.0.1:6379> set name 1 # 在事务外修改了name
OK
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set name 2
QUEUED
127.0.0.1:6379> set gender 1
QUEUED
127.0.0.1:6379> exec # 执行事务,因为watch的key被修改,事务返回nil,未执行
(nil)
127.0.0.1:6379> get gender # gender未被设置
(nil)

使用 UNWATCH 可以取消对所有 key 的监视。

持久化机制

持久化是将内存中的数据保存到磁盘,防止服务宕机导致数据丢失。Redis 提供两种方式:RDB 和 AOF。

RDB方式

RDB 是 Redis 默认的持久化方案,它会在特定时间点生成数据集的一个快照(dump.rdb 文件)。
bgsave 是触发 RDB 持久化的主要命令,其执行流程如下:

RDB持久化bgsave命令执行流程图

  • 执行 BGSAVE 命令。
  • 父进程检查是否有子进程正在执行,有则直接返回。
  • 父进程 fork 一个子进程(此过程会短暂阻塞)。
  • fork 完成后,父进程继续处理请求,子进程将内存数据写入临时 RDB 文件。
  • 子进程写入完成后,用新文件替换旧的 RDB 文件。

触发方式

  • 手动触发:执行 SAVE(阻塞)或 BGSAVE(后台异步)命令。
  • 自动触发:根据配置规则(如 save 900 1),或主从复制全量同步时,或执行 shutdown 且未开启 AOF 时。

优点

  • 数据恢复速度快。
  • 生成快照时父进程不进行 I/O,对服务性能影响小。
    缺点
  • 无法做到实时/秒级持久化,可能丢失最后一次快照后的数据。
  • fork 过程在数据量大时可能较耗时。
  • 不同版本 RDB 文件格式可能不兼容。

AOF方式

AOF 通过记录每次写命令来持久化数据,重启时重放命令来恢复数据,解决了数据持久化的实时性问题。
开启 AOF:appendonly yes。写命令会先存入 aof_buf 缓冲区,然后根据 appendfsync 策略同步到磁盘:

appendfsync always   # 每次写命令都同步,最安全但最慢
appendfsync everysec # 每秒同步一次,推荐配置
appendfsync no       # 由操作系统决定同步时机

AOF 工作流程:
AOF持久化工作流程:命令写入、缓冲、同步、重写

  1. 命令追加到 AOF 缓冲区。
  2. 缓冲区根据策略同步到 AOF 文件。
  3. 定期进行 AOF 重写(rewrite),压缩文件体积(将进程内数据转换为最小命令集)。
  4. 重启时加载 AOF 文件恢复数据。

优点

  • 数据安全性高,配置为 everysec 最多丢失1秒数据。
  • append-only 模式写入性能好。
    缺点
  • 文件体积通常比 RDB 大。
  • 数据恢复速度比 RDB 慢。

主从复制

主从复制 是实现 Redis 高可用 的基础。主库(Master)可读写,从库(Slave)只读并同步主库数据。

# 启动主库
redis-server
# 启动从库并指定主库
redis-server --port 6380 --slaveof 127.0.0.1 6379
# 在运行中的Redis实例上执行,成为从库
slaveof 127.0.0.1 6379
# 停止复制,变回主库
SLAVEOF NO ONE

主从复制原理

  1. 从节点连接主节点,发送 PSYNC 命令。
  2. 首次连接触发全量复制:主节点生成 RDB 快照文件发送给从节点。
  3. 从节点接收并加载 RDB 文件到内存。
  4. 主节点将生成 RDB 期间接收到的新写命令(存于缓冲区)发送给从节点,从节点执行这些命令以保持最终一致。
  5. 之后进入命令传播阶段,主节点持续将写命令异步发送给从节点。
  6. 网络断开重连后,可能会触发部分复制(增量复制)。

哨兵Sentinel

主从复制无法自动进行故障转移,哨兵模式在此基础上提供了自动故障发现和转移的能力,实现了高可用。客户端首先连接哨兵获取当前主节点地址。

Redis哨兵监控主从节点架构图

工作原理

  • 每个哨兵以每秒一次的频率向主、从节点及其他哨兵发送 PING 命令。
  • 如果实例超时未回复,哨兵会将其标记为“主观下线”。
  • 如果主节点被标记为主观下线,所有监视它的哨兵会投票确认其是否“客观下线”。
  • 当主节点被判定为客观下线后,哨兵会通过选举出一个领导者哨兵来负责故障转移。
  • 领导者哨兵会选择一个合适的从节点升级为新的主节点,并让其他从节点复制新主节点,同时通知客户端主节点已变更。

Redis cluster

集群模式实现了 Redis 的分布式存储,数据分片存储在多个节点上,解决了哨兵模式下主节点写能力和存储容量受单机限制的问题。最小规模为6节点(3主3从)。
集群采用虚拟槽分区,共有16384个槽,每个节点负责一部分槽。数据通过 crc16(key) % 16384 计算槽位,然后分配到对应节点。

Redis Cluster虚拟槽分区示意图

优点

  • 无中心架构,支持线性扩展。
  • 数据分片存储,可动态调整数据分布。
  • 具备自动故障转移能力。
    缺点
  • 不支持跨节点的批量操作(如 mget 在多个 key 分布于不同节点时效率低)。
  • 事务仅支持同一节点上的多个 key。
  • 只能使用一个数据库(db0)。

过期键的删除策略?

  1. 惰性删除:当客户端访问一个 key 时,Redis 会检查其是否过期,过期则立即删除。
  2. 定期删除:Redis 定期(默认每100ms)随机抽取一些设置了过期时间的 key 进行检查和删除。
  3. 内存驱逐:当 Redis 内存使用达到 maxmemory 限制时,会触发配置的内存淘汰策略来释放空间。

内存淘汰策略有哪些?

当内存使用超过 maxmemory 时,Redis 会根据配置的策略删除部分数据。
Redis 4.0 之前

  • volatile-lru:从已设置过期时间的 key 中,移除最近最少使用的。
  • allkeys-lru:从所有 key 中,移除最近最少使用的。
  • volatile-ttl:从已设置过期时间的 key 中,移除将要过期的。
  • volatile-random:从已设置过期时间的 key 中,随机移除。
  • allkeys-random:从所有 key 中,随机移除。
  • no-eviction:不删除,新写入操作会报错(默认)。

Redis 4.0 之后新增

  • volatile-lfu:从已设置过期时间的 key 中,移除最不经常使用的。
  • allkeys-lfu:从所有 key 中,移除最不经常使用的。

如何保证缓存与数据库双写时的数据一致性?

这是一个经典难题,没有完美的银弹方案,常见思路如下:

  1. 先删缓存,再更新数据库
    • 问题:在删除缓存后、更新数据库前,如果有读请求,会读到数据库旧值并重新写入缓存,导致一段时间内缓存仍是旧数据。
  2. 先更新数据库,再删缓存
    • 问题:在更新数据库后、删除缓存前,读请求可能读到缓存旧数据。但这个时间窗口通常极短,影响相对较小。如果删缓存失败,问题会持续。
    • 优化:引入消息队列或异步重试机制确保缓存删除成功。
  3. 异步更新缓存:将数据库更新操作封装成消息,由消息队列保证顺序,消费者负责更新缓存。这解耦了数据库操作和缓存操作,但系统复杂度增加。

缓存穿透

指查询一个数据库中根本不存在的数据,缓存无法命中,请求直达数据库。如果大量此类请求,数据库压力巨大。
解决方案

  1. 缓存空值:即使查询不到数据,也将一个空值(或特殊标记)缓存起来,并设置较短的过期时间。
  2. 布隆过滤器:将所有可能存在的数据哈希到一个位数组中。查询时,先经过布隆过滤器,如果判断数据不存在,则直接返回,避免查询数据库。

缓存雪崩

大量缓存 key 在同一时间点或时间段失效,导致所有请求直接涌向数据库,造成数据库压力激增甚至崩溃。
解决方案

  • 分散过期时间:给缓存 key 的过期时间加上一个随机值,避免集体失效。
  • 热点数据永不过期:对极热点数据,可以考虑不设置过期时间,或由后台任务异步更新。
  • 构建高可用缓存集群:如使用 Redis Cluster,避免单点故障。

缓存击穿

指一个热点 key 在缓存过期瞬间,同时有大量请求进来,这些请求全部打到数据库,导致数据库压力骤增。
解决方案

  • 互斥锁:当缓存失效时,只允许一个线程去查询数据库并重建缓存,其他线程等待。可以使用 Redis 的 SETNX 命令实现分布式锁。
    public String get(String key) {
    String value = redis.get(key);
    if (value == null) { //缓存值过期
        String lockKey = "lock:" + key;
        // 尝试获取分布式锁,设置30秒超时
        if (redis.set(lockKey, "1", "NX", "PX", 30000) == "1") {  //设置成功,获得锁
            value = db.get(key); // 查数据库
            redis.set(key, value, expire_secs); // 回写缓存
            redis.del(lockKey); // 释放锁
        } else {  // 其他线程已经获得锁,等待后重试
            sleep(50);
            return get(key);  // 重试
        }
    } else {
        return value;
    }
    }

pipeline的作用?

Redis 执行一条命令包含网络往返时间。pipeline 可以将多个命令打包一次性发送,服务器依次执行后再一次性返回结果,极大地减少了网络开销,提升了吞吐量。
注意事项

  • pipeline 不是原子操作,中途异常不会回滚。
  • 打包的命令数不宜过多,避免单次传输数据过大或客户端等待过久。
  • 与原生批命令(MSET/MGET)不同,pipeline 支持任意命令的批量执行。

LUA脚本

Lua 脚本在 Redis 中以原子方式执行,执行期间不会插入其他命令,非常适合用于实现复杂的原子性操作。

> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

作用与场景

  1. 原子性:保证一系列命令的原子执行。
  2. 减少网络开销:将多个命令打包在一个脚本中。
  3. 复杂操作:例如实现限流、库存扣减等需要判断和循环的逻辑。

示例:接口限流

-- KEYS[1]: 接口key, ARGV[1]: 限流次数, ARGV[2]: 时间窗口
local c
c = redis.call('get',KEYS[1])
if c and tonumber(c) > tonumber(ARGV[1]) then
    return c;
end
c = redis.call('incr',KEYS[1])
if tonumber(c) == 1 then
    redis.call('expire',KEYS[1],ARGV[2])
end
return c;

此脚本先检查是否超限,未超限则计数,并在第一次计数时设置过期时间,整个过程是原子的。

希望这份系统的梳理能帮助你更好地理解和掌握 Redis。在实际应用中,还需要结合具体业务场景进行设计和调优。




上一篇:Redis从入门到实践:特性详解、六大应用场景与版本演进指南
下一篇:Redis面试高频30题精解:从缓存原理到集群架构全掌握
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-7 22:02 , Processed in 0.839115 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表