Skip to main content

Redis 题库

Redis(Remote Dictionary Server)是一种开源的内存数据存储系统,它可以用作数据库、缓存和消息中间件。

基础概念

为什么说redis是单线程服务

Redis不是严格意义上的单线程服务。这里的单线程,主要是指redis的核心执行(网络I/O、键值对读写)是单线程完成的。

类似持久化、数据同步等、还是由其他线程执行。

Redis6.0为什么要引入多线程

Redis6.0在网络IO模块支持多线程,核心键值对读写仍然是单线程。

Redis单线程为什么性能这么好

  1. 基于内存的数据库,相比于传统的数据库,在性能上具有天然的优势。
  2. 核心执行引擎是单线程,在高并发场景下避免了锁竞争。
  3. 通过事件驱动模型IO多路复用,实现客户端请求、网络IO的高效处理。
  4. 高效的数据结构和算法,如:哈希表、跳表等。

事件驱动模型

事件驱动模型是一种异步编程模型。这种模型基于事件循环来执行,也就是通过监听和响应事件来处理输入和输出。

在Redis的主线程中,运行着一个事件循环。该EventLoop不断地监听和处理发生的事件,如新的客户端连接、数据可读或可写等。

优点

相比于多线程、多进程模型来说,事件驱动模型避免了为每一个请求创建一个线程甚至进程,因此事件驱动模型可以轻松处理大量并大请求。 且不会存在系统调度开销。

缺点

  1. 事件驱动模型更适合I/O密集型任务,对于计算密集型任务,不如多线程、多进程模型。
  2. 一旦发生阻塞,会影响整个事件循环

IO多路复用

I/O多路复用是一种操作系统提供的机制,允许单个进程或线程同时监听多个I/O通道的状态,以确定哪个通道有数据可读或可写。这使得程序能够有效地处理多个I/O操作,而不需要为每个I/O通道创建一个独立的线程。主要的I/O多路复用机制包括selectpollepoll

select是一种传统的I/O多路复用机制,最早出现在Unix系统中。它的工作方式是通过一个文件描述符集合,检查每个文件描述符的状态(可读、可写等)。主要的特点包括:

  • 阻塞调用: 当没有任何文件描述符就绪时,select调用会阻塞,等待至少有一个文件描述符就绪。
  • 文件描述符数量限制: 通常有一个限制,例如1024个文件描述符。
  • 轮询开销: select采用轮询的方式检查文件描述符的状态,可能会带来一些开销。

poll是对select的改进,同样用于多路复用I/O。它的一些特点包括:

  • 无文件描述符数量限制: poll没有select的文件描述符数量限制。
  • 阻塞调用:select类似,当没有文件描述符就绪时,poll调用也会阻塞。
  • 逐个扫描: poll通过逐个扫描文件描述符,找出就绪的文件描述符。

epoll是在Linux系统上引入的新一代I/O多路复用机制,相对于selectpoll有更高的性能。它的一些特点包括:

  • 无文件描述符数量限制: epoll也没有文件描述符数量的限制,可以处理数以万计的并发连接。
  • 事件驱动: epoll是事件驱动的,只在有事件发生时进行处理,而不是通过轮询来检查。
  • 支持Edge Triggered(ET)模式: 在ET模式下,只有在状态变化时才会触发事件,而不是像Level Triggered(LT)模式那样持续通知。
  • 更高的性能: 由于采用了事件驱动和其他一些优化,epoll在处理大量并发连接时表现更好。

