Redis Stack十部曲之五:管理Redis

文章目录

  • 安全
    • ACL
    • TLS
  • 配置
    • redis.conf`配置文件
    • 通过命令行传递参数
    • 动态修改Redis配置
    • Redis作为缓存使用
  • Redis Sentinel
    • Redis Sentinel像一个分布式系统
    • 运行Redis Sentinel
    • 配置Redis Sentinel
      • quorum参数
      • 其它Sentinel 选项
    • Redis Sentinel部署模式探索
      • ASCII 图示说明
      • 模式1:只用两个 Sentinel,不要这样做
      • 模式2:基本设置,三个盒子
      • 模式3:在客户端盒子中放置 Sentinel
      • 模式4:客户端侧的 Sentinel 设置,少于三个客户端
      • Sentinel, Docker, NAT和可能的问题
    • Redis Sentinel部署示例
      • Sentinel 配置文件示例
      • 启动 Sentinel
      • 查询主节点状态
      • 查询副本和其他 Sentinels
      • 获取当前主节点的地址
      • 测试故障转移
    • Sentinel API
    • Sentinel 命令
    • 在运行时重新配置Sentinel
    • 增加或删除Sentinel
    • 移除旧的主节点和无法到达的从节点
    • 发布/订阅消息
  • Redis Replication
    • 主服务器关闭持久化时复制的安全性
    • Redis Replication是如何工作的
    • 复制ID
    • 无盘复制
    • 配置
    • 只读副本
    • Redis Replication如何处理键过期
    • INFO 和 ROLE 命令
    • 重启和故障转移后的部分同步
    • 副本上的 maxmemory 设置
  • Redis Cluster
  • Redis持久化
    • RDB的优点和缺点
    • AOF的优点和缺点
    • 如何选择
    • 快照
      • 配置保存
      • 手动保存
    • 只追加文件
      • AOF 文件的工作原理
      • AOF 日志重写
        • 安全的重写机制
        • 自动重写(Redis 2.4 及更高版本)
        • Redis 7.0.0 的改进
      • AOF的持久性
      • 处理 AOF 文件截断
      • 处理 AOF 文件损坏
      • AOF是如何工作的
      • 如果我目前正在使用RDB,我如何切换到AOF?
      • AOF和RDB持久性之间的交互
      • RDB持久化备份
      • AOF持久化备份
  • 优化
  • 故障排查
  • 调试

安全

Redis 的安全模型依赖于可信的客户端和环境。其设计初衷是使 Redis 只被可信客户端访问,通常不应该直接暴露 Redis 实例到互联网或不受信任的环境中。在安全方面,Redis 提供了多层防护措施,主要包括:

  1. 访问控制

Redis 提供访问控制机制,推荐使用 Redis 6 及以上版本的访问控制列表(ACL),以创建具备细粒度权限的命名用户。传统的认证方式是通过 requirepass 设置在 redis.conf 文件中定义密码,客户端必须通过 AUTH 命令认证。

  1. 网络安全

为保证网络安全,应确保 Redis 端口仅对可信客户端开放,建议使用防火墙阻止外部访问。可以在 redis.conf 文件中指定绑定本地接口,示例如下:

bind 127.0.0.1

避免暴露 Redis 端口以防止如 FLUSHALL 命令导致的数据丢失。

  1. 受保护模式

Redis 从 3.2.0 版本开始引入了受保护模式,默认情况下,如果没有设置绑定接口或密码,Redis 将仅对本地请求作出响应,而拒绝外部访问。这一模式旨在减少暴露 Redis 实例所带来的安全隐患。

  1. TLS 支持

Redis 还支持 TLS,可以对所有通信通道进行加密,包括客户端连接、复制链接以及 Redis 集群总线协议,确保传输过程中的数据安全。

  1. 禁止特定命令

可以通过 redis.conf 文件中的 rename-command 指令,禁止或重命名 Redis 命令,防止未授权客户端执行敏感操作。例如,以下命令将 CONFIG 命令重命名为不可猜测的字符串:

rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
  1. 恶意输入攻击防护

Redis 实现了基于随机种子的哈希函数来防止恶意输入攻击。此外,Redis 使用了 qsort 算法进行排序,但由于该算法未随机化,恶意用户可能通过精心设计的输入触发最坏情况的行为,导致服务性能下降。

  1. 代码安全

在经典的 Redis 配置中,客户端可以访问所有命令,但 Redis 本身采用了多种安全编码实践,防止如缓冲区溢出等问题。建议运行 Redis 时不要使用 root 权限,而是以一个低权限的 redis 用户运行。

ACL

见ACL

TLS

见TLS

配置

Redis的确可以在没有配置文件的情况下启动,使用默认配置,但这种方式只适合于测试和开发阶段。在生产环境中,推荐通过redis.conf配置文件来配置Redis。

redis.conf`配置文件

redis.conf文件中的配置指令格式非常简单:

关键字 参数1 参数2 ... 参数N

例如:

replicaof 127.0.0.1 6380

如果参数包含空格,可以使用双引号或单引号将其包裹,例如:

requirepass "hello world"

单引号内的字符串可以包含反斜杠转义的字符,双引号内的字符串还可以使用带有反斜杠的十六进制表示法,例如\xff

Redis不同版本的redis.conf文件都有详尽的描述文档,包含指令的详细解释:

  • Redis 7.4

通过命令行传递参数

除了redis.conf,还可以通过命令行直接传递Redis的配置参数,这种方式常用于测试。例如:

./redis-server --port 6380 --replicaof 127.0.0.1 6379

命令行传递参数的格式与redis.conf文件类似,唯一的区别是关键字前需要加上--。实际上,这种方式会在内存中生成一个临时的配置文件。

动态修改Redis配置

可以使用CONFIG SETCONFIG GET命令在Redis运行时动态修改或查询配置。需要注意的是,修改后的配置不会保存到redis.conf文件中,下次重启时仍会使用旧配置。如果希望永久保存动态修改的配置,可以手动修改redis.conf文件,或者使用CONFIG REWRITE命令,自动更新配置文件中与当前运行配置不一致的字段。

Redis作为缓存使用

如果打算将Redis用作缓存,可以使用以下配置:

maxmemory 2mb
maxmemory-policy allkeys-lru

在这种配置下,Redis会在内存达到2MB时,使用LRU算法自动淘汰旧的键值对,类似于Memcached的工作方式。

Redis Sentinel

Redis Sentinel 提供了高可用性解决方案,适用于不使用 Redis Cluster 时的 Redis 部署。Sentinel 能够自动监控 Redis 主从实例的状态,并在主节点出现故障时自动执行故障转移,使得应用能够继续正常工作。Redis Sentinel 的主要功能包括:

  1. 监控 (Monitoring):Sentinel 持续监控 Redis 主节点和从节点,确保它们按预期工作。如果检测到问题,Sentinel 会采取相应的措施。
    2. 通知 (Notification):当 Sentinel 发现 Redis 实例有问题时,它可以通过 API 向系统管理员或其他程序发送通知,告知存在的问题。
  2. 自动故障转移 (Automatic failover):如果主节点出现问题,Sentinel 会启动自动故障转移,将其中一个从节点提升为新的主节点,并重新配置其他从节点以指向新主节点。应用程序也会得到通知以连接到新的主节点。
  3. 配置提供者 (Configuration provider):Sentinel 作为服务发现的权威信息源,客户端可以通过连接 Sentinel 来获取当前负责服务的主节点地址。如果发生故障转移,Sentinel 会向客户端报告新的主节点地址。

通过这些功能,Redis Sentinel 能够确保 Redis 实例在出现故障时能够自动恢复和重新配置,从而实现高可用性。

Redis Sentinel像一个分布式系统

Redis Sentinel 是设计成多个 Sentinel 进程协同工作的架构。多个 Sentinel 进程共同运行的优势如下:

  1. 降低误报概率:只有当多个 Sentinel 同意某个主节点不可用时,才会判定为主节点失效。这种机制减少了误报的概率,提高了检测的准确性。
  2. 系统具备容错能力:即使并非所有的 Sentinel 进程都在工作,系统仍然能够继续运作。这使得 Sentinel 系统在故障情况下仍然健壮可靠,避免了 Sentinel 本身成为单点故障的问题。

整个系统,包括 Sentinels、Redis 实例(主节点和从节点)以及连接到 Sentinel 和 Redis 的客户端,是一个更大的分布式系统。通过这种设计,Redis Sentinel 系统不仅能够自动故障转移,而且具有高度的可靠性,适合在高可用场景中使用。

运行Redis Sentinel

如果您使用的是 redis-sentinel 可执行文件(或如果您有一个指向 redis-server 可执行文件的符号链接,并命名为 redis-sentinel),您可以使用以下命令行启动 Sentinel:

redis-sentinel /path/to/sentinel.conf

另外,您也可以直接使用 redis-server 可执行文件,以 Sentinel 模式启动:

redis-server /path/to/sentinel.conf --sentinel

这两种方式的效果是一样的。

需要注意的是,在运行 Sentinel 时,强制要求使用配置文件,因为该文件将用于保存当前状态,以便在重启时重新加载。如果未提供配置文件或配置文件路径不可写,Sentinel 将拒绝启动。

默认情况下,Sentinel 监听 TCP 26379 端口,因此,为了使 Sentinel 正常工作,您的服务器必须开放 26379 端口,以接收来自其他 Sentinel 实例的连接。否则,Sentinel 之间无法通信,也无法达成一致,故障转移将无法进行。

配置Redis Sentinel

Redis Sentinel 的配置文件 sentinel.conf 是一个自我文档化的示例配置文件,可以用来配置 Sentinel。一个典型的最小配置文件如下所示:

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5

您只需指定要监控的主节点,并为每个主节点(可以有任意数量的从节点)指定不同的名称。无需指定从节点,因为它们会自动发现。Sentinel 会自动更新配置以保留关于从节点的额外信息(以防重启)。每当从节点被提升为主节点或发现新的 Sentinel 时,配置都会重写。

