内存数据库之redis(补充)

355 阅读25分钟

五种数据类型使用场景

string类型

Redis提供了原生命令

**典型示例1:**商品库存数,商品的浏览次数,问题或者回复的点赞次数等

incr key 
decr key 
incrby key increment 
decrby key decrement

**典型示例2:**时效信息存储

Redis的数据存储具有自动失效能力,存储的key-value可以设置过期时间。

例如,用户登录某个App需要获取登录验证码,验证码在30秒内有效。就可以使用string类型存储验证 码,同时设置30秒的失效时间。

keys = redisCli.get(key);
if(keys != null)
  {
     return false;
  }
else
  {
     sendMsg();
     redisCli.set(keys, value, expireTime);
  } 

list类型

list是按照插入顺序排序的字符串链表,可以在头部和尾部插入新的元素

消息队列实现(一般不使用)主流:Kafka、RocketMQ、RabbitMQ

典型示例1:最新上架商品

展示最新上架前100名,进行TOP产品的存储。

// 把新上架商品添加到链表里
ret = r.lpush("new:goods", goodsId)
// 保持链表100位
ret = r.ltrim("new:goods", 0, 99)
// 获得前100个最新上架的商品id列表
newest_goods_list = r.lrange("new:goods", 0, 99)

set类型

set存储了一个无序集合,具备去重功能。 当需要存储一个列表信息,同时要求列表内的元素不能有重复,用set比较合适。与此同时,set还提供 求交集、并集、差集。 

获取到两个用户相似的产品,然后确定相似产品的类目就可以进行用户分析。类似的应用场景还有,社交场景下共同关注好友,相似兴趣等来做人物画像。

// userid为用户ID,goodID为感兴趣的商品信息
// sadd "user:userId" goodID
sadd "user:1001" 1
sadd "user:1001" 2
sadd "user:1002" 1
sadd "user:1002" 3
sinter "user:1001" "user:1002"
// 结果为1 

hash类型

Redis在存储对象(例如,用户信息)的时候需要对对象进行序列化转换然后存储。 

{
"name": "isisiwish",
"phone": "1008611",
"sex": "男"
} 

这种类似场景还很多,例如订单数据,产品数据,商家基本信息等。 

Redis sorted set的使用场景与set类似,区别是set无序的,而sorted set可以通过提供一个score参数来 为存储数据排序,并且是自动排序,插入既有序。业务中如果需要一个有序且不重复的集合列表,就可 以选择sorted set数据结构。

SDS 

Redis中字符串的实现。在3.2以后的版本中,SDS又有多种结构(sds.h):

  • sdshdr5
  • sdshdr8
  • sdshdr16
  • sdshdr32
  • sdshdr64

用于存储不同的长度的字符串,分别代表: 

2^5=32byte 2^8=256byte 2^16=65536byte=64KB 2^32byte=4GB

Redis使用SDS实现字符串的原因:

C语言本身没有字符串类型(只能用字符数组char[]实现)。 

  1. 使用字符数组必须先给目标变量分配足够的空间,否则可能会溢出。 
  2. 如果要获取字符长度,必须遍历字符数组,时间复杂度是O(n)。 
  3. C字符串长度的变更会对字符数组做内存重分配。 
  4. 通过从字符串开始到结尾碰到的第一个'\0'来标记字符串的结束,因此不能保存图片、音频、视频、 压缩文件等二进制(bytes)保存的内容,二进制不安全。

SDS的特点

  1. 不用担心内存溢出问题,如果需要会对SDS进行扩容。 
  2. 获取字符串长度时间复杂度为O(1),因为定义了len属性。 
  3. 通过“空间预分配”(sdsMakeRoomFor)和“惰性空间释放”,防止多次重分配内存。 
  4. 判断是否结束的标志是len属性(它同样以'\0'结尾是因为这样就可以使用C语言中函数库操作字符串 的函数了),可以包含'\0'。  

持久化机制