Redis数据类型

  1. 字符串(String):

    • 简介: 字符串是Redis最简单的数据类型,可以包含任意类型的数据,包括文本和二进制数据。
    • 用途: 通常用于存储简单的键值对数据,也可用于缓存和计数器等场景。
    • 数据结构:简单动态字符串类型
      • 整数:采用int编码,元数据8字节+int8字节,共计16字节
      • 长度小于44:采用紧凑型编码。
      • 长度大于44:采用指针。
    • 命令示例:
      SET mykey "Hello"
      GET mykey
  2. 哈希(Hash):

    • 简介: 哈希是一个键值对集合,每个哈希可以存储多个字段和对应的值,类似于关联数组。
    • 用途: 适合存储对象,如用户信息、配置项等,便于按字段进行读写操作。
    • 数据结构
      • 哈希表
      • 压缩列表
    • 命令示例:
      HSET user:1000 username "john_doe"
      HGET user:1000 username
  3. 列表(List):

    • 简介: 列表是一个有序的字符串元素集合,支持在两端执行插入和删除操作。
    • 用途: 适用于队列、栈等数据结构,可按顺序存储多个元素。
    • 数据结构
      • 双向链表
      • 压缩列表
    • 命令示例:
      LPUSH mylist "item1"
      RPUSH mylist "item2"
      LRANGE mylist 0 -1
  4. 集合(Set):

    • 简介: 集合是一个无序的字符串元素集合,元素是唯一的。
    • 用途: 适用于存储唯一值,如用户标签、好友关系等。
    • 数据结构
      • 哈希表
      • 整数数组
    • 命令示例:
      SADD myset "value1"
      SADD myset "value2"
      SMEMBERS myset
  5. 有序集合(Sorted Set):

    • 简介: 有序集合是集合的扩展,每个元素都有一个分数,可以按升序或降序排列。
    • 用途: 适用于需要排序的场景,如排行榜。
    • 数据结构
      • 跳表
      • 压缩列表
    • 命令示例:
      ZADD myzset 1 "value1"
      ZADD myzset 2 "value2"
      ZRANGE myzset 0 -1 WITHSCORES
  6. 位图(Bitmap):

    • 简介: 位图是一种特殊的字符串,可进行位操作,常用于存储和处理状态信息。
    • 用途: 例如用户在线状态、签到记录等。
    • 命令示例:
      SETBIT mybitmap 0 1
      GETBIT mybitmap 0
  7. HyperLogLog:

    • 简介: HyperLogLog是一种基数估算算法,用于估计集合中不同元素的数量,具有固定的空间复杂度。
    • 用途: 适用于估算大规模数据集合的基数,如网站访问用户数。
    • 命令示例:
      PFADD myhyperloglog element1 element2 element3
      PFCOUNT myhyperloglog
  8. Geo(地理空间):

    • 简介: Geo是用于处理地理空间信息的数据类型,主要用于存储地理位置坐标。
    • 用途: 适用于实现位置相关的应用,如附近的商家、地点推荐等。
    • 命令示例:
      GEOADD mylocations 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"

哈希表底层原理

哈希表底层是基于数组实现的。数组中每一个元素我们称之为哈希桶。当发生键值对操作时,首先对key值进行哈希运算, 并将哈希唯一映射到某一个哈希桶中。因此,哈希表可以实现一个近似O(1)的操作。

Redis与Memcached区别

  • Redis拥有更丰富的我数据结构,支持持久化、淘汰、事务、集群等功能
  • Memcached仅支持键值对缓存

内存淘汰策略

  1. No Eviction (无淘汰策略):
    • 如果配置为无淘汰策略,当内存超过限制时,Redis会拒绝写入操作,直到足够的内存被释放为止。这样可以确保不会删除任何数据,但也可能导致服务不可用。
  2. All Keys Random (随机淘汰):
    • 这种策略会从所有的键中随机选择一些进行淘汰。虽然简单,但可能导致一些热门的数据被删除,从而影响性能。
  3. Volatitle TTL (根据TTL淘汰):
    • 在这种策略中,Redis会优先淘汰具有较短TTL(生存时间)的键。这样做可以确保首先删除那些很快就会过期的数据,腾出空间来存储新的数据。
  4. Least Recently Used (LRU - 最近最少使用):
    • LRU算法会根据最近访问的时间来淘汰最少使用的键。当内存不足时,选择最近最少使用的键进行淘汰。这通常是一个比较有效的策略,因为一般而言,很少被访问的键可能很久没有使用过,有可能不再需要。
  5. Least Frequently Used (LFU - 最不经常使用):
    • LFU算法会根据键被访问的频率来淘汰最不经常使用的键。具有较低访问频率的键会被优先淘汰。

除了缓存还有那些应用场景

  • 缓存
  • 分布式锁
  • 消息队列
  • 布隆过滤器
  • 排行榜
  • 秒杀场景


缓存

什么是缓存击穿、缓存穿透、缓存雪崩、缓存预热

  1. 缓存击穿:指的是在高并发的情况下,缓存中的某个热点数据突然过期或被删除,导致大量请求直接穿透缓存,同时请求数据库获取数据,给数据库造成巨大压力,可能导致数据库崩溃。
    • 预防策略:热点数据不设置过期,由程序自己维护
  2. 缓存穿透:指的是请求的数据在缓存和数据库中都不存在,导致每次请求都直接穿透缓存,请求数据库获取数据。这种情况可能是由于恶意攻击或请求了不存在的数据导致的。
    • 预防策略:查询未命中也是用缓存,或者使用布隆过滤
  3. 缓存雪崩:指的是缓存中大量的数据同时过期或失效,导致大量请求直接穿透缓存,同时请求数据库获取数据,给数据库造成巨大压力,可能导致数据库崩溃。
    • 预防策略:过期时间增加随机值,热点数据不设置过期时间
  4. 缓存预热:指的是在缓存系统启动或更新缓存数据时,提前将热点数据加载到缓存中,以便在请求到来时能够直接从缓存中获取数据,减少对数据库的请求压力。缓存预热可以提高缓存的命中率,减少数据库的负载。