上述示例配置基本上监控了两组 Redis 实例,每组由一个主节点和一个未定义数量的从节点组成。第一组实例称为 mymaster,第二组称为 resque

sentry monitor <master-name> <ip> <port> <quorum> 的参数含义如下:

  1. master-name:要监控的主节点名称(例如 mymaster)。
  2. ip:主节点的 IP 地址(例如 127.0.0.1)。
  3. port:主节点的端口(例如 6379)。
  4. quorum:达成共识以标记主节点为失败的 Sentinel 数量。

quorum参数

如果您有 5 个 Sentinel 进程,并且为某个主节点设置了 quorum 为 2,具体情况如下:

  1. 故障检测:如果有两个 Sentinel 同时认为主节点不可达,那么这两个 Sentinel 中的一个将尝试启动故障转移过程。
  2. 故障转移授权:只有当至少有三个 Sentinel 处于可用状态时,故障转移才会获得授权并实际启动。这意味着在进行故障转移之前,需要有大多数 Sentinel 进程能够互相通信。

在实践中,这意味着在故障发生期间,如果大多数 Sentinel 进程无法通信(例如,在网络分区或部分故障的情况下),Sentinel 将不会启动故障转移。此设计的目的是避免错误的故障转移,确保系统的稳定性和可用性。通过设置适当的 quorum,Sentinel 可以在不必要的情况下避免错误的判断,从而增强整个系统的稳定性。

其它Sentinel 选项

在 Redis Sentinel 的配置中,其他选项通常采用以下形式:

sentinel <option_name> <master_name> <option_value>

这些选项用于以下目的:

  1. down-after-milliseconds:这是一个以毫秒为单位的时间,当某个实例在此时间内未能响应(无论是未回复 PING 请求,还是回复错误)时,Sentinel 将开始认为该实例处于“下线”状态。
  2. parallel-syncs:设置故障转移后能够同时重新配置为使用新主节点的副本数量。较低的值将使故障转移过程的完成时间变长,但如果副本配置为服务旧数据,您可能不希望所有副本同时与主节点重新同步。尽管副本的复制过程大部分是非阻塞的,但在从主节点加载大量数据的瞬间,副本需要停止操作。通过将此选项设置为 1,您可以确保一次只有一个副本不可达。

配置参数可以在运行时修改:

  • 针对特定主节点的配置参数:可以使用 SENTINEL SET 命令进行修改。
  • 全局配置参数:可以使用 SENTINEL CONFIG SET 命令进行修改。

有关其他选项的详细描述,请参见文档的其余部分以及与 Redis 发行版一起提供的示例 sentinel.conf 文件。 运行时重新配置 Sentinel 的更多信息,可以查看相关章节,确保 Sentinel 在运行中能够灵活调整其配置,以适应不断变化的环境需求。

Redis Sentinel部署模式探索

ASCII 图示说明

我们使用 ASCII来以图形格式显示配置示例,不同的符号表示:

  • 方框:表示独立失败的计算机或虚拟机,称为“盒子”。
  • 线条:不同盒子之间的连接,表示它们能够相互通信。

实例名称约定:

  • 主节点:M1、M2、M3、…、Mn
  • 副本:R1、R2、R3、…、Rn(R 代表副本)
  • Sentinel:S1、S2、S3、…、Sn
  • 客户端:C1、C2、C3、…、Cn
  • 当实例因 Sentinel 操作而改变角色时,我们用方括号表示,例如 [M1] 表示由于 Sentinel 干预而成为主节点的实例。

模式1:只用两个 Sentinel,不要这样做

quorum = 1
+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+

在这种设置中,如果主节点 M1 失败,R1 将被提升为新的主节点,因为两个 Sentinel 能够就故障达成一致(当然,仲裁设置为 1),并且也能授权故障转移,因为大多数节点的数量为 2。这种情况下看似有效,但实际上存在一些严重问题:

  1. 单点故障:如果运行 M1 的机器停止工作,S1 也会停止工作。此时,运行在其他机器上的 Sentinel(S2)无法授权故障转移,整个系统将变得不可用。
  2. 缺乏多数:故障转移需要多数节点的同意。只有当大多数 Sentinel 能够就主节点的状态达成一致时,才能安全地进行故障转移。否则,可能导致数据不一致和系统可用性降低。
  3. 分裂脑问题:如图所示:
    +----+           +------+
    | M1 |----//-----| [M1] |
    | S1 |           | S2   |
    +----+           +------+
    
    在这个配置中,我们可能创建了两个主节点(M1 和 S2)。假设 S2 在没有授权的情况下进行了故障转移,那么客户端可能会同时向两个主节点写入数据。在网络分区恢复时,不可能确定哪个配置是正确的,这将导致“分裂脑”问题。

因此,为了避免这些潜在的灾难,请务必始终在三个不同的盒子中部署至少三个 Sentinel。这样可以确保:

  • 提高可用性:即使一个 Sentinel 失效,其他 Sentinel 仍然能够监控并进行故障转移,确保系统的可用性。
  • 避免数据不一致:通过确保大多数节点的同意,可以避免在分裂的情况下形成两个主节点。

综上所述,至少三个 Sentinel 的配置是确保 Redis 高可用性的最佳实践。

模式2:基本设置,三个盒子

quorum = 2
       +----+| M1 || S1 |+----+|
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

如果主节点 M1 失败,S2 和 S3 将会就故障达成一致,并能够授权进行故障转移,从而使客户端能够继续正常操作。

在任何 Sentinel 设置中,由于 Redis 使用异步复制,总是存在丢失写入数据的风险,因为某个已确认的写入可能无法到达被提升为主节点的副本。上述设置中由于客户端可能与旧主节点隔离,风险更高,如下图所示:

         +----+| M1 || S1 | <- C1 (写入将会丢失)+----+|//
+------+    |    +----+
| [M2] |----+----| R3 |
| S2   |         | S3 |
+------+         +----+

在这种情况下,网络分区将旧主节点 M1 隔离,因此副本 R2 被提升为主节点。然而,像 C1 这样的客户端仍然在与旧主节点相同的分区内继续写入数据。这些数据将永远丢失,因为当分区恢复时,旧主节点会被重新配置为新主节点的副本,从而丢弃其数据集。

可以使用以下 Redis 复制功能来缓解这一问题,允许主节点在检测到无法将写入传输到指定数量的副本时停止接受写入:

min-replicas-to-write 1
min-replicas-max-lag 10

在上述配置中,当 Redis 实例作为主节点时,如果无法向至少一个副本写入数据,则会停止接受写入。由于复制是异步的,无法写入实际上意味着副本要么断开连接,要么在指定的最大延迟秒数内未发送异步确认。

使用此配置,旧的 Redis 主节点 M1 在上述示例中将在 10 秒后变得不可用。当分区恢复时,Sentinel 配置将收敛到新的配置,客户端 C1 将能够获取有效的配置并继续使用新主节点。

然而,没有免费的午餐。如果两个副本都宕机,主节点将停止接受写入。这是一种权衡:在确保数据一致性和可用性之间进行选择。在设计 Redis 高可用性解决方案时,需要根据具体的使用场景仔细考虑这些设置。

模式3:在客户端盒子中放置 Sentinel

quorum = 2
            +----+         +----+| M1 |----+----| R1 ||    |    |    |    |+----+    |    +----+|+------------+------------+|            |            |+----+        +----+      +----+| C1 |        | C2 |      | C3 || S1 |        | S2 |      | S3 |+----+        +----+      +----+

在这个设置中,Sentinel 的视角与客户端相同:如果主节点可以被大多数客户端访问,那就是可行的。这里的 C1、C2 和 C3 是通用客户端,并不意味着 C1 代表一个单一的连接到 Redis 的客户端。更可能的是,它们代表的是应用服务器、Rails 应用程序等。

如果 M1 和 S1 所在的服务器发生故障,故障转移将顺利进行。然而,容易看出,不同的网络分区将导致不同的行为。例如,如果客户端与 Redis 服务器之间的网络断开,Sentinel 将无法进行配置,因为 Redis 的主节点和副本都会不可用。

模式4:客户端侧的 Sentinel 设置,少于三个客户端

quorum = 3
            +----+         +----+| M1 |----+----| R1 || S1 |    |    | S2 |+----+    |    +----+|+------+-----+|            |+----+        +----+| C1 |        | C2 || S3 |        | S4 |+----+        +----+

这种设置与示例 3 类似,但这里我们在可用的四个盒子中运行四个 Sentinel。如果主节点 M1 变得不可用,其他三个 Sentinel 将执行故障转移。理论上,如果仅移除运行 C2 和 S4 的盒子,并将 quorum 设置为 2,此设置也能工作。然而,通常我们希望在 Redis 层实现高可用性的同时,应用层也能实现高可用性。

Sentinel, Docker, NAT和可能的问题

Docker 使用了一种称为端口映射的技术:在 Docker 容器内运行的程序可能会以不同于其认为正在使用的端口进行公开。这对于在同一台服务器上同时运行多个使用相同端口的容器非常有用。然而,端口和地址的重映射会给 Sentinel 带来两个主要问题:

  1. Sentinel 自动发现失效

    • Sentinel 之间的自动发现依赖于它们发送的 hello 消息,这些消息包含它们监听的端口和 IP 地址。
    • 当使用端口映射时,Sentinel 无法了解地址或端口已经被重映射,因此它们会宣布一个不正确的信息供其他 Sentinel 连接。
  2. 主节点的 INFO 输出问题

    • 在 Redis 主节点的 INFO 输出中,副本的信息是通过主节点检查 TCP 连接的远程对等体来检测的,而副本在握手期间会发布端口。
    • 由于上述第一个问题,端口可能是错误的,从而导致副本无法被正确识别。

