跳至主要內容

(8)Redis


1 Redis到底是单线程还是多线程?

对于核心业务功能部分比如命令操作处理数据,Redis是单线程的,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程,所以一般我们认为Redis是个单线程程序。但是从整个框架层面出发严格来说Redis是多线程的。

在Redis版本迭代过程中,在两个重要的时间节点上引入了多线程的支持:

  • Redis v4.0:引入多线程异步处理一些耗时较旧的任务,例如异步删除命令unlink,异步持久化等等。
  • Redis v6.0:在核心网络模型中引入多线程,进一步提高对于多核CPU的利用率。

2 为什么Redis那么快?

  • 基于内存的数据存储:Redis 是一个基于内存的数据库,它将数据存储在内存中,因此读写速度非常快。与磁盘存储相比,内存存取速度更快,这使得 Redis 能够在毫秒级别内处理大量的请求。
  • 单线程模型:Redis 使用单线程模型来处理客户端请求,这意味着它不需要进行复杂的线程管理和上下文切换,从而避免了由于线程切换而引起的性能损耗。虽然单线程模型在某些情况下可能会受到性能瓶颈的限制,但在 Redis 的场景下,它可以充分利用 CPU 的缓存和计算资源,实现高效的请求处理。
  • 事件驱动和异步 I/O:Redis 使用事件驱动的方式来处理网络请求和数据操作,通过非阻塞的异步 I/O 操作,它可以在等待 I/O 操作完成的同时处理其他请求,从而提高了系统的并发能力和响应速度。
  • 优化的数据结构和算法:Redis 内置了丰富的数据结构(如字符串、哈希表、列表、集合、有序集合等),并针对不同的数据操作场景进行了优化。例如,它使用哈希表来实现键值对存储,使得数据的查找和更新操作都可以在 O(1) 的时间复杂度内完成。
  • 持久化和复制机制的优化:Redis 支持多种持久化方式(如快照和 AOF 日志),以及主从复制和 Sentinel 高可用性方案,通过这些机制的优化,Redis 不仅能够保证数据的持久性和可用性,还可以在故障恢复时尽快恢复服务。

Redis 在设计和实现上都充分考虑了性能和效率,使得它能够在内存数据库领域表现出色,成为了业界广泛使用的高性能数据存储解决方案之一。

3 Redis为什么采用单线程处理命令操作?

Redis的大部分操作都在内存中完成的,执行速度非常快,所以它的性能瓶颈是网络延迟而不是执行速度,因此多线程并不会带来巨大的性能提升。并且多线程会导致过多的上下文切换,带来不必要的性能开销。

同时多线程编程模式面临共享资源并发访问控制问题。并发访问控制一直是多线程开发中的一个难点问题,如果没有精细的设计,比如说,只是简单地采用一个粗粒度互斥锁,就会出现不理想的结果:即使增加了线程,大部分线程也在等待获取访问共享资源的互斥锁,并行变串行,系统吞吐率并没有随着线程的增加而增加。而且采用多线程开发一般会引入同步原语来保护共享资源的并发访问,这也会降低系统代码的易调试性和可维护性。为了避免这些问题,Redis 直接采用了单线程模式。

4 Redis的使用场景有哪些?

  • 缓存:Redis 可以作为缓存系统来加速应用程序的读取操作。它可以存储经常访问的数据,如数据库查询结果、页面片段、会话数据等,从而减少对后端存储系统的访问次数,提高了系统的响应速度和吞吐量。
  • 会话存储:Redis 可以用作会话存储(Session Store),将用户的会话数据存储在内存中,以提供快速的访问速度和高并发性能。这对于需要处理大量用户会话的 Web 应用程序和分布式系统来说非常有用。
  • 队列系统:Redis 的列表数据结构非常适合作为队列系统的基础,可以用来实现任务队列(Task Queue)、消息队列(Message Queue)等,用于解耦和异步处理系统中的任务和消息。
  • 计数器和排行榜:Redis 的计数器和有序集合数据结构可以用于实现实时计数和排行榜功能。例如,可以使用 Redis 计数器来记录网站访问量、用户行为次数等,也可以使用有序集合来实现用户排行榜、文章热度排名等。
  • 发布/订阅:Redis 提供了发布/订阅(Pub/Sub)功能,可以用于实现实时消息推送、事件通知等功能。发布者将消息发布到指定的频道,订阅者则可以订阅感兴趣的频道,从而实现消息的实时传递和处理。
  • 分布式锁:Redis 可以用作分布式系统中的锁服务,通过设置键的过期时间和原子性操作来实现简单而高效的分布式锁。这对于控制共享资源的并发访问和避免竞态条件非常有用。
  • 地理位置服务:Redis 的地理位置数据类型(Geo)可以用于实现地理位置服务,如附近的人、附近的商店等功能。通过存储和查询地理位置数据,可以实现基于地理位置的搜索和推荐。
  • 实时数据分析:Redis 可以用作实时数据分析和统计系统的基础,通过存储和处理实时数据流,实现实时监控、报警、日志分析等功能。