持久化

Redis 使用两种主要的持久化机制,分别是 Append-Only File (AOF) 和 Redis DataBase (RDB)。

AOF(Append-Only File)

  • 工作原理: AOF 记录了所有写操作。当Redis重新启动时,会通过回放AOF文件中的写操作,实现数据还原。
  • 写回策略:
    • Always:同步写回
    • Everysec:每秒一次,将缓冲区数据刷新到磁盘
    • No:由操作系统控制写回。
  • 优点:
    • 数据相对可靠,持久化过程中,数据基本上是实时写入的,因此对数据的损失较小。
    • 可配置性,AOF 持久化提供了一些可配置的选项,例如appendfsync策略,可以控制 AOF 文件的同步策略。你可以根据对数据一致性和性能的要求进行调整。
  • 缺点:
    • 性能影响:AOF 持久化会对 Redis 的性能产生一定影响,因为每次写操作都需要写入磁盘。这可能会导致写入性能下降,尤其是在高负载的情况下。
    • 文件大小:随着时间的推移,AOF 文件可能会变得非常大。为了避免文件过大,Redis 提供了 AOF 文件自动重写功能,但这仍然需要一定的计算资源。
    • 恢复时间:在启动时,Redis 需要加载并执行 AOF 文件中的所有命令,这可能需要一些时间,特别是对于大型的 AOF 文件。
  • FAQ:
    • AOF 文件过大如何处理:通过 BGREWRITEAOF 指令来重写AOF日志。其原理就是将同一个key的操作合并。
    • AOF 重写是否会阻塞 redis:重写由后台线程完成,不会阻塞主线程
    • AOF 重写期间新的写操作如何保存:新的写操作会先暂存到重写缓冲区,等重写完成后再追加。
AOF持久化配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
# 当 AOF 文件的大小超过上一次重写后大小的 100%(即增长了一倍),或者 AOF 文件达到 64MB 时,
# Redis 会自动触发 AOF 文件重写操作,以优化文件大小并减少历史命令的数量。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

RDB(Redis DataBase)

  • 工作原理: RDB 是一种快照机制,记录某一时刻的全量数据。
  • 同步策略
    • save:阻塞主线程
    • bgsave:不阻塞主线程
  • 优点:
    • 数据紧凑:RDB 文件相对较小,它是一个二进制文件,它存储了 Redis 服务器的整个数据集。
    • 恢复速度块:恢复速度比 AOF 快,因为只需加载一个文件即可。
  • 缺点:
    • 数据相对不可靠:RDB是某一个时间全量数据的快照,是定期执行的。对于快照之后的数据不会保存。因此可能造成数据大量不一致。
    • 性能影响:在数据量较大时,生成 RDB 文件可能会影响 Redis 的性能。
RDB持久化配置
# 配置 RDB 文件的保存路径
dir /path/to/redis/rdb

# 配置 RDB 文件的文件名
dbfilename dump.rdb

# 配置 RDB 文件的保存条件
# 在 900 秒内有 1 个键发生变化时,将进行 RDB 持久化;
# 在 300 秒内有 10 个键发生变化时,将进行 RDB 持久化;
# 在 60 秒内有 10000 个键发生变化时,将进行 RDB 持久化。
save 900 1
save 300 10
save 60 10000

如何选择合适的持久化方式