因为 Sentinel 使用主节点的 INFO 输出信息自动检测副本,所以在 Docker 部署的主副本实例中,Sentinel 将无法找到可用的副本,进而无法进行故障转移。为了在使用 Docker 的情况下运行一组 Sentinel 实例并解决上述问题,可以使用以下两个 Sentinel 配置指令,强制 Sentinel 宣布特定的 IP 和端口:

sentinel announce-ip <ip>
sentinel announce-port <port>

Redis Sentinel部署示例

在本节中,我们假设 Sentinel 实例在端口 5000、5001 和 5002 上运行,同时 Redis 主节点在端口 6379 上,副本在端口 6380 上。我们将整个教程中使用 IPv4 回环地址 127.0.0.1,假设您在个人计算机上运行模拟。

Sentinel 配置文件示例

sentinel5000.conf

port 5000
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1

sentinel5001.confsentinel5002.conf配置文件与 sentinel5000.conf 相同,但将端口号更改为 5001 和 5002。

启动 Sentinel

一旦您启动这三个 Sentinel 实例,您将看到它们记录的一些消息,例如:

+monitor master mymaster 127.0.0.1 6379 quorum 2

这是一个 Sentinel 事件,您可以通过 Pub/Sub 接收此类事件,如果您订阅后文中的事件名称。Sentinel 在故障检测和故障转移期间会生成和记录不同的事件。

查询主节点状态

要开始使用 Sentinel,最直接的方法是检查其监控的主节点的状态:

$ redis-cli -p 5000
127.0.0.1:5000> sentinel master mymaster

您应该看到类似以下的信息:

 1) "name"2) "mymaster"3) "ip"4) "127.0.0.1"5) "port"6) "6379"7) "runid"8) "953ae6a589449c13ddefaee3538d356d287f509b"9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "735"
19) "last-ping-reply"
20) "735"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "126"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "532439"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "1"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "60000"
39) "parallel-syncs"
40) "1"

一些关键信息如下:

  • num-other-sentinels:显示为 2,表明 Sentinel 已检测到两个其他 Sentinel 监控此主节点。如果您检查日志,您会看到生成的 +sentinel 事件。
  • flags:显示为 master。如果主节点宕机,则这里应会看到 s_down 或 o_down 标志。
  • num-slaves:正确设置为 1,表明 Sentinel 检测到有一个副本与主节点相连。

查询副本和其他 Sentinels

要进一步探索该实例,您可以尝试以下两个命令:

SENTINEL replicas mymaster
SENTINEL sentinels mymaster

第一个命令将提供有关连接到主节点的副本的类似信息,第二个命令则提供关于其他 Sentinels 的信息。

获取当前主节点的地址

正如我们所述,Sentinel 还充当配置提供者,供希望连接到一组主节点和副本的客户端使用。由于可能存在故障转移或重新配置,客户端无法知道给定实例的当前活动主节点,因此 Sentinel 提供了一个 API 来询问此问题:

127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"

测试故障转移

此时,我们的 Sentinel 部署已准备好进行测试。我们可以通过终止主节点来检查配置是否更改。为此,我们可以执行以下命令:

redis-cli -p 6379 DEBUG sleep 30

此命令将使我们的主节点在 30 秒内变得不可达,模拟主节点因某种原因挂起。如果您检查 Sentinel 日志,应该会看到大量活动:

  1. 每个 Sentinel 都会检测到主节点故障,并记录 +sdown 事件。
  2. 此事件随后升级为 +odown,表示多个 Sentinel 就主节点不可达达成一致。
  3. Sentinel 进行投票,选出一个 Sentinel 开始第一次故障转移尝试。
  4. 故障转移发生。

如果您再次询问 mymaster 的当前主节点地址,最终我们应该得到不同的回复:

127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6380"

到此为止,一切顺利……此时您可以开始创建自己的 Sentinel 部署,或者可以继续阅读以了解所有 Sentinel 命令和内部机制。

Sentinel API

Sentinel 提供了一个 API,以便检查其状态、监控主节点和副本的健康状况、订阅以接收特定通知,并在运行时更改 Sentinel 配置。

默认情况下,Sentinel 运行在 TCP 26379 端口(请注意,6379 是普通的 Redis 端口)。Sentinel 使用 Redis 协议接受命令,因此您可以使用 redis-cli 或任何其他未修改的 Redis 客户端与 Sentinel 通信。

您可以直接查询 Sentinel,以检查被监控的 Redis 实例的状态,查看它所知道的其他 Sentinel 等等。或者,通过使用 Pub/Sub,您可以接收来自 Sentinel 的推送式通知,每当某个事件发生时,例如故障转移或实例进入错误状态等。

Sentinel 命令

以下是 SENTINEL 命令的主要 API 及其子命令列表(适用的最低版本已注明):

  • SENTINEL CONFIG GET <name> (>= 6.2) 获取全局 Sentinel 配置参数的当前值。指定的名称可以是通配符,类似于 Redis 的 CONFIG GET 命令。
  • SENTINEL CONFIG SET <name> <value> (>= 6.2) 设置全局 Sentinel 配置参数的值。
  • SENTINEL CKQUORUM <master name> 检查当前 Sentinel 配置是否能够达到故障转移主节点所需的法定人数,以及授权故障转移所需的多数。此命令应在监控系统中使用,以检查 Sentinel 部署是否正常。
  • SENTINEL FLUSHCONFIG 强制 Sentinel 重新将其配置写入磁盘,包括当前 Sentinel 状态。通常,Sentinel 会在状态更改时每次重新写入配置(在重启时持久化的状态子集的上下文中)。但是,有时由于操作错误、磁盘故障、软件包升级脚本或配置管理器,配置文件可能会丢失。在这种情况下,强制 Sentinel 重新写入配置文件的方法很方便。此命令即使在之前的配置文件完全丢失的情况下也能工作。
  • SENTINEL FAILOVER <master name> 强制进行故障转移,仿佛主节点不可达,并且不需要其他 Sentinel 的同意(但会发布新版本的配置,以便其他 Sentinel 更新其配置)。
  • SENTINEL GET-MASTER-ADDR-BY-NAME <master name> 返回具有该名称的主节点的 IP 和端口号。如果正在进行故障转移或该主节点的故障转移成功终止,则返回提升后的副本的地址和端口。
  • SENTINEL INFO-CACHE (>= 3.2) 返回来自主节点和副本的缓存 INFO 输出。
  • SENTINEL IS-MASTER-DOWN-BY-ADDR 检查当前 Sentinel 视角下,指定的 ip:port 主节点是否处于关闭状态。此命令主要供内部使用。
  • SENTINEL MASTER <master name> 显示指定主节点的状态和信息。
  • SENTINEL MASTERS 显示被监控主节点的列表及其状态。
  • SENTINEL MONITOR 启动 Sentinel 的监控。有关更多信息,请参阅“运行时重新配置 Sentinel”部分。
  • SENTINEL MYID (>= 6.2) 返回 Sentinel 实例的 ID。
  • SENTINEL PENDING-SCRIPTS 此命令返回有关待处理脚本的信息。
  • SENTINEL REMOVE 停止 Sentinel 的监控。有关更多信息,请参阅“运行时重新配置 Sentinel”部分。
  • SENTINEL REPLICAS <master name> (>= 5.0) 显示该主节点的副本列表及其状态。
  • SENTINEL SENTINELS <master name> 显示该主节点的 Sentinel 实例列表及其状态。
  • SENTINEL SET 设置 Sentinel 的监控配置。有关更多信息,请参阅“运行时重新配置 Sentinel”部分。
  • SENTINEL SIMULATE-FAILURE (crash-after-election|crash-after-promotion|help) (>= 3.2) 此命令模拟不同的 Sentinel 崩溃场景。
  • SENTINEL RESET <pattern> 此命令将重置所有匹配名称的主节点。pattern 参数是一个通配符模式。重置过程将清除主节点的任何先前状态(包括进行中的故障转移),并删除与该主节点已发现并关联的每个副本和 Sentinel。

对于连接管理和管理目的,Sentinel 支持以下 Redis 命令的子集:

  • ACL (>= 6.2) 此命令管理 Sentinel 访问控制列表。有关更多信息,请参阅 ACL 文档页面和 Sentinel 访问控制列表身份验证。
  • AUTH (>= 5.0.1) 进行客户端连接的身份验证。有关更多信息,请参阅 AUTH 命令和“使用身份验证配置 Sentinel 实例”部分。
  • CLIENT 此命令管理客户端连接。有关更多信息,请参阅其子命令页面。
  • COMMAND (>= 6.2) 此命令返回有关命令的信息。有关更多信息,请参阅 COMMAND 命令及其各种子命令。
  • HELLO (>= 6.0) 切换连接的协议。有关更多信息,请参阅 HELLO 命令。
  • INFO 返回有关 Sentinel 服务器的信息和统计信息。有关更多信息,请参阅 INFO 命令。
  • PING 此命令仅返回 PONG。
  • ROLE 此命令返回字符串“sentinel”和被监控主节点的列表。有关更多信息,请参阅 ROLE 命令。
  • SHUTDOWN 关闭 Sentinel 实例。

最后,Sentinel 还支持 SUBSCRIBEUNSUBSCRIBEPSUBSCRIBEPUNSUBSCRIBE 命令。有关更多详细信息,请参阅 Pub/Sub 消息部分。

在运行时重新配置Sentinel

从 Redis 版本 2.8.4 开始,Sentinel 提供了一个 API 来添加、移除或更改指定主节点的配置。请注意,如果您有多个 Sentinel 实例,您应该对所有实例应用更改,以便 Redis Sentinel 正常工作。这意味着更改单个 Sentinel 的配置不会自动传播到网络中的其他 Sentinel。

以下是用于更新 Sentinel 实例配置的 SENTINEL 子命令列表:

  • SENTINEL MONITOR <name> <ip> <port> <quorum> 此命令指示 Sentinel 开始监控具有指定名称、IP、端口和法定人数的新主节点。它与 sentinel.conf 配置文件中的 sentinel monitor 配置指令相同,唯一的区别是您不能在 IP 中使用主机名,而需要提供 IPv4 或 IPv6 地址。
  • SENTINEL REMOVE <name> 用于移除指定的主节点:该主节点将不再被监控,并将完全从 Sentinel 的内部状态中移除,因此它将不再被 SENTINEL masters 等命令列出。
  • SENTINEL SET <name> [<option> <value> …] 此命令与 Redis 的 CONFIG SET 命令非常相似,用于更改特定主节点的配置参数。可以指定多个选项/值对(或根本不指定)。所有可以通过 sentinel.conf 配置的参数也可以使用 SET 命令进行配置。

以下是一个使用 SENTINEL SET 命令来修改名为 objects-cache 的主节点的 down-after-milliseconds 配置的示例:

SENTINEL SET objects-cache-master down-after-milliseconds 1000

如前所述,SENTINEL SET 可以用于设置启动配置文件中可以设置的所有配置参数。此外,可以仅通过使用以下命令来更改主节点的法定人数配置,而无需使用 SENTINEL REMOVE 删除并重新添加主节点:

SENTINEL SET objects-cache-master quorum 5

请注意,没有等效的 GET 命令,因为 SENTINEL MASTER 提供了所有配置参数,以易于解析的格式(作为字段/值对数组)。

增加或删除Sentinel

将新的 Sentinel 添加到您的部署中是一个简单的过程,因为 Sentinel 实现了自动发现机制。您只需启动新的 Sentinel,并配置它以监控当前活动的主节点。在 10 秒内,Sentinel 将获取其他 Sentinels 的列表以及附加到主节点的副本集。

如果您需要一次添加多个 Sentinels,建议逐个添加,每添加一个就等待所有其他 Sentinels 知道第一个 Sentinel 后再添加下一个。这是为了确保在添加新 Sentinels 的过程中,如果发生故障,仍能保证在分区的一侧达成多数。

这可以通过为每个新 Sentinel 添加 30 秒的延迟轻松实现,并且在没有网络分区的情况下进行。

在该过程结束时,可以使用命令 SENTINEL MASTER mastername 来检查所有 Sentinels 是否一致同意监控主节点的 Sentinel 总数。

移除一个 Sentinel 的过程则复杂一些:Sentinels 永远不会忘记已经看到的 Sentinels,即使它们长时间无法访问,因为我们不想动态改变授权故障转移所需的多数,并创建新的配置编号。因此,移除 Sentinel 的步骤如下,要求在没有网络分区的情况下执行:

  1. 停止您想要移除的 Sentinel 的进程。
  2. 向所有其他 Sentinel 实例发送 **SENTINEL RESET *** 命令(如果只想重置单个主节点,可以使用确切的主节点名称代替 *)。一个接一个地发送,每个实例之间至少等待 30 秒。
  3. 通过检查每个 Sentinel 的 SENTINEL MASTER mastername 输出,确保所有 Sentinels 对当前活动的 Sentinel 数量达成一致。

移除旧的主节点和无法到达的从节点

Sentinels 永远不会忘记给定主节点的副本,即使这些副本长时间无法访问。这是有用的,因为 Sentinels 应该能够在网络分区或故障事件后正确地重新配置返回的副本。

此外,在故障转移后,故障转移的主节点实际上会被添加为新主节点的副本,这样它将在再次可用时被重新配置为与新主节点进行复制。

然而,有时您可能希望永久地从 Sentinels 监控的副本列表中移除一个副本(这可能是旧的主节点)。

为此,您需要向所有 Sentinels 发送 SENTINEL RESET mastername 命令:它们将在接下来的 10 秒内刷新副本列表,只添加从当前主节点的 INFO 输出中列出的正确复制的副本。

发布/订阅消息

客户端可以将 Sentinel 作为一个与 Redis 兼容的 Pub/Sub 服务器(但不能使用 PUBLISH),以便订阅或模式订阅(PSUBSCRIBE)频道,并接收特定事件的通知。

频道名称与事件名称相同。例如,名为 +sdown 的频道将接收与实例进入 SDOWN(SDOWN 意味着该实例在您查询的 Sentinel 的视角中不再可达)状态相关的所有通知。

要获取所有消息,只需使用 PSUBSCRIBE * 进行订阅。

以下是您可以使用此 API 接收的频道和消息格式的列表。第一个单词是频道/事件名称,其余部分是数据格式。

注意:当指定实例详细信息时,意味着提供以下参数以识别目标实例:

<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>

识别主节点的部分(从 @ 参数到最后)是可选的,仅在实例本身不是主节点时指定。

  • +reset-master – 主节点已重置。
  • +slave – 检测到并附加了新的副本。
  • +failover-state-reconf-slaves – 故障转移状态已更改为重新配置副本状态。
  • +failover-detected – 检测到由其他 Sentinel 或任何外部实体启动的故障转移(附加副本转变为主节点)。
  • +slave-reconf-sent – 领导 Sentinel 向该实例发送了 REPLICAOF 命令,以将其重新配置为新的副本。
  • +slave-reconf-inprog – 正在重新配置的副本显示为新主节点的副本 ip:port 对,但同步过程尚未完成。
  • +slave-reconf-done – 副本现在已与新主节点同步。
  • -dup-sentinel – 为指定主节点删除了一个或多个重复的 Sentinel(例如,当 Sentinel 实例重启时会发生这种情况)。
  • +sentinel – 检测到并附加了该主节点的新 Sentinel。
  • +sdown – 指定实例现在处于主观下线状态。
  • -sdown – 指定实例不再处于主观下线状态。
  • +odown – 指定实例现在处于客观下线状态。
  • -odown – 指定实例不再处于客观下线状态。
  • +new-epoch – 当前纪元已更新。
  • +try-failover – 新的故障转移正在进行中,等待多数选举。
  • +elected-leader – 赢得指定纪元的选举,可以进行故障转移。
  • +failover-state-select-slave – 新的故障转移状态为选择副本:我们正在寻找合适的副本进行提升。
  • no-good-slave – 没有合适的副本可供提升。当前我们将稍后尝试,但可能会更改,并且状态机在这种情况下将完全中止故障转移。
  • selected-slave – 我们找到了指定的良好副本进行提升。
  • failover-state-send-slaveof-noone – 我们正在尝试将提升的副本重新配置为主节点,等待其切换。
  • failover-end-for-timeout – 故障转移因超时而终止,副本最终将被配置为与新主节点进行复制。
  • failover-end – 故障转移成功结束。所有副本似乎已重新配置为与新主节点进行复制。
  • switch-master – 主节点的新 IP 和地址是在配置更改后指定的。这是外部用户最感兴趣的消息。
  • +tilt – 进入倾斜模式。
  • -tilt – 退出倾斜模式。

Redis Replication

在 Redis 复制的基础上(不包括 Redis 集群或 Redis Sentinel 提供的高可用性功能),存在一种简单易用的主从(领导者-跟随者)复制机制。这种机制允许副本 Redis 实例成为主实例的精确副本。每当连接中断时,副本会自动重新连接到主节点,并会尝试成为主节点的精确副本,无论主节点发生什么情况。这个系统主要通过三个机制工作:

  1. 命令流复制:当主节点和副本实例连接良好时,主节点通过向副本发送命令流来保持副本更新,以复制主节点上的数据集的变化。这些变化可能是由于客户端写入、键过期、被驱逐或任何其他更改主节点数据集的操作引起的。
  2. 部分重同步:当主节点和副本之间的链接因网络问题或超时而断开时,副本会重新连接并尝试进行部分重同步。这意味着副本会试图仅获取在断开连接期间错过的命令流的一部分。
  3. 全重同步:当无法进行部分重同步时,副本会请求全重同步。这将涉及一个更复杂的过程,主节点需要创建其所有数据的快照,将其发送给副本,然后继续发送数据集更改时的命令流。

Redis 默认使用异步复制,具有低延迟和高性能的特性,这使得它成为绝大多数 Redis 用例的自然复制模式。然而,Redis 副本会定期与主节点异步确认接收到的数据量。因此,主节点在每次处理命令时并不等待副本的确认,但它会知道在需要时哪个副本已经处理了哪个命令。这使得可以选择性地实现同步复制。

客户端可以使用 WAIT 命令请求某些数据的同步复制。然而,WAIT 只能确保在其他 Redis 实例中存在指定数量的已确认副本,并不会将一组 Redis 实例转变为强一致性的 CP 系统:在故障转移期间,已确认的写入仍可能丢失,这取决于 Redis 持久性配置的具体情况。然而,WAIT 显著降低了在故障事件后丢失写入的可能性,尤其是在难以触发的故障模式下。

有关高可用性和故障转移的更多信息,请参阅 Redis Sentinel 或 Redis Cluster 文档。本文档的其余部分主要描述了 Redis 基本复制的基本特性。

注意:

  • Redis使用异步复制,副本与主节点之间以异步方式确认已处理的数据量。
  • 一个主节点可以有多个副本。
  • 副本可以接受来自其他副本的连接。除了将多个副本连接到同一主节点,副本也可以以级联结构连接到其他副本。从Redis 4.0开始,所有子副本将从主节点接收完全相同的复制流。
  • Redis复制在主节点上是非阻塞的。这意味着当一个或多个副本执行初始同步或部分重新同步时,主节点将继续处理查询。
  • 复制在副本端也大体上是非阻塞的。在副本执行初始同步时,假如您在redis.conf中进行了相应配置,它可以使用旧版本的数据集处理查询。否则,您可以将Redis副本配置为在复制流断开时向客户端返回错误。然而,在初始同步后,旧数据集必须被删除并加载新数据集。在此短暂窗口期间(对于非常大的数据集,这个时间可能会持续很多秒),副本将阻塞传入连接。自Redis 4.0起,您可以配置Redis使得旧数据集的删除在不同的线程中进行,但加载新的初始数据集仍将在主线程中进行,并且会阻塞副本。
  • 复制可以用于可扩展性,拥有多个副本以处理只读查询(例如,慢速O(N)操作可以转移到副本上),或者仅仅是为了提高数据安全性和高可用性。
  • 您可以利用复制避免主节点将完整数据集写入磁盘的成本:一种典型的技术是在主节点的redis.conf中配置不持久化到磁盘,然后连接一个配置为定期保存或启用AOF的副本。然而,这种设置必须谨慎处理,因为重启的主节点将从一个空数据集开始:如果副本尝试与其同步,副本也将被清空。

主服务器关闭持久化时复制的安全性

在使用Redis复制的环境中,强烈建议在主节点和副本上启用持久化。当由于慢磁盘等原因无法实现持久化时,应将实例配置为禁止自动重启。为了更好地理解为什么启用自动重启且未开启持久化的主节点是危险的,我们来看一个可能导致数据丢失的故障模式:假设有一个设置,其中节点A充当主节点,持久化关闭,并且节点B和C从节点A复制数据。

  • 节点A崩溃,但它有一个自动重启系统,可以重新启动进程。然而,由于持久化已关闭,节点将以空数据集重新启动。
  • 节点B和C将从空的节点A进行复制,从而有效地销毁它们的数据副本。

当使用Redis Sentinel实现高可用性时,在主节点上关闭持久化并自动重启进程是非常危险的。例如,主节点可能重启得足够快,以至于Sentinel没有检测到故障,从而导致上述故障模式发生。

Redis Replication是如何工作的

每个Redis主节点都有一个复制ID,这是一个大的伪随机字符串,用于标记数据集的特定历史记录。每个主节点还有一个偏移量(offset),该偏移量在发送给副本的复制流的每个字节时递增,以更新副本中修改数据集的新更改的状态。即使没有副本实际连接,复制偏移量也会递增。因此,每对:

复制 I D ,偏移量 复制ID,偏移量 复制ID,偏移量

都可以唯一标识主节点的确切数据集版本。当副本连接到主节点时,它们使用PSYNC命令发送其旧主节点的复制ID和迄今为止处理的偏移量。这样,主节点可以仅发送所需的增量部分。如果主节点的缓冲区没有足够的回溯数据,或者副本引用的历史(复制ID)不再被知晓,那么就会发生完全重新同步。在这种情况下,副本将从头开始获取完整的数据集副本。以下是完全同步的更详细的工作方式:

  1. 主节点启动一个后台保存进程以生成RDB文件。与此同时,它开始缓冲所有来自客户端的新的写命令。
  2. 当后台保存完成后,主节点将RDB文件传输给副本,副本将其保存在磁盘上,然后将其加载到内存中。
  3. 主节点随后会将所有缓存的新的写命令发送到副本。这是以命令流的形式进行的,格式与Redis协议本身相同。

复制ID

在之前的部分,我们提到如果两个实例具有相同的复制 ID 和复制偏移量,它们的数据是完全相同的。然而,了解复制 ID 的具体含义以及实例实际上具有两个复制 ID(主 ID 和次 ID)是很有用的。

  • 复制 ID 的定义

复制 ID 基本上标记了一组特定的数据集历史。每当实例从头开始重新启动为主节点,或副本被提升为主节点时,都会为该实例生成一个新的复制 ID。在连接到主节点的副本会在握手后继承其复制 ID。因此,两个实例具有相同的 ID 表明它们持有相同的数据,但可能在不同的时间。这时,复制偏移量则用于理解在给定历史(复制 ID)下,哪个实例拥有最新的数据集。

  • 为何 Redis 实例具有两个复制 ID

Redis 实例之所以有两个复制 ID,是因为副本被提升为主节点的情况。在故障转移后,提升的副本需要记住其过去的复制 ID,因为该复制 ID 是前主节点的复制 ID。这样,当其他副本与新主节点同步时,它们会尝试使用旧主节点的复制 ID 进行部分重新同步。这样的工作是可行的,因为当副本被提升为主节点时,它将其次 ID 设置为主 ID,记住在这个 ID 切换发生时的偏移量。随后,它会选择一个新的随机复制 ID,因为新的历史开始了。

在处理新副本连接时,主节点会将它们的 ID 和偏移量与当前 ID 和次 ID 进行匹配(在一定偏移量内,以确保安全)。简而言之,这意味着在故障转移后,连接到新提升的主节点的副本不必执行完全同步。

  • 为什么副本在故障转移后需要更改其复制 ID
    副本在故障转移后需要更改其复制 ID 的原因是,旧主节点可能由于某种网络分区仍在正常工作。如果保留相同的复制 ID,将违反任何两个随机实例具有相同 ID 和相同偏移量表示它们拥有相同数据集的事实。这种设计确保了数据一致性和系统的健壮性。

无盘复制

通常,完全重新同步需要在磁盘上创建一个 RDB 文件,然后从磁盘重新加载同一个 RDB 文件,以将数据传输到副本。在慢速磁盘上,这对主节点来说可能是一个非常有压力的操作。Redis 2.8.18 版本首次支持无盘复制(diskless replication)。在这种设置中,子进程直接通过网络将 RDB 文件发送给副本,而不使用磁盘作为中介存储。这种方法的优点包括:

  1. 降低延迟:由于不涉及磁盘读写,数据传输速度更快,可以减少数据同步的时间。
  2. 减轻负担:主节点在执行数据同步时不会受到慢速磁盘的影响,从而提高了整体性能和响应速度。
  3. 资源优化:可以在没有磁盘 IO 竞争的情况下更有效地使用系统资源,尤其是在高负载情况下。

这种无盘复制技术大大改善了 Redis 的性能,特别是在对磁盘 IO 速度要求较高的场景中。

配置

配置基本的 Redis 复制非常简单:只需在副本的配置文件中添加以下行:

replicaof 192.168.1.1 6379

当然,你需要将 192.168.1.16379 替换为你的主节点的 IP 地址(或主机名)和端口号。或者,你可以通过调用 REPLICAOF 命令来使主节点与副本开始同步。

要启用无盘复制,可以使用 repl-diskless-sync 配置参数。延迟开始传输以等待第一个副本到达后更多副本的时间由 repl-diskless-sync-delay 参数控制。

只读副本

自 Redis 2.6 以来,副本支持默认启用的只读模式。该行为由 redis.conf 文件中的 replica-read-only 选项控制,并且可以使用 CONFIG SET 在运行时启用或禁用。

只读副本将拒绝所有写命令,因此不可能因为错误而向副本写入数据。这并不意味着该特性旨在将副本实例暴露在互联网上或更一般的存在不可信客户端的网络中,因为像 DEBUGCONFIG 这样的管理命令仍然是启用的。

你可能会想知道为什么可以恢复只读设置,并使副本实例可以接受写操作。答案是,允许可写副本的存在仅仅是历史原因。使用可写副本可能导致主节点和副本之间的不一致,因此不建议使用可写副本。要理解在什么情况下这可能会导致问题,我们需要了解复制是如何工作的。主节点上的更改通过将常规 Redis 命令传播到副本来进行复制。当主节点上的键过期时,这将作为 DEL 命令传播。如果主节点上存在的键在副本上被删除、过期或与主节点的类型不同,那么对于从主节点传播过来的 DELINCRRPOP 等命令,副本将以不同的方式反应。传播的命令可能在副本上失败,或者产生不同的结果。为了最小化风险(如果你坚持使用可写副本),我们建议遵循以下建议:

  • 不要向可写副本中的键写入与主节点上也使用的键相同的值。(如果你无法控制所有向主节点写入的客户端,这可能很难保证。)

  • 不要将实例配置为可写副本作为在运行系统中升级一组实例的中间步骤。一般来说,如果希望保证数据一致性,请不要将实例配置为可写副本,如果它可能被提升为主节点。

Redis Replication如何处理键过期

Redis 允许键具有有限的生存时间(TTL),这是一个非常实用的功能。然而,这个功能依赖于实例能够准确计时。尽管如此,Redis 副本在正确复制具有过期时间的键时表现良好,即使这些键是通过 Lua 脚本进行更改的。

为了实现这一功能,Redis 并不依赖主节点和副本之间的时钟同步,因为这无法解决,并且会导致竞争条件和数据集分歧。因此,Redis 使用了三种主要技术,使过期键的复制能够正常工作:

  1. 主节点驱动的过期

副本不会主动过期键,而是等待主节点过期这些键。当主节点过期一个键(或因 LRU 被驱逐时),它会合成一个 DEL 命令并将其传输到所有副本。

但是,由于是主节点驱动的过期,有时副本可能仍会在内存中保留已在逻辑上过期的键,因为主节点未能及时发送 DEL 命令。为了处理这种情况,副本使用其逻辑时钟来报告在不违反数据集一致性的读取操作中键不存在(因为来自主节点的新命令将会到达)。通过这种方式,副本避免报告仍然存在的逻辑上已过期的键。在实际应用中,使用副本进行扩展的 HTML 片段缓存将避免返回已超出期望生存时间的项目。

  1. Lua 脚本执行时的时间冻结

在 Lua 脚本执行过程中,不会执行键的过期操作。当 Lua 脚本运行时,概念上主节点的时间是“冻结”的,因此给定键在脚本运行期间要么存在,要么不存在。这可以防止在脚本中途键过期,并确保在副本中发送相同的脚本时,保证对数据集产生相同的效果。

  1. 提升副本为主节点后独立过期

一旦副本被提升为主节点,它将开始独立地过期键,并且不需要旧主节点的任何帮助。这意味着副本在成为主节点后,可以自行管理键的生命周期,而不再依赖于之前的主节点来发送过期命令。

通过这些技术,Redis 能够有效地管理键的过期,同时确保副本和主节点之间的数据一致性。

INFO 和 ROLE 命令

Redis 提供了两个命令,可以获取有关主节点和副本实例当前复制参数的大量信息:

  • INFO:当使用 INFO replication 参数调用该命令时,仅显示与复制相关的信息。
  • ROLE:该命令以更易于计算机处理的格式提供主节点和副本的复制状态,包括它们的复制偏移量、连接的副本列表等信息。

重启和故障转移后的部分同步

自 Redis 4.0 起,当一个实例在故障转移后被提升为主节点时,它仍然可以与旧主节点的副本进行部分重新同步。为此,副本会记住其前主节点的旧复制 ID 和偏移量,因此即使连接的副本请求旧的复制 ID,它也可以提供部分的复制日志。

然而,被提升的副本的新复制 ID 将会不同,因为这代表了数据集的不同历史。例如,主节点可以返回可用状态,并在一段时间内继续接受写入,因此在提升的副本中使用相同的复制 ID 将违反一个规则,即复制 ID 和偏移量对只标识单个数据集。

此外,当副本被正常关闭并重新启动时,能够在 RDB 文件中存储所需的信息,以便与其主节点重新同步。这在升级时很有用。在需要时,最好使用 SHUTDOWN 命令以执行保存和退出操作。

值得注意的是,通过 AOF 文件重新启动的副本无法进行部分同步。然而,可以在关闭之前将实例切换为 RDB 持久化,然后重新启动,最后可以再次启用 AOF。

副本上的 maxmemory 设置

默认情况下,副本会忽略 maxmemory 设置(除非在故障转移后被提升为主节点或手动更改)。这意味着键的驱逐将由主节点处理,主节点在驱逐键时向副本发送 DEL 命令。

这种行为确保主节点和副本保持一致性,这通常是所希望的。然而,如果副本是可写的,或者你希望副本具有不同的内存设置,并且你确信对副本执行的所有写入都是幂等的,那么可以更改此默认设置(但要确保理解所做的更改)。

请注意,由于副本默认不进行驱逐,因此可能会使用超过 maxmemory 设置的内存(因为副本上的某些缓冲区可能更大,或者数据结构有时可能会占用更多内存等)。确保监控你的副本,并确保它们有足够的内存,以便在主节点达到配置的 maxmemory 设置之前,不会出现真实的内存不足情况。

要更改这种行为,可以允许副本不忽略 maxmemory。使用的配置指令如下:

replica-ignore-maxmemory no

Redis Cluster

Redis持久化

持久化指的是将数据写入耐用存储(如固态硬盘 SSD)的过程。Redis 提供了一系列持久化选项,具体如下:

  1. RDB(Redis 数据库)
    • RDB 持久化在指定的时间间隔内执行数据集的时间点快照。
    • 这种方式适合于在一定频率下保存数据,可以减少对性能的影响。
  2. AOF(追加文件)
    • AOF 持久化记录服务器接收到的每个写操作。
    • 这些操作可以在服务器启动时重放,以重建原始数据集。命令使用与 Redis 协议相同的格式进行记录。
    • AOF 提供了更细粒度的持久化方式,适用于需要更高数据可靠性的场景。
  3. 无持久化
    • 你可以完全禁用持久化。这种设置通常用于缓存场景,其中数据的持久性不重要。
  4. RDB + AOF
    • 你还可以在同一实例中结合使用 RDB 和 AOF。
    • 这种组合能够同时享受两种持久化方式的优势,在数据安全性和性能之间取得平衡。

RDB的优点和缺点

优点如下:

  1. 备份的理想选择
    • RDB 文件非常适合用于备份。例如,你可以每小时归档 RDB 文件,保存最近 24 小时的数据快照,并每天保存一个 RDB 快照,保留 30 天。这使你能够在灾难发生时轻松恢复数据集的不同版本。
  2. 灾难恢复
    • RDB 是灾难恢复的优秀选择,因为它是一个单一的紧凑文件,可以方便地传输到远程数据中心或上传到 Amazon S3(可以加密传输)。
  3. 性能优化
    • RDB 最大化了 Redis 的性能。为了持久化,Redis 主进程只需创建一个子进程来处理所有的 I/O 操作,主进程不会执行磁盘 I/O 操作。这意味着在持久化过程中不会影响主进程的性能。
  4. 快速重启
    • 在处理大数据集时,RDB 允许比 AOF 更快的重启。这是因为 RDB 文件是一种更紧凑的表示,加载速度更快。
  5. 支持部分重新同步
    • 在副本上,RDB 支持在重启和故障转移后进行部分重新同步。这使得在主从复制的环境中,系统恢复的效率更高。

缺点如下:

  1. 数据丢失风险
    • 如果 Redis 停止工作(例如由于断电),你可能会丢失最近的数据。虽然可以配置不同的保存点以生成 RDB 快照(例如,在至少五分钟和 100 次写入后生成快照),但通常情况下,你的 RDB 快照每五分钟生成一次或更久。因此,如果 Redis 没有正常关闭,你可能会失去最近几分钟的数据。
  2. 频繁的 fork() 调用
    • RDB 在持久化到磁盘时需要频繁调用 fork() 创建子进程。如果数据集很大,这个过程可能会耗时,导致 Redis 在执行 fork() 时可能会停止为客户端提供服务几毫秒甚至一秒钟,尤其是在 CPU 性能不佳的情况下。

AOF的优点和缺点

优点如下:

  1. 灵活的 fsync 策略
    • 使用 AOF 时,可以根据需要设置不同的 fsync 策略:
      • 不进行 fsync:最大化写入性能,但会增加数据丢失的风险。
      • 每秒 fsync:这是默认策略,性能良好,最多只会丢失一秒钟的数据。
      • 每次查询 fsync:提供最大的安全性,但可能会影响性能。
  2. 后台线程处理 fsync
    • AOF 的 fsync 操作是由后台线程处理的,主线程会尽量在没有 fsync 进行时进行写入操作。因此,在正常操作下,用户的写入性能不会受到太大影响。
  3. append-only 日志格式
    • AOF 使用追加日志格式,这意味着没有随机寻址(seek)操作,并且在电源故障时不会出现数据损坏的问题。即使日志以半写入的命令结束,Redis 提供的 redis-check-aof 工具可以轻松修复。
  4. 自动重写 AOF
    • Redis 能够在 AOF 文件过大时自动在后台重写 AOF。这一过程是安全的,Redis 在继续向旧文件追加数据的同时,生成一个包含当前数据集最小操作集的新文件。新文件准备好后,Redis 切换到新文件并开始向其追加。
  5. 可导出和恢复数据
    • AOF 包含所有操作的日志,格式易于理解和解析。如果你意外使用 FLUSHALL 命令清空了所有数据,只要没有进行重写,就可以通过停止服务器、删除最后一条命令并重新启动 Redis 来恢复数据集。

缺点如下:

  1. 文件大小
    • AOF 文件通常较大:相同数据集的 AOF 文件通常会比 RDB 文件更大。这是因为 AOF 记录了每一个写入操作,而 RDB 则是一个点时间快照。
  2. 性能
    • AOF 性能受 fsync 策略影响
      • 每秒 fsync:在默认配置下,AOF 的性能仍然很高,通常能满足大多数场景的需求。
      • 禁用 fsync:在这种情况下,AOF 的性能应该与 RDB 相当,即使在高负载情况下也能保持快速。

如何选择

选择合适的持久化策略对于确保数据的安全性和可用性至关重要。以下是对两种持久化方法(RDB 和 AOF)使用建议的总结:

  1. 同时使用 RDB 和 AOF
    • 如果您希望获得与 PostgreSQL 类似的数据安全性,那么同时使用这两种持久化方法是一个不错的选择。
    • 结合 RDB 的快照和 AOF 的日志,可以在系统崩溃后快速恢复数据,并最大限度地减少数据丢失的风险。
  2. 单独使用 RDB
    • 如果您对数据安全性非常重视,但可以接受在灾难情况下损失几分钟的数据,那么仅使用 RDB 是合适的。
    • RDB 的快照功能能够满足大多数备份需求,并在系统恢复时提供更快的启动时间。
  3. 单独使用 AOF
    • 虽然有许多用户选择仅使用 AOF,但通常不建议这样做。因为 AOF 在遇到问题时可能导致数据丢失,并且在 AOF 引擎出现错误时可能无法快速恢复。
    • 定期生成 RDB 快照可以作为有效的备份策略,确保在 AOF 出现故障时能够恢复数据。

快照

Redis 的快照持久化功能允许您将数据集的状态定期保存到磁盘上,以便在需要时进行恢复。快照文件通常称为 dump.rdb

配置保存

您可以通过 redis.conf 文件或使用 CONFIG SET 命令来设置快照保存策略。快照保存策略的基本语法如下:

save <seconds> <changes>
  • <seconds>:在指定的时间段内(以秒为单位)进行快照。
  • <changes>:在此时间段内发生的最小键更改数量。

以下是一个示例配置,使 Redis 每隔 60 秒自动保存一次数据集,如果在此期间至少有 1000 个键发生变化:

save 60 1000

这意味着如果在 60 秒内有至少 1000 个键被更改,Redis 将创建一个新的快照并将其保存到 dump.rdb 文件中。

手动保存

除了自动快照,您还可以手动触发快照保存。可以使用以下命令之一:

  • SAVE:该命令会阻塞 Redis,直到快照完成。
  • BGSAVE:该命令会在后台异步保存快照,不会阻塞 Redis 处理其他客户端请求。

只追加文件

Redis 的附加文件(AOF)持久化是一种确保数据持久性的有效策略,特别是在需要避免数据丢失的应用场景中。AOF 在 Redis 1.1 版本引入,提供了在发生意外停机(如系统崩溃或断电)时的更高数据安全性。要启用 AOF,可以在 Redis 的配置文件中设置:

appendonly yes

一旦启用,Redis 将在接收到更改数据集的命令(例如 SET)时,将该命令附加到 AOF 文件中。