5 Redis 6.0 之后为什么引入了多线程?

虽然 Redis 的主要工作(网络 I/O 和执行命令)一直是单线程模型,但是在 Redis 6.0 版本之后,也采用了多个 I/O 线程来处理网络请求,这是因为随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 I/O 的处理上。

所以为了提高网络 I/O 的并行度,Redis 6.0 对于网络 I/O 采用多线程来处理。但是对于命令的执行,Redis 仍然使用单线程来处理,所以大家不要误解 Redis 有多线程同时执行命令。

Redis 官方表示,Redis 6.0 版本引入的多线程 I/O 特性对性能提升至少是一倍以上。

Redis 6.0 版本支持的 I/O 多线程特性,默认情况下 I/O 多线程只针对发送响应数据(write client socket),并不会以多线程的方式处理读请求(read client socket)。

6 Redis 如何实现数据不丢失?

Redis 的读写操作都是在内存中,所以 Redis 性能才会高,但是当 Redis 重启后,内存中的数据就会丢失,那为了保证内存中的数据不会丢失,Redis 实现了数据持久化的机制,这个机制会把数据存储到磁盘,这样在 Redis 重启就能够从磁盘中恢复原有的数据。

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

  • AOF 日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
  • RDB 快照:将某一时刻的内存数据,以二进制的方式写入磁盘;
  • 混合持久化:Redis 4.0 新增的方式,集成了 AOF 和 RBD 的优点;

7 Redis 支持哪些数据结构?

字符串(Strings):这是最基本的数据类型,可以包含任何数据,例如文本、数字或二进制数据。字符串最大可以是512MB。
哈希(Hashes):是键值对集合。它们是存储对象的理想方式,每个对象可以有多个字段和值。
列表(Lists):简单的字符串列表,按插入顺序排序。你可以在列表的头部或尾部添加元素。
集合(Sets):是字符串的无序集合。它们是通过哈希表实现的,因此可以快速查找、添加和删除。
有序集合(SortedSets):类似于Sets,但每个元素都关联一个浮点数分数。这使得它们按分数有序。
位图(Bitmaps):通过提供对字符串的位操作,实现了一种特殊的操作方式。这不是一个独立的数据类型,而是一套操作字符串的方法。
超日志(HyperLogLogs):用于高效地估算集合的基数(不同元素的数量),特别适用于大量数据。
流(Streams):在Redis5.0中引入,用于构建消息队列系统。它们类似于日志结构,可以有效地存储和检索消息流。
地理空间(Geospatial):用于存储地理位置信息,并能够轻松地进行各种地理空间计算。

8 如何监控 Redis 的性能?

Redis提供了两种方式来监控执行慢的指令,slowlog 和 latency monitor。另外Redis也有可以进行在线监控的系统TreeNMS。

Slowlog是Redis从2.2.12版本引入的一条命令,用于记录记录慢查询执行时间的日志系统。slowlog的信息只保存在内存中,因此slowlog的效率很高,完全不用担心会影响到redis的性能。我们可以通过redis.conf 和客户端指令来配置 slowlog。

  • 通过redis.conf配置slowlog
//时间阈值,执行时间大于此时间会被定义成慢执行指令,并记录到slowlog,默认10000,单位微秒
 slowlog-log-slower-than 10000 

 //slowlog的总长度,128代表slowlog最多记录128个慢指令,超过则覆盖
 slowlog-max-len 128 
  • 查看slowlog配置
 //查看slowlog的执行耗时阈值
 config get slowlog-log-slower-than 
 //查看slowlog保存数量的阈值
 config get slowlog-max-len

和slowlog不同的是latency monitor监控的指标维度更多,slowlog只会记录指令执行的耗时,latency monitor不仅记录指令,还会记录线程fork延时、AOF写入延时、进程调用fsync系统调用等信息。