选择合适的 Redis 持久化方式取决于业务需求和使用场景

  1. 数据重要性:如果您的数据非常重要,不能丢失,那么您可能需要选择 AOF 持久化方式。AOF 持久化方式会将每个写入操作记录到日志文件中,因此即使 Redis 服务器崩溃,也可以通过重新加载 AOF 文件来恢复数据。
  2. 数据恢复速度:如果您需要快速恢复数据,那么您可能需要选择 RDB 持久化方式。RDB 持久化方式会将 Redis 服务器的整个内存数据快照保存到磁盘中,因此恢复数据的速度非常快。
  3. 数据一致性:如果您需要更高的数据一致性,那么您可能需要选择 AOF 持久化方式。AOF 持久化方式会记录每个写入操作,因此可以保证数据的一致性。
  4. 性能影响:AOF 持久化方式可能会对 Redis 服务器的性能产生一定的影响,因为它需要将每个写入操作记录到日志文件中。相比之下,RDB 持久化方式对性能的影响较小,因为它只需要在特定的时间点生成数据快照。
  5. 数据大小:如果您的 Redis 服务器存储的数据量非常大,那么您可能需要选择 RDB 持久化方式。RDB 持久化方式可以生成紧凑的二进制文件,因此可以节省磁盘空间。


集群

Redis 集群是用于解决单点瓶颈的一种方案。主要包括:Redis 主从集群(Redis Sentinel)和切片集群(Redis Cluster)。

其中,主从集群更适合于需要数据高可用性和读性能优化的场景,而切片集群更适合于需要水平扩展和处理大量数据的场景。

主从集群

Redis 主从集群:也就是我们常说的主备,是一个包含一个主节点(Master)和多个从节点(Slave)的架构。主节点负责处理所有的写操作,并将数据同步到从节点。从节点用于提供数据副本,并在主节点发生故障时进行故障转移。

在主从集群中,客户端可以连接到主节点或从节点进行数据读写操作。主节点和从节点之间通过复制协议保持数据一致性。当主节点发生故障时,Sentinel 会监测到并自动将一个从节点提升为主节点,以确保缓存系统的正常运行。

优点

  • 高可用性:主从集群通过主节点和从节点的复制机制,提供了数据的冗余备份,当主节点发生故障时,从节点可以快速提升为主节点,确保缓存系统的正常运行。
  • 高性能:主从集群是读写分离的,主库负责写,从库负责读。通过水平拓展从节点,可以提供更高的并发性能。

缺点

  • 主节点性能瓶颈:主节点负责处理所有的写操作,因此在高负载情况下可能成为性能瓶颈。
  • 数据延迟:主从集群中,从节点的数据是通过主节点复制而来的,因此可能存在一定的数据延迟。

同步架构

  • 主-从架构
  • 主-从-从架构:过多的从库直接从主库获取数据,会对主库造成一定的压力,因此可以将压力以联级的方式传输到从库点上。

主从复制

Redis 的主从复制是指将主节点(Master)的数据同步到从节点(Slave)的过程。通过主从复制,可以实现数据的冗余备份,提高缓存系统的可用性和可靠性。

主从复制的逻辑如下:

  1. 建立连接:从节点与主节点建立连接,并发送复制请求。
  2. 全量同步:主节点将整个数据库的内容发送给从节点,完成数据的初始同步。
  3. 增量同步:在全量同步完成后,主节点会将后续的写操作以命令的形式发送给从节点,从节点执行这些命令,完成数据的实时同步。

增量复制

在 redis2.8 以前,当主从节点之间因网络问题断开后,重新建立连接时,主从之间会重新执行一次全量复制。

在 redis2.8 之后,支持增量复制。 在增量复制过程中,主节点会维护一个复制偏移量(replication offset),用于记录主节点已经发送给从节点的数据量。当主从节点之间的连接断开后,从节点会记录最后一次接收到的复制偏移量。

当主从节点重新建立连接时,从节点会向主节点发送自己记录的复制偏移量。主节点会根据从节点的复制偏移量,判断从节点是否已经接收了全部数据。如果从节点的复制偏移量小于主节点的当前偏移量,则主节点会将从节点的复制偏移量之后的数据发送给从节点,完成增量同步。

通过复制偏移量的机制,Redis 的主从复制可以在网络问题导致连接断开后,快速恢复数据同步,避免了全量同步的开销,提高了复制的效率和可靠性。

从库读到过期数据

设置过期尽量使用 expireat 而不是 expire

哨兵机制

在 Redis 主从集群中,哨兵(Sentinel)机制用于监测主节点的健康状况,并在主节点发生故障时进行自动故障转移。

哨兵是一个独立的进程,它与主从节点进行通信,并监测主节点的心跳信息。如果哨兵监测到主节点发生故障,它会根据一定的规则选择一个从节点,并将其提升为主节点,以确保缓存系统的正常运行。