AOF 文件的工作原理

  • 重放机制:当 Redis 重启时,它会重新播放 AOF 文件中的所有命令,以重建数据集的状态。这确保了即使在意外情况下,数据也能得到恢复。
  • 多文件机制(Redis 7.0.0 及更高版本)
    • Redis 7.0.0 引入了多文件 AOF 机制,将原始的单个 AOF 文件分割为基础文件和增量文件。
    • 基础文件:最多只有一个,代表在 AOF 重写时的数据快照(可以是 RDB 或 AOF 格式)。
    • 增量文件:可以有多个,包含自上一个基础 AOF 文件创建以来的增量更改。
    • 所有这些文件都会存储在一个单独的目录中,并由一个清单文件进行跟踪。

AOF 日志重写

随着写操作的进行,AOF 文件会不断增大。例如,如果你对一个计数器进行了 100 次递增操作,数据集中只会有一个键(包含最终值),但在 AOF 文件中将会有 100 条记录,其中 99 条记录对于重建当前状态是多余的。

安全的重写机制

Redis 支持一个有趣的功能:它可以在后台重建 AOF 文件,而不会中断对客户端的服务。具体过程如下:

  1. 最小命令集重写:当你发出 BGREWRITEAOF 命令时,Redis 会写入重建当前数据集所需的最短命令序列。
  2. 后台执行:Redis 在后台创建一个新 AOF 文件,记录当前内存中的数据集状态。旧文件继续接受新写入操作。
  3. 原子切换:一旦新的 AOF 文件准备就绪,Redis 会执行一个原子替换操作,将新文件与旧文件交换,并开始将后续的写入操作追加到新文件中。
自动重写(Redis 2.4 及更高版本)

自 Redis 2.4 版本起,AOF 重写的触发可以自动进行。在配置文件中可以设置重写条件,Redis 会根据这些条件自动调用 BGREWRITEAOF 来优化 AOF 文件。

Redis 7.0.0 的改进

在 Redis 7.0.0 中,当调度 AOF 重写时,Redis 父进程会打开一个新的增量 AOF 文件继续写入。子进程则执行重写逻辑,生成新的基础 AOF 文件。同时,Redis 会使用一个临时清单文件来跟踪新生成的基础文件和增量文件。

  • 原子替换:一旦新基础文件和增量文件准备完毕,Redis 将执行一个原子替换操作,使临时清单文件生效。
  • 重写限制机制:为避免因 AOF 重写的重复失败和重试而产生过多的增量文件,Redis 引入了 AOF 重写限制机制,确保失败的 AOF 重写会以逐渐减慢的速率重试。

AOF的持久性

**AOF(Append Only File)**提供了多种持久性选项,以控制数据在磁盘上的同步(fsync)频率。可以根据需求选择不同的持久性策略,具体如下:

  1. fsync 策略选项
  • appendfsync always
    • 描述:每次将新命令追加到 AOF 时都调用 fsync。
    • 性能:非常慢,但安全性极高。
    • 注意:命令在执行完来自多个客户端的批量命令或管道后,才会被追加到 AOF 中,因此每次写入后都会有一次 fsync。
  • appendfsync everysec
    • 描述:每秒调用一次 fsync。
    • 性能:相对较快(自 2.4 版本起,这个选项的性能与 RDB 快照相当)。
    • 风险:在发生灾难时,可能会丢失最近一秒的数据。
  • appendfsync no
    • 描述:从不调用 fsync,只将数据交给操作系统。
    • 性能:更快,但安全性较低。
    • 注意:在这种配置下,Linux 通常会每 30 秒将数据刷新到磁盘,但具体行为取决于内核的调优设置。
  1. 推荐配置
  • 默认策略:建议使用 appendfsync everysec,这既能提供合理的性能,又能在一定程度上保证数据安全。虽然 always 策略提供了最大的安全性,但在实际应用中其性能往往无法满足需求。
  • 批量提交appendfsync always 策略支持组提交(group commit),对于多个并行写入操作,Redis 会尝试合并为一次 fsync 操作,从而提升性能。

处理 AOF 文件截断

当 AOF 文件被截断时,可能是由于服务器在写入 AOF 文件时崩溃,或者存储 AOF 文件的卷已满。在这种情况下,AOF 仍然包含一致的数据,代表数据集的某个时刻版本(在默认的 AOF fsync 策略下,可能最多为一秒钟),但 AOF 中的最后一条命令可能已被截断。最新版本的 Redis 仍然能够加载 AOF,只需丢弃文件中最后一个格式不正确的命令。此时,服务器会发出类似以下的日志:

* Reading RDB preamble from AOF file...
* Reading the remaining AOF tail...
# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 439 !!!
# AOF loaded anyway because aof-load-truncated is enabled

如果您希望 Redis 在这种情况下停止,可以更改默认配置;但是,默认配置是无论文件最后一条命令是否格式正确都继续运行,以保证在重启后能够恢复可用性。

对于旧版本的 Redis,可能无法恢复,需要采取以下步骤:

  1. 备份 AOF 文件
    • 在进行任何修复操作之前,请确保备份原始 AOF 文件。
  2. 使用 redis-check-aof 工具修复文件
    • 运行以下命令修复原始文件:
      $ redis-check-aof --fix <filename>
      
    • 可选地,使用 diff -u 检查修复前后两个文件的差异。
  3. 用修复后的文件重新启动服务器

处理 AOF 文件损坏

如果 AOF 文件不仅被截断,而且在中间出现了无效字节序列,情况会更复杂。Redis 在启动时会报错并中止:

* Reading the remaining AOF tail...
# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>

处理步骤:

  1. 备份 AOF 文件
    • 同样,首先备份损坏的 AOF 文件。
  2. 运行 redis-check-aof 工具
    • 初始时,不带 --fix 选项运行工具,以了解问题:
      $ redis-check-aof <filename>
      
    • 检查工具给出的错误提示,跳转到文件中的指定偏移量,查看是否可以手动修复文件。AOF 使用与 Redis 协议相同的格式,手动修复相对简单。
  3. 自动修复(如果手动修复不可行)
    • 如果无法手动修复,您可以让工具为您修复文件,但在这种情况下,从无效部分到文件末尾的所有 AOF 部分可能会被丢弃,如果损坏发生在文件的初始部分,可能会导致大量数据丢失。

AOF是如何工作的

日志重写(Log rewriting)使用了与快照相同的写时复制(copy-on-write)技巧,具体工作原理如下:

  • Redis 7.0 及以上版本
  1. Redis 进行 fork 操作,此时有一个子进程和一个父进程。
  2. 子进程开始将新的 base AOF 写入临时文件,这个文件代表重写后的基础 AOF 文件。
  3. 父进程打开一个新的增量 AOF 文件,用于继续记录后续的更新。如果重写失败,旧的 base 文件和增量文件(如果有的话)加上这个新打开的增量文件,仍然能够完整地表示更新后的数据集,因此不会有数据丢失。
  4. 当子进程完成 base 文件的重写时,父进程收到信号,并使用新打开的增量文件和子进程生成的 base 文件构建一个临时的清单(manifest 文件),并将其持久化。
  5. 交换清单文件:Redis 原子性地交换旧的清单文件和新的清单文件,这样日志重写的结果就生效了。之后 Redis 清理旧的 base 文件和未使用的增量文件。

Redis 7.0 以下版本

  1. Redis 进行 fork 操作,同样有一个子进程和一个父进程。
  2. 子进程开始将新的 AOF 写入临时文件,这个临时文件存放的是重写后的所有操作。
  3. 父进程将所有新的更改累积到内存缓冲区中,同时将这些更改写入旧的 append-only 文件中。这样即使重写失败,依然是安全的。
  4. 当子进程完成文件重写后,父进程收到信号,将内存缓冲区的内容追加到子进程生成的新文件的末尾。
  5. 原子性地重命名文件:Redis 将新生成的文件原子性地重命名为旧文件,接着开始将新的数据追加到新文件中。

无论是 Redis 7.0 及以上版本,还是更早的版本,Redis 都能确保即使在日志重写期间出现故障,仍然可以保证数据的安全性和一致性。

如果我目前正在使用RDB,我如何切换到AOF?

如果你想在当前使用 RDB 快照的服务器上启用 AOF(追加日志文件),你需要通过 CONFIG 命令在运行的服务器上先启用 AOF 来进行数据转换。

  • Redis 2.2 及以上版本

准备工作

  1. 备份最新的 dump.rdb 文件。
  2. 将此备份文件转移到安全的地方。

在活跃的数据库上切换到 AOF

  1. 启用 AOF:redis-cli config set appendonly yes
  2. 可选操作:禁用 RDB:redis-cli config set save ""
  3. 确保写操作正确地被追加到 AOF 文件中。
  4. 重要提示:更新 redis.conf 文件(可以通过 CONFIG REWRITE 命令更新),确保配置文件与上述配置匹配。如果你忽略这一步,当你重启服务器时,配置更改将丢失,服务器会以旧配置重新启动,导致数据丢失。

下一次重启服务器时

  1. 在重启服务器之前,等待 AOF 重写完成并持久化数据。你可以通过 INFO persistence 来监控,确保 aof_rewrite_in_progressaof_rewrite_scheduled 都为 0,同时确认 aof_last_bgrewrite_statusok
  2. 重启服务器后,检查数据库中包含的键数量是否与之前相同。
  • Redis 2.0

步骤

  1. 备份最新的 dump.rdb 文件。
  2. 将此备份文件转移到安全的地方。
  3. 停止所有对数据库的写操作!
  4. 执行 redis-cli BGREWRITEAOF,这将生成 AOF 文件。
  5. 在 Redis 完成 AOF 文件生成后,停止服务器。
  6. 编辑 redis.conf,启用 AOF 持久化。
  7. 重启服务器。
  8. 确保你的数据库中包含的键数量与切换前相同。
  9. 确保写操作正确地被追加到 AOF 文件中。

AOF和RDB持久性之间的交互

Redis 2.4 及以上版本确保在 RDB 快照操作正在进行时不会触发 AOF 重写操作,或在 AOF 重写过程中允许执行 BGSAVE。这避免了两个 Redis 后台进程同时进行大量的磁盘 I/O 操作。

当快照正在进行时,如果用户明确请求通过 BGREWRITEAOF 执行日志重写操作,服务器会返回 OK 状态码告知用户该操作已被安排,重写操作会在快照完成后开始。

如果同时启用了 AOF 和 RDB 持久化,在 Redis 重启时会使用 AOF 文件来重建原始数据集,因为 AOF 文件被保证是最完整的数据记录。

RDB持久化备份

Redis 非常适合数据备份,因为您可以在数据库运行时复制 RDB 文件:RDB 文件一旦生成就不会再被修改,并且在生成过程中会使用临时文件名,只有在新快照完全生成后才会通过 rename(2) 原子性地重命名到最终位置。

这意味着在服务器运行时复制 RDB 文件是完全安全的。我们建议的做法是:

  1. 在服务器中创建一个 cron 任务,每小时生成一个 RDB 文件的快照并存储在一个目录中,每天生成一个快照并存储在另一个目录中。
  2. 每次 cron 脚本运行时,确保使用 find 命令删除过旧的快照。例如,您可以保存过去 48 小时的每小时快照,以及一个或两个月的每日快照。确保为快照命名时包含日期和时间信息。
  3. 每天至少一次,确保将 RDB 快照转移到数据中心之外,或者至少将其转移到不在运行 Redis 实例的物理机器上。

AOF持久化备份

如果您运行的是仅启用了 AOF 持久化的 Redis 实例,仍然可以执行备份。从 Redis 7.0.0 开始,AOF 文件被分割成多个文件,这些文件存储在由 appenddirname 配置指定的目录中。在正常操作中,您只需复制或打包该目录中的文件即可完成备份。然而,如果备份期间正好发生了 AOF 重写操作,可能会导致备份无效。为了解决此问题,您必须在备份期间禁用 AOF 重写操作:

  1. 使用以下命令关闭自动重写:
   CONFIG SET auto-aof-rewrite-percentage 0
  1. 确保在此期间不要手动启动重写(例如通过 BGREWRITEAOF 命令)。
  2. 使用以下命令检查当前是否有正在进行的重写操作:
  INFO persistence

确认 aof_rewrite_in_progress 值为 0。如果为 1,则需要等待重写完成。
4. 现在您可以安全地复制 appenddirname 目录中的文件。
5. 完成备份后重新启用自动重写:

   CONFIG SET auto-aof-rewrite-percentage <之前的值>

优化

故障排查

调试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1556052.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

redis——哨兵机制

redis中提供了哨兵&#xff08;Sentinel&#xff09;机制来实现主从集群的自动故障恢复。 主从复制是实现redis高可用性的基石&#xff0c;从节点宕机时我们仍然可以将请求发送给主节点或者其他从节点&#xff0c;而当主节点宕机的时候&#xff0c;无法执行写操作&#xff0c;无…

百元头戴式耳机都有哪些值得入手?四款爆款高性价比机型推荐

在追求性价比的时代&#xff0c;选择一款既实惠又高品质的头戴式降噪耳机&#xff0c;成为了许多人的明智之举。它不仅能够为您带来出色的音质和降噪效果&#xff0c;还能让您在享受音乐的同时&#xff0c;节省不必要的开支。那百元头戴式耳机都有哪些值得入手&#xff1f;让我…

Mysql备份与恢复——日志

Mysql日志 Buffer Pool Innodb 存储引擎设计了一个缓冲池&#xff08;Buffer Pool&#xff09;&#xff0c;来提高数据库的读写性能。 Mysql中比较重要的日志包括&#xff1a;二进制日志 binlog&#xff08;归档日志&#xff09;和 redo log&#xff08;重做日志&#xff09;…

【买瓜 / F】

题目 思路 折半搜索 注意要从小到大排序&#xff08;虽然我也不知道为什么&#xff09; 代码 #include <bits/stdc.h> using namespace std; typedef long long ll; int n, m, t; int a[35]; unordered_map<ll, int> h; int ans INT_MAX; void dfs1(int k, int…

系统架构设计师-论文题(2021年下半年)

1.试题一 论面向方面的编程技术及其应用针对应用开发所面临的规模不断扩大、复杂度不断提升的问题&#xff0c;面向方面的编程Aspect Oriented Programming,AOP技术提供了一种有效的程序开发方法。为了理解和完成一个复杂的程序&#xff0c;通常要把程序进行功能划分和封装。一…

54.二叉树的最大深度

迭代 class Solution {public int maxDepth(TreeNode root) {if(rootnull){return 0;}int de0;Queue<TreeNode> qunew LinkedList<>();TreeNode tn;int le;qu.offer(root);while(!qu.isEmpty()){lequ.size();while(le>0){tnqu.poll();if(tn.left!null){qu.offe…

【Linux】Ubuntu20.04上使用RabbitVCS的图形化SVN

文章目录 1、RabbitVCS1.1、RabbitVCS 介绍1.2、RabbitVCS 主要功能1.3、Ubuntu下 TortoiseSVN 替代者 2、安装2.1、命令安装2.2、安装使用2.3、使用权限 3、解决SVN无法保存密码问题3.1、问题描述3.2、解决方法 1、RabbitVCS 1.1、RabbitVCS 介绍 它是一款Linux系统下的图形…

Excel中的屠龙大招

indirect的地位部分动摇&#xff0c;神坛下已初生大力骑士——“”。 (笔记模板由python脚本于2024年10月06日 18:57:11创建&#xff0c;本篇笔记适合同时喜欢python和Excel的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&…

李宏毅深度学习-自注意力机制

输入是向量序列的情况 在图像识别的时候&#xff0c;假设输入的图像大小都是一样的。但如果问题变得复杂&#xff0c;如图6.2所示&#xff0c;输入是一组向量&#xff0c;并且输入的向量的数量是会改变的&#xff0c;即每次模型输入的序列长度都不一样&#xff0c;这个时候应该…

DBMS-3.2 SQL(2)——DML的SELECT(含WHERE、聚集函数、GROUP BY、HAVING之间的关系)

本文章的素材与知识来自李国良老师和王珊老师。 数据操纵语言DML&#xff08;Data Manipulation Language&#xff09; SELECT 一.SELECT的语法与构成 1.语法 2.构成 二.投影 投影操作可以选择表中的若干列&#xff0c;主要体现在SELECT子句后的列表达式。 1.列表达式 2.…

鸿蒙开发(NEXT/API 12)【穿戴设备模板化通知】手机侧应用开发

手机侧应用向穿戴设备发送通知&#xff0c;并在穿戴设备上按模板显示&#xff0c;支持穿戴设备收到通知后同步振动或响铃&#xff08;跟随穿戴设备系统设置&#xff09;。执行成功后&#xff0c;穿戴设备上会显示下图所示通知界面。 该接口无需用户授权&#xff0c;仅需要确保…

视频转文字免费的软件有哪些?6款工具一键把视频转成文字!又快又方便!

视频转文字免费的软件有哪些&#xff1f;在视频制作剪辑过程中&#xff0c;我们经常进行视频语音识别成字幕&#xff0c;帮助我们更好地呈现视频内容的观看和宣传&#xff0c;市场上有许多免费的视频转文字软件&#xff0c;可以快速导入视频&#xff0c;进行视频内音频的文字转…

Vueron引领未来出行:2026年ADAS激光雷达解决方案上市路线图深度剖析

Vueron ADAS激光雷达解决方案路线图分析&#xff1a;2026年上市展望 Vueron近期发布的ADAS激光雷达解决方案路线图&#xff0c;标志着该公司在自动驾驶技术领域迈出了重要一步。该路线图以2026年上市为目标&#xff0c;彰显了Vueron对未来市场趋势的精准把握和对技术创新的坚定…

【Mybatis篇】Mybatis的注解开发

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】&#xff0c;【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 文章目录 &#x1f3af; Select注解 …

Gridview配置数据源--信任服务器证书

目录 背景过程Gridview配置数据源GridView与数据源&#xff1a;数据库连接与安全&#xff1a;信任服务器证书&#xff1a;配置信任服务器证书&#xff1a;注意事项&#xff1a; 生成连接字符串程序运行报错问题解决 总结 背景 Gridview配置数据源之后&#xff0c;程序报错 过…

一篇文章教会你DHT11读取温湿度,附STM32代码示例

目录 一、DHT11说明&#xff1a; 1.典型电路&#xff1a; 2.串行通信说明&#xff08;单线双向&#xff09;&#xff1a; 单总线说明&#xff1a; 单总线传送数据位定义&#xff1a; 校验位数据定义&#xff1a; 二、DHT11读取时为啥要切换模式&#xff1a; 1. 通信时序…

【Linux】进程第三弹(虚拟地址空间)

目录 现象 底层原因 数据不发生修改 数据修改 小总结 地址空间本质 为什么要有地址空间 现象 来看代码&#xff1a; #include <stdio.h> #include <unistd.h> #include <sys/types.h>int val 50;int main() {printf("father process is running…

【springboot】简易模块化开发项目整合Redis

接上一项目&#xff0c;继续拓展项目 1.整合Redis 添加Redis依赖至fast-demo-config模块的pom.xml文件中 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependenc…

九、3 串口发送+printf函数移植+打印汉字

1、接线图 TX与RX交叉连接&#xff0c;TXD接STM32的PA10&#xff0c;RXD接STM32的PA9 VCC与3.3V用跳线帽连接 2、函数介绍 3、代码部分 &#xff08;1&#xff09;发送字节的函数&#xff08;Byte&#xff09; 可直接发送十六进制数 如0x41&#xff0c;也可直接发送字符 如A …