Skip to content

Redis

Redis 可以用来干什么?

Redis 有哪些数据结构?

  1. 字符串:这是Redis最简单的数据类型,可以包含任何形式的字符串,含有二进制数据。字符串类型是二进制安全的,意味着可以存储任何形式的数据,例如:图片、序列化对象等。用途广泛,例如:常用作计数器,令牌,分布式锁等。

  2. 哈希:HashMap类型,可以储存对象,比如我们可以存储一个用户的对象,键可以是用户的ID,值就是用户的各种属性。

  • 列表(List):List是字符串链表,按插入顺序排序,你可以添加一个元素到头部(左边)或尾部(右边)。列表适合用于消息队列,用户关系链,最新项目列表等。

  • 集合(Set):Set是一个无序且唯一的字符串集合。集合的主要应用场景包括交集、并集、差集、以及元素的查询,适用于标签系统,好友关系链等场景。

  • 有序集合(Sorted Set):在集合类型的基础上,为每个元素关联了一个分数。元素的排列顺序由分数决定。有序集合适合用于排行榜应用,优先队列等。

Redis 的 Zset 主要使用两种数据结构:跳表(Skip List)哈希表(Hash Table)。以下是它们的具体实现细节:

  1. 跳表(Skip List)
  • 结构:跳表是一种随机化的数据结构,它由多层链表组成。最底层的链表存储所有的元素,随着层数的增加,链表中元素的数量逐渐减少,每一层的元素都是下一层的子集。

跳表的查询、插入和删除操作的时间复杂度平均为 O(log N),这使得它在有序数据的操作中非常高效。

  • 作用:在 Redis 的 Zset 中,跳表用于维护元素的顺序。每个 Zset 元素都包含一个分数(score),通过分数可以快速地确定元素在集合中的位置。

  • 哈希表(Hash Table)
  • 结构:哈希表是由一组键值对组成的,键是元素的值,值是元素的分数。Redis 使用哈希表来存储 Zset 中的元素和它们对应的分数。

  • 作用:哈希表提供 O(1) 的时间复杂度来查找、插入和删除元素。

当 Zset 中的元素较少时,Redis 首先使用哈希表进行存储。通过哈希表可以快速查找元素是否存在以及获取元 素的分数。

  • Zset 的底层实现
  • 插入:当新元素插入到 Zset 中时,Redis 首先在哈希表中添加该元素及其分数。然后,通过跳表将元素插入到合适的位置,以保持有序性。

  • 删除:删除元素时,Redis 首先在哈希表中找到该元素,然后从跳表中删除该元素。

  • 查找:查找某个元素的分数时,Redis 先在哈希表中查找该元素,如果存在,则返回对应的分数;如果不存在,则返回 nil。

  • 范围查询:当需要进行范围查询(例如获取分数在某个范围内的所有元素)时,Redis 可以利用跳表的有序特性高效地完成这项操作。

  1. 字符串(String)

    • set:设置指定 key 的值

    • get:获取指定 key 的值

    • incr:将 key 中储存的数字值增一

    • decr:将 key 中储存的数字值减一

    • append:在 key 的值尾部添加指定字符串

  2. 哈希(Hash)

    • hset:为哈希表中的字段赋值

    • hget:获取哈希表中指定字段的值

    • hmget:获取所有指定字段的值

    • hdel:删除一个或多个哈希表字段

    • hlen:获取哈希表中字段的数量

  3. 列表(List)

    • lpush:将一个或多个值插入到列表头部

    • rpush:将一个或多个值插入到列表的尾部

    • lpop:移出并获取列表的第一个元素

    • rpop:移出并获取列表最后一个元素

    • lrange:获取列表指定范围内的元素

  4. 集合(Set)

    • sadd:向集合添加一个或多个成员

    • srem:移除集合中一个或多个成员

    • smembers:返回集合中的所有成员

    • sismember:判断成员元素是否是集合的成员

    • scard:返回集合的成员数

  5. 有序集合(Sorted Set)

    • zadd:向有序集合添加一个或多个成员,或者更新已存在成员的分数

    • zrem:移除有序集合中的一个或多个成员

    • zrange:通过索引区间返回有序集合成指定区间内的成员

    • zrank:返回有序集合中指定成员的索引

    • zrevrange:返回有序集中指定区间内的成员,通过索引,分数从高到底