具体来说,哨兵机制包括以下几个步骤:

  1. 监测主节点:哨兵进程会定期向主节点发送心跳信息,以监测主节点的健康状况。如果主节点在一定时间内没有响应心跳信息,哨兵会将其标记为疑似故障。
  2. 确认主节点故障:当哨兵监测到主节点疑似故障时,它会向其他哨兵进程发送请求,以确认主节点是否真的发生故障。如果大多数哨兵都确认主节点故障,那么哨兵会将主节点标记为已故障。
  3. 选择从节点:在确认主节点故障后,哨兵会根据一定的规则选择一个从节点。通常,哨兵会选择与主节点最近一次同步数据的从节点。
  4. 提升从节点为主节点:哨兵会向选中的从节点发送命令,将其提升为主节点。然后,哨兵会向其他从节点发送命令,让它们开始复制新的主节点的数据。
  5. 通知客户端:当主节点发生故障并进行故障转移后,哨兵会向客户端发送通知,让它们更新连接到新的主节点。

通过哨兵机制,Redis 主从集群可以实现自动故障转移,提高了缓存系统的可用性和可靠性。同时,哨兵机制还提供了监控和通知功能,方便管理员对集群进行管理和维护。

选主打分

  1. 从库优先级
  2. 从库复制进度
  3. 从库ID

为什么至少需要三个哨兵

在 Redis Sentinel 中,至少需要三个 Sentinel 节点才能保证高可用性。

这是因为 Sentinel 节点之间需要通过选举算法选举出一个领导者,领导者负责执行故障转移操作。如果只有两个 Sentinel 节点,那么在选举过程中可能会出现分歧,导致无法选举出领导者,从而无法进行故障转移。

因此,为了避免这种情况的发生,至少需要三个 Sentinel 节点。这样,在选举过程中,至少有一个 Sentinel 节点会得到多数选票,从而选举出领导者,保证故障转移的顺利进行。

当然,为了进一步提高 Sentinel 系统的可靠性,通常建议使用更多的 Sentinel 节点。这样可以提高 Sentinel 系统的容错能力,避免单个 Sentinel 节点故障导致整个 Sentinel 系统不可用的情况。

切片集群

Redis 切片集群:是一种分布式存储解决方案,它将数据分散存储在多个 Redis 实例中,每个实例负责存储一部分数据。切片集群通过哈希槽(Hash Slot)的机制来分配数据。

在切片集群中,数据被划分为多个哈希槽,每个 Redis 实例都被分配了一定数量的哈希槽。当客户端要存储或获取数据时,会根据键的哈希值来确定应该将数据存储在哪个 Redis 实例的哈希槽中。

切片集群提供了水平扩展和负载均衡的能力,可以通过增加 Redis 实例的数量来增加缓存系统的容量和性能。同时,切片集群还提供了主从复制和故障转移功能,以确保数据的高可用性。

一致性哈希

2^32-1 个哈希槽。



进阶

Redis的哨兵(Sentinel)是用来做什么的?它如何确保高可用性?

Redis Sentinel是用于监控和管理Redis主从复制集群的工具,可以自动故障转移主服务器。

什么是Redis Pipeline,和事务有什么区别?

Redis Pipeline允许一次性发送多个命令,减少了通信开销,提高了性能,特别是在批量操作时。 其中,Redis Pipeline不是原子操作,执行失败不会回滚。而事务是原子操作,执行失败会回滚。

什么是Redis事务?如何保证事务的一致性?

Redis事务是一组命令,可以按顺序执行。 通过MULTI和EXEC命令,可以保证事务的原子性,要么全部执行成功,要么全部失败。

请介绍下Redis Watch机制

Redis的乐观锁机制是通过WATCH命令来实现的,用于在事务中实现并发控制。

WATCH命令用于监视一个或多个键,一旦对这些键的值发生更改,与这些键相关的事务将被取消。 通常WATCH配合事务(MUTIL/EXEC)执行。

Redis事务和Lua脚本有什么区别?

Redis事务和Lua脚本都涉及原子性操作,要么全部成功,要么全部失败。除此之外:

  • Redis事务是一组Redis命令的集合,按顺序执行
  • Lua脚本是在Redis中执行的Lua编程语言脚本,支持条件控制、循环、业务逻辑等复杂命令的组合


实战

Redis内存数据库的内存指的是共享内存吗

  • 共享内存:共享内存是一种进程间通信的方式,它允许多个进程访问同一块内存区域。
  • 物理内存:物理内存是计算机硬件上的实际内存,用于存储程序和数据。

乐观锁(WATCH)

WATCH 命令用于在执行修改操作之前监视一个或多个数据键。

它提供了一种乐观锁机制,允许客户端在执行某些操作之前先获取键的当前值,并在操作期间监视这些键的值是否发生了变化。

当客户端使用 WATCH 命令监视一个或多个键时,Redis 会将这些键的监视信息保存在一个内部队列中。当客户端执行某些操作(例如 GET、SET、DEL 等)时,Redis 会检查这些键是否被其他客户端修改过。如果键的值在客户端执行操作之前发生了变化,Redis 会返回一个错误,提示客户端需要重新获取键的最新值并再次尝试执行操作。

import redis

# 创建 Redis 连接
redis_client = redis.Redis(host='localhost', port=6379, db=0)

# 监视计数器的值
redis_client.watch('counter')

# 获取计数器的当前值
counter_value = redis_client.get('counter')

# 将计数器的值增加 1
new_counter_value = counter_value + 1
try:
redis_client.set('counter', new_counter_value)
except redis.exceptions.WatchError:
# 处理 WATCH 错误
raise Excepion("WATCH 监视的数据发生了变化,需要重新获取最新值并再次尝试执行操作")
else:
# 输出结果
print("计数器的值更新为:", new_counter_value)
finally:
redis_client.unwatch()

分布式锁

分布式锁分为获取锁和释放锁两个部分。

获取锁

可以使用SET命令来获取锁,结合NX(如果不存在)和EX(设置过期时间)参数,确保锁的独占性和自动释放。

获取锁
SET lock_key my_unique_identifier NX EX 30

释放锁

可以使用Lua脚本来释放锁

释放锁-Lua脚本
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end

如何避免Redis阻塞

Redis 可能在多种场景下发生阻塞,主要的原因包括:

  1. 持久化操作: 当 Redis 执行持久化操作(如RDB快照或AOF文件重写)时,它可能会阻塞对数据库的正常读写操作。持久化操作可能会导致一些短暂的阻塞,具体取决于数据的大小和系统的性能。

    • 避免方法: 可以通过合理配置持久化策略,选择合适的持久化方式(RDB或AOF)以及调整触发持久化的时机来减轻持久化操作对性能的影响。另外,可以考虑使用 Redis 的持久化异步化特性,如AOF的fsync选项,以减少阻塞时间。
  2. 主从同步: 当 Redis 配置了主从复制时,主节点与从节点之间的数据同步也可能导致阻塞。主节点在进行全量同步或部分重同步时,从节点可能会暂时阻塞。

    • 避免方法: 可以通过合理设置复制选项,例如选择合适的复制缓冲区大小,避免在高峰期进行同步等来减轻主从同步对性能的影响。
  3. 慢查询: 当 Redis 执行慢查询时,可能会阻塞其他请求的执行。

    • 全量键值对查询
    • 复杂的聚合计算
    • BigKey写入于删除
    • 清空数据库
    • 避免方法: 定期检查慢查询日志,通过优化查询语句、使用索引等手段提高查询性能。此外,可以设置合适的超时时间,对于长时间运行的查询进行适当的限制。
  4. 内存压力: 当 Redis 的内存使用达到系统可用内存的上限时,系统可能会开始进行内存回收,这可能导致 Redis 在一段时间内被阻塞。

    • 避免方法: 监控 Redis 的内存使用情况,合理设置内存策略,考虑在需要时使用分片或集群来扩展内存容量。

切片集群数据倾斜

造成原因及解决方案

  • 存在BigKey
    • 解决方案:使用更合适的数据结构,将BigKey拆成多个Key
  • 哈希槽分配不均匀
    • 解决方案:增加虚拟节点

内存碎片

频繁的增删改会造成内存碎片。我们可以通过 info memory 查看内存碎片指标

当指标大于1小于1.5时,整体影响还好,但指标大于1.5时,表示内存碎片率已经超过50%了。

解决方案:

  1. 重启 redis
  2. 开启自动内存碎片清理

脑裂

当主节点因网络问题,哨兵重新选主时。因为网络恢复,使得当前集群存在两个主节点。

危害:

  1. 当旧主节点恢复时,客户端的数据仍然会往旧主节点写。
  2. 新主节点上线后,会将旧主节点转化为从节点,因此期间数据会丢失。

解决方案:

  1. 提升网络稳定性
  2. 定期备份数据

redis-cell

redis-cell是令牌桶的一种实现,默认redis是不支持的,需要在配置中加载该模块。

loadmodule /path/to/redis/modules/redis-cell.so

通过CELL.SET创建令牌桶,创建时需要指定桶大小和令牌补充速率。 和CELL.AWAIT指令可以消费令牌桶,需要指定消费令牌数量。