持久化概述

Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘。当下次Redis重启时,利用持久化文件实现数据恢复。

Redis持久化分为RDB持久化AOF持久化,前者将当前数据保存到硬盘,后者则是将每次执行的写命令保存到硬盘(类似于MySQL的Binlog)。由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式

RDB持久化

RDB持久化执行流程:

将Reids在内存中的数据定时dump到磁盘上的rdb文件,在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

RDB优点

  • RDB文件紧凑,体积小,网络传输快,适合全量复制
  • 恢复速度比AOF快很多,对性能的影响相对较小

RDB缺点

  • 数据快照的持久化方式做不到实时持久化

AOF持久化

AOF持久化是将Redis执行的每次写命令记录到单独的日志文件中(有点像MySQL的binlog);当Redis重启时再次执行AOF文件中的命令来恢复数据。与RDB相比,AOF的实时性更好,因此已成为主流的持久化方案,

AOF执行流程:

命令追加(append):

将Redis的写命令追加到缓冲区aof_buf,再将命令写入文件,避免每次有写命令都直接写入硬盘,导致硬盘IO成为Redis负载的瓶颈。

AOF缓存区的同步文件策略由参数appendfsync控制:

  • always:命令写入aof_buf后立即调用系统fsync操作同步到AOF文件,fsync完成后线程返回。1、硬盘IO成为性能瓶颈;2、使用固态硬盘会大大降低寿命。
  • no:命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步;同步由操作系统负责,通常同步周期为30秒。数据会很多,数据安全性无法保证。
  • everysec:命令写入aof_buf后调用系统write操作,write完成后线程返回;fsync同步文件操作由专门的线程每秒调用一次。默认配置且为推荐配置。

AOF优点

  • 支持秒级持久化、兼容性好
  • 更好地保护数据不丢失 appen-only模式写入性能比较高 适合做灾难性的误删除紧急恢复

AOF缺点

  • 对于同一份文件,AOF文件要比RDB快照大 AOF开启后,写的QPS会有所影响,相对于RDB来说写QPS要下降 数据库恢复比较慢,不合适做冷备

AOF常用配置总结:

  • appendonly no:是否开启AOF 
  • appendfilename "appendonly.aof":AOF文件名 
  • dir ./:RDB文件和AOF文件所在目录 
  • appendfsync everysec:fsync持久化策略 
  • no-appendfsync-on-rewrite no:AOF重写期间是否禁止fsync;如果开启该选项,可以减轻文件重写时CPU和硬盘的负载(尤其是硬盘),但是可能会丢失AOF重写期间的数据;需要在负载和安全性之间进行平衡 
  • auto-aof-rewrite-percentage 100:文件重写触发条件之一 
  • auto-aof-rewrite-min-size 64mb:文件重写触发提交之一 
  • aof-load-truncated yes:如果AOF文件结尾损坏,Redis启动时是否仍载入AOF文件

策略选择

实际生产环境中,根据数据量、应用对数据的安全要求、预算限制等不同情况,会有各种各样的持久化策略;如完全不使用任何持久化、使用RDB或AOF的一种,或同时开启RDB和AOF持久化等。此外,持久化的选择必须与Redis的主从策略一起考虑,因为主从复制与持久化同样具有数据备份的功能,而且主机master和从机slave可以独立的选择持久化方案。

主从模式

概述

主从复制是指将一台Redis服务器的数据,复制到其它的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。且为1:n的关系。

作用

  • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。 
  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复,但实际上是一种服务的冗余。 
  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。 
  • 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

主从复制的实现原理