Redis 为什么快呢?

①、基于内存的数据存储,Redis 将数据存储在内存当中,使得数据的读写操作避开了磁盘 I/O。而内存的访问速度远超硬盘,这是 Redis 读写速度快的根本原因。

②、单线程模型,Redis 使用单线程模型来处理客户端的请求,这意味着在任何时刻只有一个命令在执行。这样就避免了线程切换和锁竞争带来的消耗。

③、IO 多路复⽤

NIO 是同步非阻塞的( I/O 多路复用模型),服务器可以用一个线程处理多个客户端连接,通过 Selector 监听多个 Channel 来实现多路复用,客户端发送的连接请求会注册到多路复用器上,多路复用器轮询到连接有 IO 请求就进行处理:

Redis 持久化⽅式有哪些?有什么区别?

RDB

RDB 持久化通过创建数据集的快照(snapshot)来工作,在指定的时间间隔内将 Redis 在某一时刻的数据状态保存到磁盘的一个 RDB 文件中。

AOF

AOF 持久化通过记录每个写操作命令到AOF缓冲中,并将其追加到 AOF 文件中来工作,恢复时通过重新执行这些命令来重建数据集。

AOF 的主要作用是解决了数据持久化的实时性,目前已经是 Redis 持久化的主流方式。

Redis 的数据恢复?

Redis 启动时加载数据的流程:

  1. AOF 持久化开启且存在 AOF 文件时,优先加载 AOF 文件。

  2. AOF 关闭或者 AOF 文件不存在时,加载 RDB 文件。

  3. 加载 AOF/RDB 文件成功后,Redis 启动成功。

  4. AOF/RDB 文件存在错误时,Redis 启动失败并打印错误信息。

高可用

Redis 保证高可用主要有三种方式:主从、哨兵、集群

主从复制

主从复制,是指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。前者称为 主节点(master),后者称为 从节点(slave)。且数据的复制是 单向 的,只能由主节点到从节点。Redis 主从复制支持 主从同步 和 从从同步 两种,后者是 Redis 后续版本新增的功能,以减轻主节点的同步负担。

主从复制主要的作用

  • 数据冗余: 主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

  • 故障恢复: 当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复 (实际上是一种服务的冗余)

  • 负载均衡: 在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务 (即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点),分担服务器负载。尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高 Redis 服务器的并发量。

  • 高可用基石: 除了上述作用以外,主从复制还是哨兵和集群能够实施的 基础,因此说主从复制是 Redis 高可用的基础

主从复制存在哪些问题

  • 一旦主节点出现故障,需要手动将一个从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令其他从节点去复制新的主节点,整个过程都需要人工干预。

  • 主节点的写能力受到单机的限制。

  • 主节点的存储能力受到单机的限制。

Redis Sentinel(哨兵)

主从复制存在一个问题,没法完成自动故障转移。所以我们需要一个方案来完成自动故障转移,它就是 Redis Sentinel(哨兵)。

Redis Sentinel

Redis Sentinel ,它由两部分组成,哨兵节点和数据节点:

  • 哨兵节点: 哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的 Redis 节点,不存储数据,对数据节点进行监控。

  • 数据节点: 主节点和从节点都是数据节点;

在复制的基础上,哨兵实现了 自动化的故障恢复 功能,下面是官方对于哨兵功能的描述:

  • 监控(Monitoring): 哨兵会不断地检查主节点和从节点是否运作正常。 ping请求

  • 自动故障转移(Automatic failover): 当 主节点 不能正常工作时,哨兵会开始 自动故障转移操作,它会将失效主节点的其中一个 从节点升级为新的主节点,并让其他从节点改为复制新的主节点。

  • 配置提供者(Configuration provider): 客户端在初始化时,通过连接哨兵来获得当前 Redis 服务的主节点地址。

  • 通知(Notification): 哨兵可以将故障转移的结果发送给客户端

缓存设计

什么是缓存击穿、缓存穿透、缓存雪崩?

缓存击穿(热点key问题)

一个并发访问量比较大的 key 在某个时间过期,导致所有的请求直接打在 DB 上。

解决⽅案:

  1. 加锁更新,⽐如请求查询 A,发现缓存中没有,对 A 这个 key 加锁,同时去数据库查询数据,写⼊缓存,再返回给⽤户,这样后⾯的请求就可以从缓存中拿到数据了。

  • 将过期时间组合写在 value 中,通过异步的⽅式不断的刷新过期时间,防⽌此类现象。

缓存穿透

缓存穿透指的查询缓存和数据库中都不存在的数据,这样每次请求直接打到数据库,就好像缓存不存在一样。

缓存穿透将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端存储的意义。

缓存穿透可能会使后端存储负载加大,如果发现大量存储层空命中,可能就是出现了缓存穿透问题。

缓存穿透可能有两种原因:

  1. 自身业务代码问题

  2. 恶意攻击,爬虫造成空命中

它主要有两种解决办法:

  • 缓存空值/默认值

一种方式是在数据库不命中之后,把一个空对象或者默认值保存到缓存,之后再访问这个数据,就会从缓存中获取,这样就保护了数据库。

缓存空值/默认值

缓存空值有两大问题:

  1. 空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间(如果是攻击,问题更严重),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。

  2. 缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。 例如过期时间设置为 5 分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致。 这时候可以利用消息队列或者其它异步方式清理缓存中的空对象。

  • 布隆过滤器 除了缓存空对象,我们还可以在存储和缓存之前,加一个布隆过滤器,做一层过滤。

布隆过滤器里会保存数据是否存在,如果判断数据不不能再,就不会访问存储。

两种解决方案的对比:

缓存雪崩

某⼀时刻发⽣⼤规模的缓存失效的情况,例如缓存服务宕机、大量 key 在同一时间过期,这样的后果就是⼤量的请求进来直接打到 DB 上,可能导致整个系统的崩溃,称为雪崩。

缓存雪崩是三大缓存问题里最严重的一种,我们来看看怎么预防和处理。

  • 提高缓存可用性
  1. 集群部署:通过集群来提升缓存的可用性,可以利用 Redis 本身的 Redis Cluster 或者第三方集群方案如 Codis 等。

  2. 多级缓存:设置多级缓存,第一级缓存失效的基础上,访问二级缓存,每一级缓存的失效时间都不同。

  • 过期时间
  1. 均匀过期:为了避免大量的缓存在同一时间过期,可以把不同的 key 过期时间随机生成,避免过期时间太过集中。

  2. 热点数据永不过期。

  • 熔断降级
  1. 服务熔断:当缓存服务器宕机或超时响应时,为了防止整个系统出现雪崩,暂时停止业务服务访问缓存系统。

  2. 服务降级:当出现大量缓存失效,而且处在高并发高负荷的情况下,在业务系统内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息。

能说说布隆过滤器吗?

布隆过滤器,它是一个连续的数据结构,每个存储位存储都是一个bit,即0或者1, 来标识数据是否存在。

存储数据的时时候,使用 K 个不同的哈希函数将这个变量映射为 bit 列表的的 K 个点,把它们置为 1。

我们判断缓存 key 是否存在,同样,K 个哈希函数,映射到 bit 列表上的 K 个点,判断是不是 1:

  • 如果全不是 1,那么 key 不存在;

  • 如果都是 1,也只是表示 key 可能存在。

布隆过滤器也有一些缺点:

  1. 它在判断元素是否在集合中时是有一定错误几率,因为哈希算法有一定的碰撞的概率。

  2. 不支持删除元素。

三种常见的缓存策略

1. 旁路缓存模式(Cache-Aside Pattern)

在这种模式下,应用程序负责从缓存中读取数据,并在缓存未命中时,从数据源(如数据库)加载数据,并将其放入缓存供后续使用。

  • 读取操作:先从缓存读取数据,如果缓存没有命中,则从数据库加载数据,再将数据写入缓存。

  • 写入操作:写入操作更新数据库后,删除缓存,下一次读取时再从数据库中加载新的数据到缓存。

可能导致数据在缓存和数据库之间的不一致性。

2. 读写穿透模式(Read/Write Through Pattern)

读写穿透模式与旁路缓存模式不同,它由缓存层自身来负责数据的加载和存储。

  • 读取操作:应用程序从缓存读取数据。如果缓存未命中,缓存层会自动从数据库加载数据,然后返回给应用程序,并在缓存中保存备份。

  • 写入操作:应用程序写数据到缓存,缓存层将新数据写入数据库,并同时更新缓存中的数据副本。

读写穿透模式的优势在于能保证缓存与数据库的一致性,减少了应用程序直接操作数据库的次数。

3. 异步缓存模式(Asynchronous Caching)

相当于把缓存当作一个缓冲器,先把数据写入到缓存中,然后由缓存系统在合适的时候异步地把数据保存到数据库,从而提高了数据写入的性能。

  • 读取操作:与传统模式一致,从缓存中读取数据,如果缓存未命中,则从数据库加载数据,并放入缓存再返回给用户。

  • 写入操作:写入操作首先更新缓存,随后异步地将更新的数据写入到数据库。

这种模式可以提高写操作的性能,因为它避免了直接写缓存的延时,但可能会存在一段时间的数据不一致性,直到缓存被异步更新。

如何保证缓存和数据库数据的⼀致性?

采用的是先写 MySQL,再删除 Redis 的方式来保证缓存和数据库的数据一致性。

选择合适的缓存更新策略

  1. 删除缓存而不是更新缓存

当一个线程对缓存的 key 进行写操作的时候,如果其它线程进来读数据的时候,读到的就是脏数据,产生了数据不一致问题。

相比较而言,删除缓存的速度比更新缓存的速度快很多,所用时间相对也少很多,读脏数据的概率也小很多。

  • 先更数据,后删缓存

因为更新数据库的速度比删除缓存的速度要慢得多。因为更新 MySQL 是磁盘 IO 操作,而 Redis 是内存操作。内存操作比磁盘 IO 快得多(这是硬件层面的天然差距)。

那假如是先删除缓存,再更新数据库,就会造成这样的情况:

缓存中不存在,数据库又没有完成更新,此时有请求进来读取数据,并写入到缓存,那么在更新完缓存后,缓存中这个 key 就成了一个脏数据。

缓存和数据库数据不一致常见原因及避免方法

  • 缓存 key 删除失败

  • 并发导致写入了脏数据

  1. 消息队列保证 key 被删除 可以引入消息队列,把要删除的 key 或者删除失败的 key 丢进消息队列,利用消息队列的重试机制,重试删除对应的 key。

  • 设置缓存过期时间兜底

这是一个朴素但是有用的办法,给缓存设置一个合理的过期时间,即使发生了缓存数据不一致的问题,它也不会永远不一致下去,缓存过期的时候,自然又会恢复一致。

怎么处理热 key?

①、把热 key 打散到不同的服务器,降低压⼒。

②、加⼊⼆级缓存,当出现热 Key 后,把热 Key 加载到 JVM 中,后续针对这些热 Key 的请求,直接从 JVM 中读取。

这些本地的缓存工具有很多,比如 Caffeine、Guava 等,或者直接使用 HashMap 作为本地缓存都是可以的。

缓存预热怎么做呢?

所谓缓存预热,就是提前把数据库里的数据刷到缓存里,通常有这些方法:

1、直接写个缓存刷新页面或者接口,上线时手动操作

2、数据量不大,可以在项目启动的时候自动进行加载

3、定时任务刷新缓存.

热点 key 重建?问题?解决?

  1. 互斥锁(mutex key) 这种方法只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可。

  2. 永远不过期 “永远不过期”包含两层意思:

  • 从缓存层面来看,确实没有设置过期时间,所以不会出现热点 key 过期后产生的问题,也就是“物理”不过期。

  • 从功能层面来看,为每个 value 设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存

Redis应用

使用 Redis 如何实现异步队列?

使用 Redis 的 pub/sub 来进行消息的发布/订阅