设置延时监控的时间阈值(单位:毫秒)

 CONFIG SET latency-monitor-threshold 100

查看延时记录:

 //查看某一种事件类型的延时记录
 latency history fork
 //查看最后一条延时记录
 latency  latest
 //以图表的方式显示延时信息,类似曲线图
 LATENCY GRAPH event

9 Redis 的数据淘汰策略有哪些?

Redis 提供了多种内存淘汰策略,以便在内存不足时决定哪些键被删除。这些策略可以在 Redis 配置文件中通过 maxmemory-policy 选项进行设置。以下是 Redis 提供的淘汰策略:

  • noeviction: 这是默认策略,当内存不足以容纳更多数据时,新的写入操作会报错。
  • allkeys-lru: 当内存不足时,根据最近最少使用 (Least Recently Used, LRU) 算法删除任何可能的键。
  • volatile-lru: 在内存不足时,根据 LRU 算法删除设置了过期时间的键。
  • allkeys-random: 在内存不足时,随机删除任何可能的键。
  • volatile-random: 在内存不足时,随机删除设置了过期时间的键。
  • volatile-ttl: 在内存不足时,首选删除 TTL(Time To Live) 值较小的键,即选择剩余过期时间最短的键进行删除。
  • allkeys-lfu:当内存不足时,使用最少频率使用(Least Frequently Used, LFU)算法删除任何可能的键。
  • volatile-lfu:当内存不足时,使用 LFU 算法删除已设置了过期时间的键。

LRU(Least Recently Used)是最经典的一款缓存淘汰算法,如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很低,当数据所占据的空间达到一定阈值时,这个最少被访问的数据将被淘汰掉。

最少频率使用(Least Frequently Used,LFU)是一种基于数据项被访问频率的淘汰策略。在 Redis 中,LFU 会维护一个计数器来记录一个键被访问的频次。当需要淘汰数据时,LFU 会优先淘汰那些被访问次数最少的键。这样可以确保热点数据(被频繁访问的数据)在内存中保留的可能性更大。

10 如何保证 Redis 的高可用性?

  • 主从复制(Master-Slave Replication):通过设置主 Redis 服务器和多个从 Redis 服务器的复制机制,可以在主服务器发生故障时自动将一个从服务器提升为新的主服务器,以保证服务的连续性。这种配置还可以用于读写分离,从而减轻主服务器的负载。
  • Sentinel 集群(Sentinel Cluster):Sentinel 是 Redis 官方提供的一种高可用性解决方案,它能够监控 Redis 实例的健康状况,并在主服务器失效时自动进行故障转移。Sentinel 可以配置成独立的集群,通过投票机制来选择新的主服务器,以确保高可用性。
  • Redis Cluster:Redis 集群是一种分布式解决方案,能够在多个 Redis 节点之间分配数据,并在节点故障时自动进行故障转移和数据重分配。Redis 集群通常包含多个主节点和若干个从节点,以提供更高的可用性和可扩展性。
  • 持久化(Persistence):通过开启 Redis 的持久化机制,可以将数据保存到磁盘上,以防止数据丢失。Redis 支持两种持久化方式:快照(snapshotting)和日志(append-only file,AOF)。快照会周期性地将内存中的数据保存到磁盘上,而 AOF 则会将写操作记录到一个追加文件中。持久化可以在 Redis 重启时用于恢复数据。
  • 监控和报警:建立有效的监控系统,监视 Redis 实例的运行状态、负载情况和性能指标,并设置合适的报警机制。及时发现问题并采取相应的措施可以防止故障的进一步扩大。
  • 负载均衡:在应用层面使用负载均衡器(如Nginx、HAProxy等)将请求分发到多个 Redis 节点上,以分散流量和提高系统的整体吞吐量。
  • 容灾备份:定期对 Redis 数据进行备份,并将备份数据存储在不同的地点,以防止数据丢失或者发生灾难性事件时能够快速恢复数据。

通过以上这些方法的组合,可以有效地提高 Redis 的高可用性,确保系统能够持续稳定地运行。

参考
https://zhuanlan.zhihu.com/p/659340028open in new window
https://xiaolincoding.com/redis/base/redis_interview.htmlopen in new window
https://www.zhihu.com/question/555657988/answer/2731683485open in new window
https://zhuanlan.zhihu.com/p/352040701open in new window

上次编辑于: