Redis 夺命十二问

转自:
https://mp.weixin.qq.com/s/GFUHslsSm96fJbhsCkFe_w

Redis 是单线程还是多线程?

Redis 在不同版本之间采用的线程模型是不一样的,在 Redis 4.0 版本之前使用的是单线程模型,在 4.0 版本之后增加了多线程的支持。

虽然在 4.0 版本之前我们说 Redis 是单线程,但也只是说它的网络 I/O 线程以及 set/get 操作是由一个线程完成的,而 Redis 的持久化和集群同步还是使用了其他线程来完成。

4.0 版本之后添加了多线程的支持,主要是体现在大数据的异步删除功能上,例如 unlink key、flushdb async、flushall async 等。

  • unlink key,类似于 del 命令,表示删除指定的 key,如果指定的 key 不存在,则忽略命令。但是与 del 命令不同的是,它是异步执行的,因此它不会阻塞。unlink 命令是非阻塞删除,即将删除操作放到另一个线程(而非主线程)中处理。返回可删除 key 的个数
  • flushdb,用于清空当前数据库中的所有 key,该命令不会失败。Redis 4.0 版本及之后,flushdb 命令增加了 async 选项,支持异步删除操作,避免阻塞其他请求
  • flushall,删除 Redis 中所有存在的 key 而不仅仅只是当前数据库,该命令不会失败。Redis 4.0 版本及之后,flushall 命令增加了 async 选项,支持异步删除操作,避免阻塞其他请求

在 Redis 中,flushdb 和 flushall 命令都是清空当前数据库的操作,但是两者有很大的区别。

  • flushall,清空数据库并执行持久化操作,即 rdb 文件会发生改变(变成 76 字节大小,初始状态下就为 76 字节),所以执行 flushall 命令后数据库就真正意义上被清空了
  • flushdb,清空数据库但是不执行持久化操作,即 rdb 文件不会发生改变,又因为 Redis 的数据是从 rdb 快照文件中读取并加载到内存的,所以 flushdb 后若想恢复数据库,可以直接 kill 掉 redis-server 进程,然后再重启服务,这样 Redis 就会重新读取 rdb 文件并将数据恢复到操作之前的状态。注意要直接 kill 掉 redis-server 服务,因为 shutdown 操作会触发持久化

删除一个大的健时,怎么优化卡顿的问题?

  • 使用异步删除:将删除放在后台线程中进行,这样不会阻塞主线程并导致卡顿。可以使用 Redis 的异步命令,比如 DEL 命令的异步版本UNLINK
  • 分批删除:如果要删除的键值对数量非常大,可以考虑将删除操作分批进行,每次删除一部分键值对,然后休眠一段时间后再继续下一批删除。这样可以分散删除操作的负载,避免一次性删除过多数据导致卡顿
  • 使用删除优化工具:Redis 提供了一些优化工具,如 redis-cli 的 --bigkeys 选项,可以快速定位并删除大的键。可以使用这些工具来优化删除操作,减少卡顿的问题
  • 优化 Redis 配置:检查 Redis 的配置文件,确保 Redis 实例的内存配置和线程数等参数适合数据和使用场景。适当调整这些配置可以提高删除操作的性能和效

注意,删除一个大的键可能会导致性能下降,因为删除操作可能需要很长的时间,并且会占用 CPU 和内存资源。因此,在删除大的键之前,建议提前做好备份,并确保在合适的时间段内进行操作,以避免对正常业务造成影响。

lazy free 是什么?

lazy free 是 Redis 中的一种内存优化机制,它用于延迟释放(free)被删除的键的内存空间。当使用 DEL 命令删除一个键时,并不会立即释放与该键相关的内存,而是将被删除键的信息添加到一个专门的数据结构中,为"freed"列表。

在 Redis 的惰性(lazy)内存释放机制中,Redis 会等到以下情况才会释放被删除键的内存空间。

  • 当 Redis 需要进行内存回收时,它会检查"freed"列表中的键,并将它们从内存中删除
  • 当 Redis 接收到一个读取或写入请求时,如果内存不足,则会尝试进行内存清理,释放相关的内存空间

这种延迟释放的机制可以降低 Redis 的内存碎片化,并提高内存的利用率,尤其在频繁删除大量键值对的场景下具有较好的效果。然而,需要注意的是,使用 lazy free 可能会导致 Redis 在删除键后的一段时间内持续占用内存,因此需要根据实际情况评估和调整相关配置。

如何配置 lazy free?

要配置 Redis 的 lazy free 功能,可以通过设置以下参数来控制其行为。

  • lazyfree-lazy-eviction:默认情况下,这个参数的值为 yes,开启 lazy free。如果要禁用 lazy free 功能,可以将其设置为 no
  • lazyfree-lazyire:默认情况下,这个参数的值为 yes,表示开启过期键的 lazy free。当一个键过期后,Redis 并不会立即从内存中删除它,等待一个合适的时机进行删除。如果要禁用过期键的 lazy free,可以将其设置为 no

注意,在开启 lazy free 之后,Redis 会在内部维护一个"freed"列表来记录被删除键的信息。如果希望"freed"列表中的键能够及时被清理,以避免过多占用内存,可以调整以下参数。

  • maxmemory-samples:这个参数默认的值是 5,表示每次进行内存清理时,Redis 会在"freed"列表中随机选择 5 个键进行检查和删除。可以根据需要调整这个值,以平衡内存清理的效率和开销
  • hz:这个参数表示 Redis 的主循环频率,也就是每秒执行的循环次数。默认情况下,这个值是 10,也就是每秒执行 10 次主循环。可以根据系统的负载情况和性能需求,适当调整这个值,以提高内存清理的开销和效率

在配置 Redis 的 lazy free 时,需要考虑系统的内存资源和实际的使用情况。合理配置这些参数可以提高内存利用率和性能,但过度使用会增加内存占用和 CPU 负载。建议根据具体的应用场景和需求进行评估和调整。

为什么 Redis 在 4.0 版本之前会选择使用单线程,而且使用单线程还那么快?

  • Redis 的大部分操作都在内存中完成,内存中的执行效率本身就很快,并且采用了高效的数据结构,比如哈希表和跳表
  • 使用单线程避免了多线程的竞争,省去了多线程切换带来的时间和性能开销,并且不会出现死锁
  • 采用 I/O 多路复用机制处理客户端大量的 Socket 请求,因为是基于非阻塞的 I/O 模型,这就让 Redis 可以高效地进行网络通信,I/O 的读写流程也不再阻塞

Redis 是如何实现数据不丢失的呢?

Redis 的数据是存储在内存中的,为了保证数据不丢失,就要把数据从内存写入到磁盘上,以便在服务器重启后还能够从磁盘中恢复原有数据,这就是 Redis 的数据持久化。

Redis 的数据持久化有三种方式。

  • AOF 日志(Append Only File,文件追加方式):记录所有的操作命令,并以文本的形式追加到文件中
  • RDB 快照(Redis DataBase):将某一个时刻的内存数据,以二进制的方式写入磁盘
  • 混合持久化方式:Redis 4.0 版本新增了混合持久化的方式,集成了 RDB 和 AOF 的优点

AOF 和 RDB 的实现原理?

AOF 采用写后日志的方式,Redis 先执行命令把数据写入内存,然后再记录日志到文件中。

AOF 日志记录的是操作命令,而不是实际的数据,采用 AOF 做故障恢复时,需要将全量日志都执行一遍。

图片

RDB 采用的是内存快照的方式,它记录的是某一时刻的数据,而不是操作,所以采用 RDB 做故障恢复时,只需要直接把 RDB 文件读入内存即可,实现快速恢复。

刚刚提到了 AOF 采用的是“写后日志”的方式,但 MySQL 采用的是“写前日志”,那 Redis 为什么要先执行命令,再把数据写入日志呢?

主要是由于 Redis 在写入日志之前,不会对命令进行语法检查。只记录执行成功的命令,避免出现记录了错误命令的情况,而且在命令执行后再写日志不会阻塞当前的写操作。

后写日志又有什么风险?

  • 数据可能会丢失:如果 Redis 刚执行完命令,此时发生故障宕机,会导致这条命令存在丢失的风险
  • 可能阻塞其他操作:AOF 日志其实也是在主线程中执行的,所以当 Redis 把日志文件写入磁盘的时候,还是会阻塞后续的操作

RDB 做快照时会阻塞线程吗?

Redis 提供了两个命令来生成 RDB 快照文件,分别是 save 和 bgsave。

save 命令在主线程中执行,会导致阻塞;而 bgsave 命令则会创建一个子进程,用于写入 RDB 文件的操作,避免了对主线程的阻塞,这也是 Redis RDB 的默认配置。

RDB 做快照的时候数据能修改吗?

save 是同步的,会阻塞客户端命令,而 bgsave 执行的时候是可以修改的。

Redis 是怎么解决在 bgsave 做快照的时候允许数据修改的呢?

主要是利用 bgsave 的子线程来实现。

  • 如果主线程执行读操作,则主线程和 bgsave 子进程互不影响
  • 如果主线程执行写操作,则被修改的数据会复制一份副本,然后 bgsave 子进程会把该副本数据写入 RDB 文件,在这个过程中,主线程仍然可以直接修改原来的数据

在这里插入图片描述

同时要注意 RDB 的执行频率,因为这会影响快照数据的完整性以及 Redis 的稳定性。所以在 Redis 4.0 版本之后,增加了 AOF 和 RDB 混合的数据持久化机制,把数据以 RDB 的方式写入文件,再将后续的操作命令以 AOF 的格式存入文件,这样既保证了 Redis 的重启速度,又降低了数据丢失风险。

Redis 是如何实现高可用的?

Redis 实现高可用主要有三种方式:主从复制、哨兵模式以及 Redis 集群。

主从复制

将一台 Redis 主服务器,同步数据到多台 Redis 从服务器上,即一主多从的模式,这个和 MySQL 主从复制的原理一样。

图片

哨兵模式

使用 Redis 主从服务的时候,会有一个问题,就是当 Redis 的主从服务器出现故障宕机时,需要手动进行恢复。为了解决这个问题,Redis 增加了哨兵模式(因为哨兵模式做到了可以监控主从服务器,并且提供自动容灾恢复的功能)。

图片

Redis Cluster(集群)

Redis Cluster 是一种分布式去中心化的运行模式,是在 Redis 3.0 版本中推出的 Redis 集群方案,它将数据分布在不同的服务器上,以此来降低系统对单个主节点的依赖,从而提高 Redis 服务的读写性能。

图片

使用哨兵模式在数据上有副本数据做保证,在可用性上又有哨兵做监控,一旦 master 宕机会选举 salve 节点为新的 master 节点,这种方式已经满足了我们的生产环境需要,为什么还需要使用集群模式呢?

哨兵模式归根到底还是主从模式,在主从模式下,可以通过增加 salve 节点来扩展并发读的能力,但是没办法扩展写能力和存储能力,存储能力只能是 master 节点能够承载的上限。所以为了扩展写能力和存储能力,就需要引入集群模式。

集群中那么多 master 节点,Redis Cluster 在存储的时候如何确定选择哪个节点呢?

Redis Cluster 采用的是类一致性哈希算法来实现节点选择的。

Redis Cluster 将自己分成了 16384 个 slot(槽位),哈希槽类似于数据分区,每个键值对都会根据它的 key,被映射到一个哈希槽中,具体的执行过程分为两大步。

  1. 根据键值对的 key,按照 CRC16 算法计算一个 16bit 的值
  2. 再用 16bit 值对 16384 取模,得到 0 ~ 16383 范围内的模数,每个模数代表一个相应编号的哈希槽

每个 Redis 节点负责处理一部分槽位,假如有三个 master 节点 A、B、C,每个节点负责的槽位如下。

在这里插入图片描述

这样就实现了 Cluster 节点的选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值