【Redis常见问题总结】

文章目录

1、redis介绍

Redis(Remote Dictionary Server)是一个开源的内存数据存储系统,也可以用作缓存和消息队列代理。它支持多种数据结构,包括字符串、哈希、列表、集合、有序集合等。Redis最初由Salvatore Sanfilippo开发,于2009年首次发布。

数据结构存储:Redis支持多种数据结构,包括字符串、哈希表、列表、集合、有序集合等。这些数据结构使得Redis可以存储和操作各种类型的数据。

内存存储:Redis的数据通常存储在内存中,这使得它具有非常高的读写速度。另外,Redis也支持数据持久化,可以将数据保存到磁盘上以防止数据丢失。

持久化:Redis支持两种持久化方式,分别是快照(snapshot)和日志(append-only file)。快照是通过将当前数据集的副本写入磁盘来实现的,而日志则是将每次写操作追加到文件中。这些持久化方式可以用于数据备份和恢复。

高性能:由于数据存储在内存中,并且Redis使用单线程模型来处理请求,因此它具有出色的性能表现。此外,Redis还支持通过主从复制和分片来提高性能和可扩展性。

支持丰富的功能:Redis提供了许多附加功能,例如事务(transaction)、发布订阅(pub/sub)、Lua脚本等,这些功能使得Redis在各种场景下都能发挥作用。

缓存:由于其快速读取能力和丰富的数据结构,Redis常用作缓存系统。它可以缓存频繁访问的数据,减少对后端存储系统的访问压力,从而提高整体性能。

消息代理:Redis支持发布订阅模式,可以用作简单的消息代理系统。发布者发布消息到指定的频道,订阅者可以订阅这些频道以接收消息。

2、redis常见的集合类型

Redis存储的数据结构一共有五种数据类型:string、list、set、hash、zset

string的底层数据:动态字符串

list的底层结构:双端连表和压缩列表

hash的底层结构:压缩列表、字典

set的底层结构:字典

zset的底层数据结构:压缩链表、跳跃表

#3、 redis应用场景:

缓存存储:Redis最直接的应用是将热点数据存储在内存中,加快数据访问速度:在Web应用中,使用Redis缓存可以显著提升系统的响应速度和用户体验。

分布式锁:在分布式系统中,Redis提供了一种实现分布式锁的方式,确保数据的一致性和同步。

计数器:Redis的INCR命令使其成为一个高效的计数器,适用于如网站访问量统计等场景。

消息队列:Redis的发布/订阅功能可以作为消息队列使用,适用于异步任务处理和事件驱动的系统。

地理位置服务:Redis支持地理位置的存储和检索,对于基于位置的服务(LBS)应用非常有用。

实时排行榜:利用Redis的有序集合功能,可以轻松实现实时排行榜。

会话存储:使用Redis存储会话数据,可以在无状态的服务器之间共享用户状态数据。

全页缓存:Redis还可以用作全页缓存(FPC),提高网页加载速度。

Token存储:用户登录成功之后,使用Redis存储Token

登录失败次数计数:使用Redis计数,登录失败超过一定次数,锁定账号

地址缓存:对省市区数据的缓存

分布式锁:分布式环境下登录、注册等操作加分布式锁

4、思考redis为什么这么快

1.内存存储:Redis 是一个基于内存的数据存储系统,数据存储在内存中,这使得它能够快速地读写数据。与传统的基于磁盘存储的数据库相比,内存存储可以大大减少数据访问的延迟。

2.单线程模型:Redis 使用单线程模型来处理客户端的请求。虽然听起来似乎会导致性能瓶颈,但实际上,由于 Redis 大部分时间都在执行内存操作,而不是 I/O 操作,因此单线程模型可以避免多线程并发带来的开销,使得 Redis 能够更高效地利用 CPU 资源。

3.非阻塞 I/O:Redis 使用非阻塞 I/O 操作,这意味着它可以在等待数据传输的同时执行其他操作,而不会阻塞整个进程。这使得 Redis 能够更有效地处理大量并发请求。

4.优化的数据结构:Redis 提供了丰富的数据结构,如字符串、哈希表、列表、集合和有序集合等,这些数据结构都经过了优化,使得在不同的使用场景下能够以高效的方式存储和操作数据。

5.持久化和复制:虽然 Redis 是一个基于内存的数据库,但它也支持持久化和复制。通过将数据写入磁盘并允许配置主从复制,Redis 提供了数据持久性和高可用性的解决方案,同时保持了高性能。

redis实现单线程模型、

Redis 单线程指的是 接收客户端请求->解析请求 ->进行数据读写等操作->发生数据给客户端」这个过程是由一个线程(主线程)来完成的,这也是我们常说 Redis 是单线程的原因。

Redis 程序并不是单线程的,Redis 在启动的时候,是会启动后台线程(BIO)的:

Redis 在 2.6 版本,会启动 2 个后台线程,分别处理关闭文件、AOF 刷盘这两个任务;

Redis 在 4.0 版本之后,新增了一个新的后台线程,用来异步释放 Redis 内存,也就是 lazyfree 线程。例如执行 unlink key / flushdb async / flushall async 等命令,会把这些删除操作交给后台线程来执行,好处是不会导致 Redis 主线程卡顿。因此,当我们要删除一个大 key 的时候,不要使用 del 命令删除,因为 del 是在主线程处理的,这样会导致 Redis 主线程卡顿,因此我们应该使用 unlink 命令来异步删除大key。

之所以 Redis 为「关闭文件、AOF 刷盘、释放内存」这些任务创建单独的线程来处理,是因为这些任务的操作都是很耗时的,如果把这些任务都放在主线程来处理,那么 Redis 主线程就很容易发生阻塞,这样就无法处理后续的请求了。

关闭文件、AOF 刷盘、释放内存这三个任务都有各自的任务队列:

BIO_CLOSE_FILE,关闭文件任务队列:当队列有任务后,后台线程会调用 close(fd) ,将文件关闭;

BIO_AOF_FSYNC,AOF刷盘任务队列:当 AOF 日志配置成 everysec 选项后,主线程会把 AOF 写日志操作封装成一个任务,也放到队列中。当发现队列有任务后,后台线程会调用 fsync(fd),将 AOF 文件刷盘,

BIO_LAZY_FREE,lazy free 任务队列:当队列有任务后,后台线程会 free(obj) 释放对象 / free(dict) 删除数据库所有对象 / free(skiplist) 释放跳表对象;

redis 实现IO多路复用

什么是I/O多路复用

I/O模型是指网络I/O模型。

是服务端如何管理连接,如何请求连接的措施,是用一个进程管理一个连接(PPC),还是一个线程管理一个连接(TPC),亦或者一个进程管理多个连接(Reactor)。

IO多路复用中多路就是多个TCP连接(或多个Channel),复用就是指复用一个或少量线程。

是多个网路IO复用一个或少量线程来处理这些连接。

参考文章:IO多路复用

5、redis持久化

Redis 提供了两种主要的持久化机制,以确保数据在服务重启或崩溃时不会丢失。这两种持久化机制分别是快照(Snapshot)和追加文件(Append-Only File,AOF)。下面我将介绍这两种持久化方式:

  1. 快照(Snapshot)持久化:

    • 快照持久化是 Redis 的默认持久化方式。
    • 它通过周期性地将内存中的数据快照(snapshot)写入磁盘上的一个二进制文件(RDB 文件)来实现。
    • RDB 文件包含了某个时间点上的所有数据,因此在恢复数据时,可以加载这个文件来还原数据状态。
    • 快照持久化适用于需要定期备份数据的场景,但在故障发生时可能会导致一定量的数据丢失。
  2. 追加文件(Append-Only File,AOF)持久化:

    • AOF 持久化以追加的方式将每个写操作(包括命令、数据更新等)记录到一个文本文件中。
    • 这个文本文件包含了将数据从空状态还原到当前状态所需的所有写操作,因此它是一个逐步构建的日志。
    • AOF 文件的内容是可读的,因此可以用于数据恢复、备份以及分析 Redis 的操作历史。
    • AOF 持久化适用于对数据完整性要求高的场景,因为它可以最小化数据丢失。

可以根据应用的需求选择其中一种或同时使用两种持久化方式。通常,Redis 用户会根据以下考虑选择持久化方式:

  • 数据一致性需求: 如果应用程序要求最小化数据丢失,通常会选择 AOF 持久化,因为它可以提供更细粒度的数据恢复。
  • 备份需求: 如果需要定期备份数据以用于灾难恢复,快照持久化通常更方便,因为它创建了一个完整的数据快照。
  • 性能: AOF 持久化会导致更多的磁盘写入操作,可能会对性能产生一定的影响。因此,快照持久化在性能上可能更有优势。

此外,Redis 还提供了混合持久化(Mixed Persistence)的功能,允许同时使用快照和 AOF 持久化,以提供更灵活的持久化配置。这使得可以在快照备份的同时,通过 AOF 文件记录写操作以确保数据完整性。

AOF的实现过程

Redis 的 AOF(Append-Only File)持久化是一种将写操作追加到日志文件的方式,用于记录 Redis 服务器的所有写操作。这个日志文件可以用来还原数据,并且在服务器重启时重新执行操作,从而实现数据的持久化。以下是 Redis AOF 持久化的实现过程:

  1. AOF 文件的创建和写入:

    • 当 Redis 服务器接收到写操作(例如 SET、INCR、DEL 等)时,它不仅会将操作应用到内存中的数据结构,还会将操作以追加的方式写入 AOF 文件。
    • Redis 使用操作的文本表示(例如 SET key value)将命令追加到 AOF 文件的末尾。
    • AOF 文件包含了一系列写操作的历史记录,这使得可以通过重新执行这些操作来还原数据。
  2. AOF 文件的同步:

    • 为了确保数据不会丢失,Redis 提供了不同的 AOF 同步选项,包括 alwayseverysecno
    • always 模式下,每个写操作都会同步到 AOF 文件,确保数据的持久性和完整性。但这也可能导致性能下降,因为每个写操作都需要进行磁盘同步。
    • everysec 模式下,Redis 每秒同步一次 AOF 文件,这可以提供良好的性能和相对较低的数据丢失风险。
    • no 模式下,Redis 不主动同步 AOF 文件,而是依赖操作系统进行同步。这可能会导致较高的数据丢失风险,但具有最好的性能。
  3. AOF 文件的重写:

    • 为了避免 AOF 文件不断增长,Redis 提供了 AOF 文件的后台重写机制。
    • AOF 重写是一个可选的操作,它会生成一个新的 AOF 文件,其中只包含可以完整还原数据的最小写操作集合。
    • 这个过程不会阻塞主 Redis 进程,因为 AOF 重写是在后台进行的。一旦重写完成,旧的 AOF 文件会被替换,从而减小了文件大小。
  4. AOF 文件的恢复:

    • 当 Redis 服务器启动时,它会尝试从 AOF 文件中重新执行保存的写操作以还原数据状态。
    • Redis 会逐行读取 AOF 文件中的命令,并将其应用到内存中的数据结构,从而恢复数据。
    • 如果 AOF 文件非常大,恢复可能会花费一定的时间,但这允许 Redis 在崩溃后快速恢复到最后一次同步点的状态。

总之,Redis 的 AOF 持久化通过将写操作以文本形式追加到文件中,以及提供不同的同步选项和后台重写机制,实现了数据的持久性和恢复能力。这使得 Redis 成为一个可靠的数据存储解决方案,适用于多种应用场景。

RDB的实现过程

Redis的RDB(Redis Database Dump)持久化是一种将内存中的数据以二进制形式快照到磁盘的方式,用于数据的备份和恢复。以下是Redis RDB持久化的实现过程:

  1. 触发快照:

    • RDB持久化可以手动触发,也可以通过配置文件中的自动保存选项来定期触发。自动保存选项通常配置了多个不同的时间间隔,Redis会根据这些时间间隔周期性地执行快照操作。
    • 当一个快照被触发时,Redis会开始执行RDB持久化。
  2. 数据快照的创建:

    • Redis会创建一个子进程来执行RDB快照操作,以避免阻塞主Redis进程。
    • 在创建数据快照之前,Redis会使用复制-on-write机制创建数据副本,以确保在创建快照期间不会修改数据。
    • 数据快照以二进制格式保存内存中的数据,包括所有数据库的键值对、数据类型、过期时间等信息。
  3. 快照数据的写入:

    • 数据快照首先会写入一个临时文件,确保在写入完成之前不会破坏原始RDB文件。
    • 当快照写入完成后,Redis会用新的快照文件替换旧的RDB文件。这是一个原子操作,确保RDB文件的一致性。
  4. 快照完成和恢复:

    • 一旦快照完成,Redis会记录生成快照的时间戳和其他信息。
    • 当Redis需要重启或者从快照文件中恢复数据时,它会加载最新的RDB文件,并将其中的数据恢复到内存中。
    • 数据恢复后,Redis可以立即提供服务,且数据与快照时间点一致。

总结来说,Redis的RDB持久化是通过创建内存数据的二进制快照来实现的,这个快照可以随时备份并在需要时恢复数据。RDB持久化适用于备份和恢复整个数据集,以及在启动时快速加载数据。但需要注意的是,RDB持久化是点对点的备份方式,可能会导致一定量的数据丢失,具体取决于触发快照的时间间隔。

6、redis集群

redis集群的搭建

在 Redis 中搭建集群可以提高性能和可用性,分布数据存储,以及实现负载均衡。Redis 集群是一个分布式系统,由多个 Redis 节点组成,每个节点负责存储一部分数据。以下是在 Redis 中搭建集群的基本步骤:

注意:Redis 集群需要至少6个 Redis 实例,通常是3个主节点和3个从节点,以确保高可用性。

  1. 安装和配置 Redis:

    • 首先,你需要在每个节点上安装 Redis。
    • 确保每个节点的 Redis 配置文件(redis.conf)已正确配置,包括绑定IP地址、端口、集群配置等。你需要在配置文件中指定集群模式并提供集群配置文件的路径。
  2. 创建集群配置文件:

    • 在一个节点上,使用 redis-trib.rb 工具来创建集群配置文件。
    • 运行以下命令创建集群配置文件:

    redis-trib.rb create --replicas 1 <node1>:<port> <node2>:<port> <node3>:<port> ...

其中,<node1>:<port><node2>:<port>,等等是你要添加到集群的节点的地址和端口。--replicas 选项指定了每个主节点的从节点数量。

  1. 启动 Redis 节点:

    • 启动每个 Redis 节点,确保它们读取了集群配置文件并按照配置运行。
  2. 添加节点到集群:

    • 使用 redis-trib.rb 工具将每个节点添加到集群。运行以下命令来添加节点:

      redis-trib.rb add-node <new-node>:<port> <existing-node>:<port>
      

      这将把 <new-node>:<port> 添加到 <existing-node>:<port> 所在的集群中。

  3. 重新分配槽位(Slots):

    • 在 Redis 集群中,数据被分割成一系列槽位。你需要手动将槽位分配给各个节点,以确保负载均衡。
    • 使用 redis-trib.rb 工具来分配槽位,或者使用 CLUSTER SETSLOT 命令来手动设置。
  4. 测试集群:

    • 使用 redis-trib.rb 工具或者连接到集群中的任何节点来测试 Redis 集群。确保数据正确分布,节点之间可以互相通信,并且集群可以正常工作。
  5. 监控和维护:

    • 定期监控 Redis 集群的性能和可用性。
    • 需要备份和恢复集群中的数据,以防止数据丢失。

Redis 集群的搭建和维护是一个复杂的过程,需要谨慎处理。建议阅读 Redis 官方文档中关于集群的详细信息,并确保按照最佳实践来设置和运行集群。另外,你也可以考虑使用 Redis Sentinel 或第三方管理工具来简化集群的维护和监控。

redis集群的原理

Redis集群通过数据分区来实现数据的分布式存储,通过自动故障转移实现高可用。

redis分区在集群创建时就已经完成了。

设置节点

Redis集群一般由多个节点组成,节点数量至少为6个才能保证组成完整高可用的集群。每个节点需要开启配置cluster-enabled yes,让Redis运行在集群模式下。

节点握手

节点握手是指一批运行在集群模式下的节点通过Gossip协议彼此通信, 达到感知对方的过程。节点握手是集群彼此通信的第一步,由客户端发起命 令:cluster meet{ip}{port}。完成节点握手之后,一个个的Redis节点就组成了一个多节点的集群。

分配槽:Redis集群把所有的数据映射到2^32-1个槽中。每个节点对应若干个槽,只有当节点分配了槽,才能响应和这些槽关联的键命令。通过 cluster addslots命令为节点分配槽。

故障转移

Redis集群的故障转移和哨兵的故障转移类似,但是Redis集群中所有的节点都要承担状态维护的任务。

故障发现

Redis集群内节点通过ping/pong消息实现节点通信,集群中每个节点都会定期向其他节点发送ping消息,接收节点回复pong 消息作为响应。如果在cluster-node-timeout时间内通信一直失败,则发送节 点会认为接收节点存在故障,把接收节点标记为主观下线(pfail)状态。

主观下线

通知其他机器:

超半数以上报主观下班,就准备将6186客观下线。

redis集群如何分区

1.节点取余分区

节点取余分区,非常好理解,使用特定的数据,比如Redis的键,或者用户ID之类,对响应的hash值取余:hash(key)%N,来确定数据映射到哪一个节点上。

不过该方案最大的问题是,当节点数量变化时,如扩容或收缩节点,数据节点映射关 系需要重新计算,会导致数据的重新迁移。

2.一致性哈希分区

一致性哈希分区实现思路是为系统中的每个节点分配一个token,范围是0~232-1,这些token构成一个哈希环。数据读写执行节点查找操作时,先根据key计算出哈希值,然后顺时针找到第一个遇到的token节点。

一致性哈希的缺点:

(1) 当使用少量节点时,节点变化将大范围的影响哈希环中的数据映射,因此这种方法不适合少量数据节点的分布式方案。

(2) 加减节点会造成哈希环中的部分数据无法命中,需要手动处理这部分数据。

(3) 由于哈希倾斜性,当数据节点较少时,往往导致某个节点负载过大而其他节点负载过小,负载不均衡。

  1. 虚拟槽分区

Redis Cluster采用的就是虚拟槽分区。虚拟槽分区巧妙的使用了哈希空间,使用分散度良好的哈希函数将所有的数据映射到一个固定范围的整数集合中,整数定义为槽(slot)。这个范围一般远远大于节点数,这是为了消除哈希的倾斜性,便于数据拆分和扩展。例如Redis Cluster槽的范围是0~16383。槽是集群内数据管理和迁移的基本单位,每个节点都会负责一定数量的槽。

如在Redis中,假设有5个节点,每个节点平均负责3276个槽。

由于采用了分散性较好的哈希函数,所有的数据大致均匀分布在0~16383各个槽中,计算公式为slot = CRC16(key) & 16383,当要操作数据时,只需要计算出相应的槽,并根据槽即可找到对应的节点。

虚拟槽分布的特点:

(1) 解耦数据和节点之间的关系,简化了节点的扩容和收缩。

(2) 解决了普通一致性哈希分区只有少量节点负载不均衡问题。

(3) 支持节点、槽、键之间的映射查询,用于数据路由。

Redis集群的原理

Redis集群通过数据分区来实现数据的分布式存储,通过自动故障转移实现高可用。

1.数据分区(Sharding):Redis集群将数据分成多个区块,每个区块称为一个槽(slot)。槽的数量固定,通常是16384个。每个节点负责处理一部分槽,节点之间不会有重叠。

节点间通信:Redis集群中的节点之间通过内部协议进行通信。节点之间会互相感知对方的存在,并且进行数据的交换和同步。

2.节点状态维护:Redis集群中的节点会相互监控彼此的状态。如果某个节点不可用,集群会自动进行故障转移,将失效节点的槽重新分配到其他可用节点上,保证数据的可用性。

3.客户端路由:客户端连接到Redis集群时,需要进行槽的映射,确定要访问的槽属于哪个节点。Redis集群通过提供集群模式下的客户端库或者代理来实现这一功能,客户端库或代理会负责将命令路由到正确的节点上。

4.数据复制(Replication):为了保证数据的可靠性和容错性,Redis集群中的每个节点通常都会有多个副本。当主节点写入数据时,它会将数据同步给其它从节点。如果主节点失效,集群会自动从从节点中选举新的主节点。

5.配置更新:Redis集群允许动态地添加或删除节点,以及对节点进行配置更新。当进行这些操作时,集群会自动进行槽的重新分配和数据的迁移,保证整个集群的平衡和可用性。

7、 redis过期删除和淘汰策略

Redis 中有两种不同的策略来控制内存中数据的清理:过期删除(Expiration)和内存淘汰(Eviction)。它们分别用于处理过期键和内存不足的情况。

  1. 过期删除(Expiration):

    • Redis 允许为每个键设置过期时间(TTL,Time To Live),一旦键过期,它就会被自动删除。
    • 过期时间可以通过 EXPIRETTLPEXPIRE 等命令来设置,以秒或毫秒为单位。
    • Redis 使用定期扫描过期键,并删除已经过期的键。这个过期键扫描是一种懒惰的方式,因为它并不会在键过期时立即删除,而是在后台的扫描任务中进行。
    • 这种方式确保了 Redis 的写操作的低延迟,因为过期键的删除是异步执行的。
  2. 内存淘汰(Eviction):

    • 当 Redis 内存用量超出配置的最大内存限制时,它会根据内存淘汰策略删除一些键以腾出内存空间。
    • 常见的内存淘汰策略包括:LRU(Least Recently Used,最近最少使用)、LFU(Least Frequently Used,最不经常使用)、Random(随机淘汰)等。
    • 可以通过配置文件或运行时命令来选择内存淘汰策略。例如,可以使用 maxmemory-policy 配置选项设置淘汰策略。

总结来说,过期删除和内存淘汰是 Redis 管理内存的两种主要方式。过期删除用于自动删除过期的键,而内存淘汰用于在内存不足时选择性删除键以释放内存空间。这两种机制结合在一起,使得 Redis 能够有效地管理内存,并确保高效的读写性能。根据具体的应用场景和需求,你可以选择合适的过期时间和内存淘汰策略。

8、redis的内存淘汰策略

Redis 提供了多种内存淘汰策略,用于在内存不足时选择性删除键以释放内存空间。以下是常见的 Redis 内存淘汰策略:

1.noeviction:默认策略,不会删除任何数据,拒绝所有写入操作并返 回客户端错误信息,此 时Redis只响应读操作。

2.volatile-lru:根据LRU算法删除设置了超时属性(expire)的键,直 到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。

3.allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性, 直到腾出足够空间为止。

4.allkeys-random:随机删除所有键,直到腾出足够空间为止。

5.volatile-random:随机删除过期键,直到腾出足够空间为止。

6.volatile-ttl:根据键值对象的ttl属性,删除最近将要过期数据。如果 没有,回退到noeviction策略

9、Redis阻塞?怎么解决?

1、因为 Redis 是单线程的,大量的慢查询可能会导致 redis-server 阻塞,可以通过 slowlog get n 获取慢日志,查看详情情况。

Redis使用过程中,有时候会出现大key的情况, 比如:

2.大key问题

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

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

大key会造成什么问题呢?

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

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

造成Redis集群中数据倾斜

主动删除、被动删等,可能会导致阻塞

如何找到大key?

bigkeys命令:使用bigkeys命令以遍历的方式分析Redis实例中的所有Key,并返回整体统计信息与每个数据类型中Top1的大Key

redis-rdb-tools:redis-rdb-tools是由Python写的用来分析Redis的rdb快照文件用的工具,它可以把rdb快照文件生成json文件或者生成报表用来分析Redis的使用详情。

如何处理大key?

删除大key:

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

当Redis版本小于4.0时,避免使用阻塞式命令KEYS,而是建议通过SCAN命令执行增量迭代扫描key,然后判断进行删除。

压缩和拆分key:

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

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

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

3、fork 子进程

在 RDB 生成和 AOF 重写时,会 fork 一个子进程完成持久化工作,当 fork 操作执行太过耗时也会造成阻塞,阻塞原因是该操作会复制父进程的空间内存表,即 fork 操作耗时跟内存量(数据集)关系较大。

fork 操作是重量级操作,会复制父进程的空间内存表(理论上需要复制与父进程同样的内存,但是 linux 有写时复制机制,父子进程贡献相同的物理内存页,实际会小很多,10G 大概只需要 20MB)。

fork 耗时应该在 20ms/GB;应该严格控制每个实例可使用的最大内存 10GB 以内(复制空间内存表);降低 fork 操作执行频率,适当放宽 AOF 重写触发时机。

使用 info stats 命令获取 lastest_fork_usec 指标,表示 redis 最近一次 fork 操作耗时。

4、AOF 刷盘阻塞

开启 AOF,文件刷盘一般每秒一次,硬盘压力过大时,fsync 需要等待写入完成。

查看 redis 日志或 info persistence 统计中的 aof_delayed_fsync 指标。

5、Redis 输入缓冲区可能导致的阻塞

输入缓冲区:redis 为每个客户端分配了输入缓冲区,其会将客户端发送命令临时保存,然后取出来执行。 qbuf 表示总容量(0 表示没有分配查询缓冲区),qbuf-free 表示剩余容量(0 表示没有剩余空间);大小不能超过 1G,当大小超过 1G 时会将客户端自动关闭,输入缓冲区不受 maxmemory 限制。

当大量的 key 进入输入缓冲区且无法被消费时,即可造成 redis 阻塞;通过 client list 命令可定位发生阻塞的客户端;通过 info clients 命令的 blocked_clients 参数可以查看到当前阻塞的命令。

6、Redis 输出缓冲区可能导致的阻塞

输出缓冲区(client output buffer):是 redis-server 端实现的一个读取缓冲区,redis-server 在接收到客户端的请求后,把获取结果写入到 client buffer 中,而不是直接发送给客户端。从而可以继续处理客户端的其他请求,这样异步处理方式使 redis-server 不会因为网络原因阻塞其他请求的处理。

class :客户端种类,normal、slave、pubsub

normal:普通的客户端

slave: 从库的复制客户端

pub/sub: 发布与订阅的客户端

hard limit: 缓冲区大小的硬性限制。

soft limit: 缓冲去大小的软性限制。

soft seconds: 缓冲区大小达到了(超过)soft limit 值的持续时间。

client-output-buffer-limit 参数限制分配的缓冲区的大小,防止内存无节制的分配。参数的默认值都为 0,意思是不做任何限制。

redis server 触发保护机制主要有两种情况:

client buffer 的大小达到了 soft limit 并持续了 soft seconds 时间,将立即断开和客户端的连接。

client buffer 的大小达到了 hard limit,server 也会立即断开和客户端的连接。

7、Swap(内存交换)

内存不足把一部分硬盘空间虚拟成内存使用。

内存与硬盘的读写速度差几个数量级, Redis 性能急剧下降。

识别 Redis 发生 Swap 的检查方法如下:

1.查询 Redis 进程号

redis-cli -p 6383 info server | grep process_id

process_id: 4476

2.根据进程号查询内存交换信息

cat /proc/4476/smaps | grep Swap

Swap: 0kB

Swap: 0kB

Swap: 4kB

Swap: 0kB

Swap: 0kB

...

如果交换量都是 0KB 或者个别的是 4KB,则正常。

预防内存交换的方法:

保证机器充足的可用内存

确保所有 Redis 实例设置最大可用内存(maxmemory),防止极端情况 Redis 内存不可控的增长

降低系统使用 swap 优先级,如echo 10 > /proc/sys/vm/swappiness。

8、网络问题

  1. 连接拒绝
    网络闪断:一般在网络割接或带宽耗尽的情况;
    redis 连接拒绝:连接数大于 maxclients 时拒绝新的连接进入,可以关注 info stats 的 rejected_connections 指标;
    连接溢出:
    进程限制:进程可打开最大文件数控制 ------ ulimit -n,通常 1024,大量连接的 redis 需要增大该值;
    backlog 队列溢出:系统对于特定端口 tcp 连接使用 backlog 队列保存,redis 默认 511,系统 backlog 默认 128,线上可使用 cron 定时执行 netstat -s | grep overflowed 统计;
  2. 网络延迟
    测量机器之间的网络延迟

redis-cli -h {ip} -p {port} --latency

redis-cli -h {ip} -p {port} --latency-history 默认15秒完成一行统计,-i控制采样时间

redis-cli -h {ip} -p {port} --latency-dist 统计图展示,每1秒采样一次

10、redis常见的问题

缓存击穿

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

解决方案:

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

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

缓存穿透

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

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

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

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

自身业务代码问题

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

它主要有两种解决办法:

缓存空值/默认值

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

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

缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。

例如过期时间设置为5分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致。

这时候可以利用消息队列或者其它异步方式清理缓存中的空对象。

布隆过滤器:

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

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

缓存雪崩

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

提高缓存可用性

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

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

过期时间

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

热点数据永不过期。

熔断降级

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

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

10、保证数据库和缓存一致性

一致性介绍:

强一致性:这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大

弱一致性:这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态

最终一致性:最终一致性是弱一致性的一个特例,系统会保证在一定时间内,能够达到一个数据一致的状态。这里之所以将最终一致性单独提出来,是因为它是弱一致性中非常推崇的一种一致性模型,也是业界在大型分布式系统的数据一致性上比较推崇的模型

根据CAP理论,在保证可用性和分区容错性的前提下,无法保证一致性,所以缓存和数据库的绝对一致是不可能实现的,只能尽可能保存缓存和数据库的最终一致性。

分析下面几种解决方案的数据同步方案:

1.先更新缓存,再更新数据库:先更新缓存可以提高读取性能,但如果更新缓存成功而更新数据库失败,可能导致数据不一致。

2.先更新数据库,再更新缓存:确保数据的持久性,但如果更新数据库成功而更新缓存失败,也可能导致数据不一致。

3.先删除缓存,后更新数据库:通过先删除缓存,再更新数据库的方式,可以在数据更新后保证数据的一致性,但会降低读取操作的性能。

4.先更新数据库,后删除缓存:确保数据的持久性,并在更新数据库成功后再删除缓存,以保持数据的一致性。

新增数据类

对于新增数据的情况,数据会直接写入数据库,无需对缓存进行操作。在这种情况下,缓存中本身就没有新增数据,而数据库中保存的是最新值。因此,缓存和数据库的数据是一致的。

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

惰性删除:

惰性删除指的是当我们查询key的时候才对key进⾏检测,如果已经达到过期时间,则删除。显然,他有⼀个缺点就是如果这些过期的key没有被访问,那么他就⼀直⽆法被删除,⽽且⼀直占⽤内存。

定期删除:

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

相关推荐
Hello-Brand1 分钟前
Java核心知识体系10-线程管理
java·高并发·多线程·并发·多线程模型·线程管理
乐悠小码7 分钟前
数据结构------队列(Java语言描述)
java·开发语言·数据结构·链表·队列
史努比.9 分钟前
Pod控制器
java·开发语言
2的n次方_11 分钟前
二维费用背包问题
java·算法·动态规划
皮皮林55112 分钟前
警惕!List.of() vs Arrays.asList():这些隐藏差异可能让你的代码崩溃!
java
莳光.12 分钟前
122、java的LambdaQueryWapper的条件拼接实现数据sql中and (column1 =1 or column1 is null)
java·mybatis
程序猿麦小七17 分钟前
基于springboot的景区网页设计与实现
java·spring boot·后端·旅游·景区
weisian15123 分钟前
认证鉴权框架SpringSecurity-2--重点组件和过滤器链篇
java·安全
蓝田~25 分钟前
SpringBoot-自定义注解,拦截器
java·spring boot·后端
.生产的驴28 分钟前
SpringCloud Gateway网关路由配置 接口统一 登录验证 权限校验 路由属性
java·spring boot·后端·spring·spring cloud·gateway·rabbitmq