一、连接建立阶段

  • 步骤1:从节点保存主节点信息,从节点服务器内部维护了两个字段,即masterhost和masterport字段,用于存储主节点的ip和port信息。
  • 步骤2:建立socket连接,从节点每秒1次调用复制定时函数replicationCron(),如果发现了有主节点可以连接,便会根据主节点的ip和port,创建socket连接。
  • 步骤3:发送ping命令,从节点成为主节点的客户端之后,发送ping命令进行首次请求,目的是检查socket连接是否可用以及主节点当前是否能够处理请求。
  • 步骤4:身份验证。从节点进行身份验证是通过向主节点发送auth命令进行的,auth命令的参数即为配置文件中的masterauth的值。 如果主节点设置密码的状态,与从节点masterauth的状态一致(一致是指都存在,且密码相同,或者都不存在),则身份验证通过,复制过程继续;如果不一致,则从节点断开socket连接,并重连。
  • 步骤5:发送从节点端口信息。身份验证后,从节点会向主节点发送其监听的端口号(前述例子中为6380),主节点将该信息保存到该从节点对应的客户端的slave_listening_port字段中;该端口信息除了在主节点中执行info Replication时显示以外,没有其他作用。

二、数据同步阶段

数据同步阶段是主从复制最核心的阶段,根据主从节点当前状态的不同,可以分为全量复制部分复制

全量复制过程

  1. 从节点判断无法进行部分复制,向主节点发送全量复制的请求;或从节点发送部分复制的请求,但主节点判断无法进行全量复制。 
  2. 主节点收到全量复制的命令后,执行bgsave,在后台生成RDB文件,并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有写命令。 
  3. 主节点的bgsave执行完成后,将RDB文件发送给从节点;从节点首先清除自己的旧数据,然后载入接收的RDB文件,将数据库状态更新至主节点执行bgsave时的数据库状态。 
  4. 主节点将前述复制缓冲区中的所有写命令发送给从节点,从节点执行这些写命令,将数据库状态更新至主节点的最新状态。 
  5. 如果从节点开启了AOF,则会触发bgrewriteaof的执行,从而保证AOF文件更新至主节点的最新状态。

全量复制的影响

  • 主节点通过bgsave命令fork子进程进行RDB持久化,该过程是非常消耗CPU、内存(页表复制)、硬盘IO的。
  • 主节点通过网络将RDB文件发送给从节点,对主从节点的带宽都会带来很大消耗。
  • 从节点清空老数据、载入新RDB文件的过程是阻塞的,无法响应客户端的命令。如果从节点执行bgrewriteaof,也会带来额外的消耗。

部分复制过程

  1. 主节点和从节点分别维护一个复制偏移量(offset),代表的是主节点向从节点传递的字节数;主节点每次向从节点传播N个字节数据时,主节点的offset增加N;从节点每次收到主节点传来的N个字节数据时,从节点的offset增加N。 
  2. 复制积压缓冲区是由主节点维护的、固定长度的、先进先出(FIFO)队列,默认大小1MB;当主节点开始有从节点时创建,其作用是备份主节点最近发送给从节点的数据。 
  3. 主从节点初次复制时,主节点将自己的runid(redis-cli info server |grep run_id)发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid判断能否进行部分复制: 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况); 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制。

psync

psync命令执行来判断主从节点是如何确定使用全量复制还是部分复制的

  1. 从节点根据当前状态,决定如何调用psync命令
  2. 主节点根据收到的psync命令及当前服务器状态来决定执行全量复制还是部分复制

主从模式优点

  • Master/Slave 角色方便水平扩展,QPS 增加,增加 Slave 即可; 
  • 降低 Master 读压力,转交给 Slave 节点; 
  • 主节点宕机,从节点作为主节点的备份可以随时顶上继续提供服务。

主从模式缺点

  • 可靠性保证不是很好,主节点故障便无法提供写入服务; 
  • 没有解决主节点写的压力; 
  • 数据冗余(为了高并发、高可用和高性能,一般是允许有冗余存在的);
  • 一旦主节点宕机,从节点晋升成主节点,需要修改应用方的主节点地址,还需要命令所有从节点去 复制新的主节点,整个过程需要人工干预; 
  • 主节点的写能力受到单机的限制; 
  • 主节点的存储能力受到单机的限制。

各场景下主从复制的选择及优化技巧:

**第一次建立复制:
**

此时全量复制不可避免,但仍有几点需要注意:如果主节点的数据量较大,应该尽量避开流量的高峰期,避免造成阻塞;如果有多个从节点需要建立对主节点的复制,可以考虑将几个从节点错开,避免主节点带宽占用过大。此外,如果从节点过多,也可以调整主从复制的拓扑结构,由一主多从结构变为树状结构(中间的节点既是其主节点的从节点,也是其从节点的主节点);但使用树状结构应该谨慎——主节点的直接从节点减少,降低了主节点的负担,但是多层从节点的延迟增大,数据一致性变差,且结构复杂,维护相当困难。

主节点重启:

  1. **主节点宕机:**主节点宕机重启后,Runid会发生变化,因此不能进行部分复制,只能全量复制。实际上在主节点宕机的情况下,应进行故障转移处理,将其中的一个从节点升级为主节点,其它从节点从新的主节点进行复制;且故障转移应尽量的自动化,后面文章将要介绍的哨兵便可以进行自动的故障转移。
  2. **安全重启:**在一些场景下,可能希望对主节点进行重启,例如主节点内存碎片率过高,或者希望调整一些只能在启动时调整的参数。如果使用普通的手段重启主节点,会使得runid发生变化,可能导致不必要的全量复制。 为了解决这个问题,Redis提供了debug reload的重启方式:重启后,主节点的runid和offset都不受影响,避免了全量复制。
  3. **从节点重启:**从节点宕机重启后,其保存的主节点的runid会丢失,因此即使再次执行slaveof,也无法进行部分复制。
  4. **网络中断:**①网络问题时间极为短暂,只造成了短暂的丢包,主从节点都没有判定超时(未触发repl-timeout)。此时只需要通过REPLCONF ACK来补充丢失的数据即可。②网络问题时间很长,主从节点判断超时(触发了repl-timeout),且丢失的数据过多,超过了复制积压缓冲区所能存储的范围。此时主从节点无法进行部分复制,只能进行全量复制。为了尽可能避免这种情况发生,应该根据实际情况适当调整复制积压缓冲区的大小;此外及时发现并修复网络中断,也可以减少全量复制。

主从复制相关配置

与主从节点都有关的配置:

  • slaveof:Redis启动时起作用;作用是建立复制关系,开启了该配置的Redis服务器在启动后成为从节点。该注释默认注释掉,即Redis服务器默认都是主节点
  • repl-timeout 60:与各个阶段主从节点连接超时判断有关

主节点相关配置:

  • repl-diskless-sync no:作用于全量复制阶段,控制主节点是否使用diskless复制(无盘复制)。所谓diskless复制,是指在全量复制时,主节点不再先把数据写入RDB文件,而是直接写入slave的socket中,整个过程中不涉及硬盘;diskless复制在磁盘IO很慢而网速很快时更有优势。需要注意的是,截至Redis3.0,diskless复制处于实验阶段,默认是关闭的
  • repl-diskless-sync-delay 5:该配置作用于全量复制阶段,当主节点使用diskless复制时,该配置决定主节点向从节点发送之前停顿的时间,单位是秒;只有当diskless复制打开时有效,默认5s。之所以设置停顿时间,是基于以下两个考虑:a.向slave的socket的传输一旦开始,新连接的slave只能等待当前数据传输结束,才能开始新的数据传输;b.多个从节点有较大的概率在短时间内建立主从复制
  • client-output-buffer-limit slave 256MB 64MB 60:与全量复制阶段主节点的缓冲区大小有关
  • repl-disable-tcp-nodelay no:与命令传播阶段的延迟有关
  • masterauth :与连接建立阶段的身份验证有关
  • repl-ping-slave-period 10:与命令传播阶段主从节点的超时判断有关
  • repl-backlog-size 1mb:复制积压缓冲区的大小
  • repl-backlog-ttl 3600:当主节点没有从节点时,复制积压缓冲区保留的时间,这样当断开的从节点重新连进来时,可以进行全量复制(默认3600s)。如果设置为0,则永远不会释放复制积压缓冲区。 
  • min-slaves-to-write 3与min-slaves-max-lag 10:规定了主节点的最小从节点数目及对应的最大延迟

从节点相关配置:

  • slave-serve-stale-data yes:与从节点数据陈旧时是否响应客户端命令有关
  • slave-read-only yes:从节点是否只读;默认是只读的。由于从节点开启写操作容易导致主从节点的数据不一致,因此该配置尽量不要修改

单机内存大小限制:

  • **切主:**当主节点宕机时,一种常见的容灾策略是将其中一个从节点提升为主节点,并将其它从节点挂载到新的主节点上,此时这些从节点只能进行全量复制。如果Redis单机内存达到10GB,一个从节点的同步时间在几分钟的级别;如果从节点较多,恢复的速度会更慢;如果系统的读负载很高,而这段时间从节点无法提供服务,会对系统造成很大的压力。
  • 从库扩容:****如果访问量突然增大,此时希望增加从节点分担读负载,如果数据量过大,从节点同步太慢,难以及时应对访问量的暴增。
  • 缓冲区溢出:****切主和从库扩容都是从节点可以正常同步的情形(虽然慢),但是如果数据量过大,造成全量复制阶段主节点的复制缓冲区溢出,从而导致复制中断,则主从节点的数据同步会全量复制→复制缓冲区溢出导致复制中断→重连→全量复制→复制缓冲区溢出导致复制中断……的循环。
  • 超时:****如果数据量过大,全量复制阶段主节点fork+保存RDB文件耗时过大,从节点长时间接收不到数据触发超时,主从节点的数据同步同样可能陷入全量复制→超时导致复制中断→重连→全量复制→超时导致复制中断……的循环。
  • 内存限制**:**主节点单机内存除了绝对量不能太大,其占用主机内存的比例也不应过大:最好只使用50%-65%的内存,留下30%-45%的内存用于执行bgsave命令和创建复制缓冲区等。

哨兵模式

在主从复制的基础上,哨兵模式实现了自动化故障恢复。 

哨兵模式由两部分组成,哨兵节点和数据节点: 

  • 哨兵节点:哨兵节点是特殊的 Redis 节点,不存储数据; 
  • 数据节点:主节点和从节点都是数据节点。 

Redis Sentinel 是分布式系统中监控 Redis 主从服务器,并提供主服务器下线时自动故障转移功能的模式。 

哨兵模式的三个特性

  • 监控:Sentinel 会不断地检查你的主服务器和从服务器是否运作正常; 
  • 提醒:当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知; 
  • 自动故障迁移:当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作。 
  • 配置提供者:客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。

哨兵模式原理

三个定时任务

  • 每 1 秒每个 Sentinel 对其他 Sentinel 和 Redis 节点执行 PING 操作(监控),这是一个心跳检 测,是失败判定的依据。 
  • 每 2 秒每个 Sentinel 通过 Master 节点的 channel 交换信息(Publish/Subscribe); 
  • 每 10 秒每个 Sentinel 会对 Master 和 Slave 执行INFO命令,这个任务主要达到两个目的: 发现 Slave 节点; 确认主从关系。

主观下线

在心跳检测的定时任务中,如果其他节点超过一定时间没有回复,哨兵节点就会将其进行主观下线

客观下线

is-master-down-by-addr命令询问其他哨兵节点该主节点的状态;如果判断主节点下线的哨兵数量达到一定数值,则对该主节点进行客观下线

选举领导者哨兵节点

当主节点被判断客观下线以后,各个哨兵节点会进行协商,选举出一个领导者哨兵节点,并由该领导者节点对其进行故障转移操作。

故障转移

  • 在从节点中选择新的主节点:选择的原则是,首先过滤掉不健康的从节点;然后选择优先级最高的从节点(由slave-priority指定);如果优先级无法区分,则选择复制偏移量最大的从节点;如果仍无法区分,则选择runid最小的从节点。
  • 更新主从状态:通过slaveof no one命令,让选出来的从节点成为主节点;并通过slaveof命令让其他节点成为其从节点。
  • 将已经下线的主节点设置为新的主节点的从节点,当原主节点重新上线后,它会成为新的主节点的从节点。

哨兵模式优点

  • 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都有;
  • 主从可以自动切换,系统更健壮,可用性更高;
  • Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。

哨兵模式缺点

  • 主从切换需要时间,会丢失数据;
  • 还是没有解决主节点写的压力;
  • 主节点的写能力,存储能力受到单机的限制;
  • 动态扩容困难复杂,对于集群,容量达到上限时在线扩容会变得很复杂。 

配置与实践建议

配置

  • **sentinel monitor {masterName} {masterIp} {masterPort} {quorum}:**sentinel monitor是哨兵最核心的配置,在前文讲述部署哨兵节点时已说明,其中:masterName指定了主节点名称,masterIp和masterPort指定了主节点地址,quorum是判断主节点客观下线的哨兵数量阈值:当判定主节点下线的哨兵数量达到quorum时,对主节点进行客观下线。建议取值为哨兵数量的一半加1。
  • **sentinel down-after-milliseconds {masterName} {time}:**sentinel down-after-milliseconds与主观下线的判断有关:哨兵使用ping命令对其他节点进行心跳检测,如果其他节点超过down-after-milliseconds配置的时间没有回复,哨兵就会将其进行主观下线。该配置对主节点、从节点和哨兵节点的主观下线判定都有效。 down-after-milliseconds的默认值是30000,即30s;可以根据不同的网络环境和应用要求来调整:值越大,对主观下线的判定会越宽松,好处是误判的可能性小,坏处是故障发现和故障转移的时间变长,客户端等待的时间也会变长。
  • **sentinel parallel - syncs {masterName} {number}:**sentinel parallel-syncs与故障转移之后从节点的复制有关:它规定了每次向新的主节点发起复制操作的从节点个数。例如,假设主节点切换完成之后,有3个从节点要向新的主节点发起复制;如果parallel-syncs=1,则从节点会一个一个开始复制;如果parallel-syncs=3,则3个从节点会一起开始复制。parallel-syncs取值越大,从节点完成复制的时间越快,但是对主节点的网络负载、硬盘负载造成的压力也越大。
  • **sentinel failover - timeout {masterName} {time}:**sentinel failover-timeout与故障转移超时的判断有关,但是该参数不是用来判断整个故障转移阶段的超时,而是其几个子阶段的超时,例如如果主节点晋升从节点时间超过timeout,或从节点向新的主节点发起复制操作的时间(不包括复制数据的时间)超过timeout,都会导致故障转移超时失败。 failover-timeout的默认值是180000,即180s;如果超时,则下一次该值会变为原来的2倍。

实践

  • 哨兵节点的数量应不止一个。一方面增加哨兵节点的冗余,避免哨兵本身成为高可用的瓶颈;另一方面减少对下线的误判。此外,这些不同的哨兵节点应部署在不同的物理机上。
  • 哨兵节点的数量应该是奇数,便于哨兵通过投票做出“决策”:领导者选举的决策、客观下线的决策等。
  • 各个哨兵节点的配置应一致,包括硬件、参数等;此外,所有节点都应该使用ntp或类似服务,保证时间准确、一致。
  • 哨兵的配置提供者和通知客户端功能,需要客户端的支持才能实现,如前文所说的Jedis;如果开发者使用的库未提供相应支持,则可能需要开发者自己实现。
  • 当哨兵系统中的节点在Docker(或其他可能进行端口映射的软件)中部署时,应特别注意端口映射可能会导致哨兵系统无法正常工作,因为哨兵的工作基于与其他节点的通信,而Docker的端口映射可能导致哨兵无法连接到其他节点。