发布/订阅模式可以 1:N 的消息发布/订阅。发布者将消息发布到指定的频道频道(channel),订阅相应频道的客户端都能收到消息。

但是这种方式不是可靠的,它不保证订阅者一定能收到消息,也不进行消息的存储。

所以,一般的异步队列的实现还是交给专业的消息队列。

Redis 如何实现延时队列?

  • 使用 zset,利用排序实现

可以使用 zset 这个结构,用设置好的时间戳作为 score 进行排序,使用 zadd score1 value1 ....命令就可以一直往内存中生产消息。再利用 zrangebysocre 查询符合条件的所有待处理的任务,通过循环执行队列任务即可。

Redis 支持事务吗?

Redis 支持简单的事务,可以将多个命令打包,然后一次性的,按照顺序执行(MULTI/EXEC)

Redis 事务的注意点有哪些?

Redis 事务是不支持回滚的,不像 MySQL 的事务一样,要么都执行要么都不执行

Redis 事务为什么不支持回滚?

Redis 是一个基于内存的数据存储系统,其设计重点是实现高性能。事务回滚需要额外的资源和时间来管理和执行,这与 Redis 的设计目标相违背。因此,Redis 选择不支持事务回滚。

换句话说,就是我 Redis 不想支持事务,也没有这个必要。

为什么需要 Lua?

既然 Redis事务能保证原子性,为什么还需要 Lua脚本呢?

  1. Lua 脚本一般比 MULTI/EXEC 更快、更简单;

  2. Redis 事务中,事务队列中的所有命令都必须在 EXEC命令执行才会被执行,对于多个命令之间存在依赖关系,比如后面的命令需要依赖上一个命令结果的场景,Redis事务无法满足,因此 Lua 脚本更适合复杂的场景;

Redis 和 Lua 脚本的使用了解吗?

Redis 的事务功能比较简单,平时的开发中,可以利用 Lua 脚本来增强 Redis 的命令。

Lua 脚本能给开发人员带来这些好处:

  • Lua 脚本在 Redis 中是原子执行的,执行过程中间不会插入其他命令。

  • Lua 脚本可以帮助开发和运维人员创造出自己定制的命令,并可以将这 些命令常驻在 Redis 内存中,实现复用的效果。

  • Lua 脚本可以将多条命令一次性打包,有效地减少网络开销

ACID的原子性和 Redis执行 Lua脚本原子性在概念上的差异:

ACID的原子性是指:命令要么全执行,要么全部不执行;

Redis中执行 Lua脚本原子性是指:Lua脚本需要作为一个整体执行且不被其他事务打断,至于 Lua脚本里面的命令是否必须全部成功,或者全部失败,并不要求

  • redis.call() 用于执行 Redis的命令。当命令执行出错时,会阻断整个脚本执行,并将错误信息返回给客户端。

  • redis.pcall() 也用于执行 Redis的命令。当命令执行出错时,不会阻断脚本的执行,而是内部捕获错误,并继续执行后续的命令。

redis保证原子性

当客户端向服务器发送一段带有 Lua 脚本的请求时,Redis会把该 Lua脚本当作一个整体,将 Lua脚本加载到一个脚本缓存中,因为 Redis读写命令是单线程操作,因此,所有的 Lua脚本会按照进入顺序放入队列中,然后串行进行读写,这样就保证了原子性

  • 它采用 IO 多路复用机制同时监听多个 Socket,并把所有产生事件的socket压入一个队列中,然后有序地每次仅一个socket的方式传送给文件事件分派器,文件事件分派器接收到socket之后会根据socket产生的事件类型调用对应的事件处理器进行处理。

  • 单机部署 不管 Lua脚本中操作的 key是不是同一个,都能保证原子性;

  • 主从部署 Redis 主从复制是用于将主节点的数据同步到从节点,以保持数据的一致性。而Redis的所有写操作都在主节点上,所以,不管 Lua脚本中操作的 key是不是同一个,都能保证原子性; 需要注意:当主节点执行写命令时,从节点会异步地复制这些写操作。在这个复制的过程中,从节点的数据可能与主节点存在一定的延迟。因此,如果在 Lua 脚本中包含读操作,并且该脚本在主节点上执行,可能会读到最新的数据,但如果在从节点上执行,可能会读到稍有延迟的数据。

假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如何将它们全部找出来?

使用 keys 指令可以扫出指定模式的 key 列表。但是要注意 keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。

SCAN cursor [MATCH pattern] [COUNT count]

Redis 运维

Redis 报内存不足怎么处理?

Redis 内存不足有这么几种处理方式:

  • 修改配置文件 redis.conf 的 maxmemory 参数,增加 Redis 可用内存

  • 也可以通过命令 set maxmemory 动态设置内存上限

  • 修改内存淘汰策略,及时释放内存空间

  • 使用 Redis 集群模式,进行横向扩容。

Redis 的过期数据回收策略有哪些?

Redis 主要有 2 种过期数据回收策略:

  1. 惰性删除

惰性删除指的是当我们查询 key 的时候才对 key 进⾏检测,如果已经达到过期时间,则删除。

显然,他有⼀个缺点就是如果这些过期的 key 没有被访问,那么他就⼀直⽆法被删除,⽽且⼀直占⽤内存。

  • 定期删除

定期删除指的是 Redis 每隔⼀段时间对数据库做⼀次检查,删除⾥⾯的过期 key。由于不可能对所有 key 去做轮询来删除,所以 Redis 会每次随机取⼀些 key 去做检查和删除。

Redis 有哪些内存溢出控制/内存淘汰策略

当 Redis 所用内存达到 maxmemory 上限时,会触发相应的溢出控制策略。

Redis六种内存溢出控制策略

  1. noeviction:默认策略,不进行任何淘汰,当内存不足以容纳更多数据时,对写操作返回错误。(但仍然允许删除操作)

  2. volatile-lru:仅从设置了过期时间的键中 使用 LRU(Least Recently Used,最近最少使用) 算法淘汰。

  3. volatile-ttl:从设置了过期时间的键中 选择将要过期的键淘汰。

  4. volatile-random:仅从设置了过期时间的键中 随机淘汰。

  5. allkeys-lru:从所有的键中使用 LRU算法淘汰数据。

  6. allkeys-random:从所有的键中随机淘汰数据。

Redis 阻塞?怎么解决?

Redis 发生阻塞,可以从以下几个方面排查:

对慢查询的处理分为两步:

  1. 发现慢查询: slowlog get{n}命令可以获取最近 的 n 条慢查询命令;

  2. 发现慢查询后,可以从两个方向去优化慢查询:

1)修改为低算法复杂度的命令,如 hgetall 改为 hmget 等,禁用 keys、sort 等命令

2)调整大对象:缩减大对象数据或把大对象拆分为多个小对象,防止一次命令操作过多的数据。

大 key 问题了解吗?

大 key 指的是存储了大量数据的键,比如:

  • 单个简单的 key 存储的 value 很大,size 超过 10KB

  • hash,set,zset,list 中存储过多的元素(以万为单位)

大 key 会造成什么问题呢?

  • 客户端耗时增加,甚至超时

  • 对大 key 进行 IO 操作时,会严重占用带宽和 CPU

  • 清理大key时,主动删除、被动删等,可能会导致阻塞

如何处理大 key?

①、删除大 key

  • 当 Redis 版本大于 4.0 时,可使用 UNLINK 命令安全地删除大 Key,该命令能够以非阻塞的方式,逐步地清理传入的大 Key。

  • 当 Redis 版本小于 4.0 时,建议通过 SCAN 命令执行增量迭代扫描 key,然后判断进行删除。

②、压缩和拆分 key

  • 当 vaule 是 string 时,比较难拆分,则使用序列化、压缩算法将 key 的大小控制在合理范围内,但是序列化和反序列化都会带来额外的性能消耗。

  • 当 value 是 string,压缩之后仍然是大 key 时,则需要进行拆分,将一个大 key 分为不同的部分,记录每个部分的 key,使用 multiget 等操作实现事务读取。

  • 当 value 是 list/set 等集合类型时,根据预估的数据规模来进行分片,不同的元素计算后分到不同的片。