Redis学习 - 基础

主要知识点:

1、数据结构的选择:数据量小选择数组和压缩列表,数据量大选择哈希表和跳表。数组和压缩列表都是在内存中分配一块地址连续的空间,非常紧凑。

哈希表一个桶数据量大时,会渐进式 rehash。哈希表中存的是 key 和 value 的指针。

2、多路复用模型:主线程接受网络、处理数据、fork 子线程,NIO中 accept() 和 s/r 会造成阻塞。多路复用监听网络和数据处理事件,批量处理,所以不会阻塞。Redis 6.0多线程主要是利用多核来分摊 IO 读写。

3、AOF 和 RDB 持久化:重写 AOF 日志 和快照刷 RDB,主线程 fork 子线程,子进程拷贝父进程内存页表(虚拟和物理内存映射表),阻塞主线程。(耗时和 Redis 实例的内存大小有关)

写时复制:

Redis 接收新写操作,分配内存,有旧 key 的拷贝数据,在新内存上修改数据,此时子进程内存页指向旧数据。做到了资源隔离。(父子进程就是为了做资源隔离)

AOF:新增操作,新旧 AOF 日志都追加操作命令。修改操作,AOF 旧日志缓冲区追加操作命令。在新日志拷贝完命令后,把旧日志缓冲区命令追加到新 AOF 日志里。

RDB:新增和修改操作都是分配内存,然后做数据操作。这些写数据对子进程不可见。所以子进程只会存第一次快照时的数据,修改后的数据在第二次快照后通过 RDB 刷到磁盘。

4、主从同步:从库请求建立连接,主库第一次会把 RDB 文件传给从库做全量复制。是通过 repl_buffer 缓存发送文件给 socket 做同步的。

主库的写操作,都会写到 backlog_buffer 环形缓存中。主从断连, repl_buffer 消失,新写操作继续写到 backlog_buffer ,并且记录主库的写偏移量 master_backlog_offset。

主从重接,从库将读偏移量带到主库。主库将两个偏移量之间的命令通过 repl_buffer 同步到从库。如果主库写的很快,从库带来的读偏移量被覆盖,就会重新全盘复制。

5、哨兵:监听,选主,通知。监听主观下线,可观下线。选主优先级、同步程度、ID 大小三个按顺序选主。

6、哨兵集群:哨兵和主库、从库、客户端建立连接。选举 leader 来负责实际的主从切换。

7、集群切片:实例与实例之间通信,每个实例保存完整的 哈希槽和实例 的映射关系。客户端请求时,获取到映射关系保存到本地。映射关系改变时,会用到重定向的操作。MOVED 命令让客户端更新本地缓存,ASK 命令数据在迁移,不会让客户端更新缓存。

 

1、Redis 速度快的原理

  Redis 采用哈希索引,哈希表O1和内存的高性能随机访问特性可以很好的匹配。

  Redis 速度快,一方面是因为Redis是内存数据库,所有操作都在内存中完成。一方面是数据结构,按键值对存储。所以高效的数据结构就是Redis快速处理数据的基础。

  另一方面是采用了 多路复用机制

  底层数据结构:简单动态字符串、双向链表、压缩列表、哈希表、跳表和整数数组。

  键和值的数据结构

 

 

 

   redis 使用了一个哈希表来保存所有键值对。一个哈希表就是一个数组,数组的每个元素就是一个哈希桶。每个哈希桶保存了键值对数据。(哈希桶保存的是指向具体值的指针)

 

  计算键的哈希值的时间复杂度是 O1,就可以知道它对应的哈希桶位置,可以访问对应的 entry 元素。(依赖哈希计算,和数据量的大小没有直接关系。)

  哈希表变慢:哈希表的冲突问题和 rehash 可能带来的阻塞。

  哈希冲突:两个 key 的哈希值和哈希桶计算对应关系时,正好落在一个哈希桶中。

  解决方式:链式哈希,同一个 哈希桶中的多个元素用一个链表来保存,它们之间依次用指针连接。

  但是当哈希表写入的数据越来越多,哈希冲突链越来越长,就回导致这个链上的元素查询耗时长,效率降低。

  rehash操作:Redis 默认使用两个全局哈希表。哈希表2最开始没有分配空间。当数据量逐渐增多到一定量,就会给哈希表2分配更大空间,将哈希表1的数据重新映射并拷贝到哈希表2中,释放哈希表1的空间。

  但是一次性拷贝的话会造成 redis 线程阻塞,所以采用了 渐进式 rehash

  每处理一个请求,就拷贝一次。这样将一次性大量拷贝的开销,分摊到了多次处理请求的过程中。

  要在字典里面查找一个键的话, 程序会先在 ht[0] 里面进行查找, 如果没找到的话, 就会继续到 ht[1] 里面进行查找, 诸如此类。

  渐进式 rehash 执行期间, 新添加到字典的键值对一律会被保存到 ht[1] 里面, 而 ht[0] 则不再进行任何添加操作: 这一措施保证了 ht[0] 包含的键值对数量会只减不增, 并随着 rehash 操作的执行而最终变成空表。

 

 

 

 

 

 

 集合数据操作效率

  集合的操作效率和底层数据结构有关。操作本身的执行特点有关,例如读写一个元素比读写所有元素的效率高。

  压缩列表:有三个字段表示列表长度、列表尾的偏移量和列表中的 entry 的个数。还有个字段表示列表结束。

  所以查找第一个和最后一个元素,直接通过表头三个字段定位,复杂度O1。查找其他元素,就是 On。

  跳表复杂度O(logN),进行范围查询很方便。

 

 

 

  单元素操作是基础;

    范围操作非常耗时;

    统计操作通常高效;

    例外情况只有几个。

  复杂度较高的 List 类型,它的两种底层实现结构:双向链表和压缩列表的操作复杂度都是 O(N)。因此,我的建议是:因地制宜地使用 List 类型。

  思考题:

  Redis用数组和压缩列表都是On,效率并不是很高。但是内存利用率好,都是非常紧凑的数据结构,比链表占用的内存更少。

  数组对 CPU 的高速缓存支持更友好。当数据量较少时,Redis 默认采用内存紧凑排列的方式存储。当数据量超过阈值后,避免查询时间复杂度太高,转为哈希表和跳表数据结构。

  如果在数组上是随机访问,会造成 CPU 高速缓存整个缓存行失效,命中率降低。(取决于数组的大小)

  不同的设计都是有不同的考虑。从操作复杂度和内存等去考虑。

 

2、高性能IO模型

  Redis 单线程,指 Redis 的网络 IO 和键值对读写是由一个线程来完成,也是 Redis 对外提供键值对存储服务的主要流程。

  但是 Redis 的其他功能,持久化、异步删除、集群数据同步等,是由额外的线程执行的。(单线程设计机制和多路复用机制)

  多线程的性能瓶颈原因:系统存在被多线程同时访问的共享资源,为了保证资源的正确性,需要额外的机制,就带来了额外的开销。

 

  多线程一般引用同步资源来保护共享资源,会降低系统代码的易调试线和可维护性。

  Redis 的单线程模型能达到每秒数十万级别的处理能力。

 

  Socket 网络模型支持非阻塞模式。

  多路复用机制的高性能 IO 模型

  一个线程处理 IO 流,select/epoll 机制。内核会一直监听存在的监听套接字和已连接套接字。一旦有请求到达,就会交给 Redis 线程处理,实现了一个 Redis 线程处理多个 IO 流的效果。(多路复用避免了 accept() 和 send()/recv() 潜在的网络 IO 阻塞点)

 

 

 

 

  思考题:

  Redis 单线程的性能瓶颈的原因:

  耗时操作:redis 在4.0 推出了 lazy-free 机制,把 bigkey 释放内存的耗时操作放在异步线程中执行。

    1)、bigkey 分配和删除需要消耗更多时间;

    2)、使用复杂度过高的命令,例如 sort。

    3)、大量 key 集中过期,key 的过期操作在主线程中执行。

    4)、淘汰策略也是在主线程中执行。

    5)、AOF 刷盘开启 always 机制,每次写入都需要写到磁盘。

    6)、主从全量同步生成 RDB,fork 的一瞬间也是会阻塞线程。

    并发量大:读写客户端数据是同步IO,只能单线程依次读取客户端的数据,无法利用 CPU 的多核。

  Redis 6.0引入多线程的原因:充分利用CPU多核优势,分摊 Redis 同步IO 读写负荷。执行命令还是单线程顺序执行,处理网络数据读写采用多线程。IO线程读和写不会同时进行,保证线程安全。

 

 

 

 

 

 

 

3、AOF 日志:宕机了,Redis 数据怎么避免丢失

  Redis 常用于缓存场景,这就有一个问题,服务端宕机,内存中的数据将全部丢失。

  此时我们可以从数据库恢复数据。

  Redis 的 AOF(append Only Files) 是写后日志,记录的是 Redis 已经操作的每一条写命令。

  Redis 向 AOF 里记录日志时,不会去检查语法,所以如果是写前日志,如果语法错误的话,就可能在恢复数据的时候出错。当 Redis 写好数据再写日志,能保证日志语法的正确性。(并且不会阻塞当前的写操作,因为 AOF 操作是主线程执行的,所以会阻塞下一个操作。

  潜在风险:刚执行完一个命令还未记录日志就宕机(直接用作数据库无法恢复数据)、AOF 是主线程执行,如果把日志文件写入磁盘,可能阻塞下一个操作。

  这两个风险都是和 AOF 写回磁盘的时机有关。

  解决第一个风险:写入策略,Always(同步写回)、Everysec(每秒写会)、No(系统控制写回时机)。(没有完美的策略,重在取舍)

  日志文件太大,文件系统对文件大小限制、追加命令记录,效率变慢、宕机后恢复数据很慢三个影响。所以就用到了 AOF 重写机制

  AOF 重写日志

  创建一个新的 AOF 文件,在旧日志文件针对一个 key 的操作命令可以取最新的命令,拷贝到新的 AOF 文件。

 

 

 

 

  新日志写回磁盘,也是一个比较耗时的过程。

  重写过程是由后台子进程 bgrewriteaof 来完成,不会阻塞主线程。每次执行重写创建新 AOF 日志,主线程 fork 出后台的 bgrewriteaof 子进程。

  在此过程中,主线程未阻塞,新的写操作,一样会写到旧 AOF 日志的缓冲区。并且该操作也会写到新 AOF 日志的缓冲区。

 

 

 

 

  旧 AOF 日志到新 AOF 日志的过程:根据数据库里数据的最新状态,生成这些数据的插入命令,作为新日志。(此操作使用后台线程,不会阻塞主线程。)

  思考题:

  进程的目的就是做资源隔离。

  fork 子进程的时候不会一次性拷贝所有内存数据给子进程。采用写时复制(copy on write)机制,避免一次性拷贝大量内存数据给子进程造成长时间的阻塞。

  但是 fork 子进程需要拷贝内存页表(虚拟内存和物理内存的映射索引表),这个过程会消耗大量 CPU 资源,拷贝结束前整个进程是阻塞的。阻塞时间取决于实例的内存大小。

  内存页表拷贝完成后,子进程与父进程指向相同的内存地址空间,可以访问父进程的内存数据。只有在发生写时,才会父子进程分离。

  子进程执行 AOF 重写,父进程依旧会有数据写入。如果父进程操作一个已经存在的 key ,就会拷贝这个 key 对应的内存数据,申请新的内存空间,用于旧数据(副本)。

  内存分配以页为单位分配,默认4K,如果是 bigkey,申请大块内存时间变长,可能会阻塞。

  如果操作系统开启了内存大页机制(Huge Page,页面大小 2M),父进程申请内存时阻塞的概率将会提高。所以 Redis 需要关闭 Huge Page 机制。

  疑惑点:

  如果是没有修改的数据,旧 AOF 日志文件和新 AOF 日志文件,都是指向同一个物理内存地址。(写时复制机制)

  写入新数据时重新分配内存,同时将操作写入旧、新 AOF 日志中。(不知道是不是写入缓冲区,或者说缓冲区本就是 AOF 日志的一部分。)

  修改数据,父线程分配内存,将旧数据拷贝一份放到新分配到的内存中(拷贝的是key和v的指针),对新内存中的数据做修改。操作记录写入旧 AOF 日志缓冲区。最后写入到新 AOF 日志。

4、宕机了,Redis 数据恢复

  虽然 AOF 日志避免了数据的丢失,但是 AOF 方法进行恢复数据时,是把 AOF 日志执行一遍。如果 AOF 日志过大,恢复的时间就会很长。

  内存快照(内存中的数据在某一时刻的状态记录):既保证了数据的可靠性,又能快速实现数据恢复。

  RDB(Redis DataBase):快照文件,记录某一时间的数据(AOF 记录的是操作),数据恢复时,直接把 RDB 读入内存就恢复成功。

  考虑到2个问题:

    对哪些数据做快照?关系到快照的执行效率问题。(全量快照)

  做快照时,数据还能写么?关系到 Redis 是否被阻塞,能否同时处理正常请求。(写时复制 copy on write

  全盘快照:提供2个命令来生成 RDB 文件。

    save:在主线程中执行,会导致阻塞。

    bgsave:fork 一个子进程,专门用于写 RDB 文件,避免主线程的阻塞。(默认配置。)

  用 bgsave 命令,既保证了数据的可靠性,有避免了对 Redis 的性能影响。

  写时复制:fork 出来的子进程(此过程主线程阻塞,需要拷贝内存页表),共享主线程中的所有内存数据。bgsave 子进程运行后,读取主线程中的内存数据,并写入 RDB 文件中。

  主线程和子进程的内存页面中的指针,都指向相同的内存物理地址。

  当主线程需要写操作时,这块数据就被复制一份,生成该数据的副本。然后 bgsave 子进程就去读取副本的数据写入到 RDB 文件。

  注:修改操作:分配内存,拷贝数据,在新内存中修改 value。旧数据还存在旧内存地址上,所以子进程根据映射关系,读取数据依旧是老数据。而父进程会更新自己的页面映射,所以父进程会读取到新数据。(保证了不会阻塞,Redis 请求能读取最新数据)

  RDB 快照,只能记录在快照时刻的数据,第一次快照结束到第二次快照之间修改的数据,不能存放。

  在两次快照期间宕机,则在第一次快照后修改的数据就会丢失。

  如果频繁的执行全量快照,也会带来两方面的开销。一是给磁盘带来很大压力,多个快照竞争有限的带宽,容易造成恶性循环。

  另一个是主线程 fork 子进程是需要阻塞主线程的。并且主线程的内存越大,阻塞时间越长。频繁的 fork 子进程,就会频繁的阻塞主线程。

  可以做增量快照,只需要记录哪些数据被修改了。

  混合使用 AOF 日志和 RDB 内存快照的方法。内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的命令操作。当第二次快照执行后,就清空 AOF 日志。(宕机后,就先恢复快照中的数据,再把 AOF 日志写到内存)

  思考题:

  数据不能丢失时,内存快照和 AOF 的混合使用是一个好选择;

  允许分钟级别的数据丢失,可以只用 RDB;

  如果只用 AOF,有限使用 everysec 的配置选项,在可靠性和性能之间平衡。

  写操作覆盖多少比例的 key,父进程就会重新分配多少比例的内存副本。

  重写的时候,是把内存中的数据以 RDB 的格式写入到 AOF 文件中(一种优化)。

  写时复制:

  AOF:新增,主线程分配内存,存入新数据。追加操作日志到新旧2个 AOF 日志。

  AOF:修改,分配内存,从主线程拷贝数据到新内存。将操作日志写入到旧 AOF 日志缓冲区,最后写入新 AOF 日志缓冲区。

  RDB:新增,主线程分配内存,存入新数据。

  RDB:修改,分配内存,从主线程拷贝数据到内存,然后修改该数据的 value。拷贝的数据是 Redis 的数据结构中的哈希表,存放的 key 和 value 的指针。所以主要是拷贝 key。如果是 bigkey 的话,就可以会阻塞主线程。

  修改时两者之间的区别:RDB 快照时,修改操作不会记录上去。AOF 重写日志时,修改操作会记录到旧 AOF 日志缓冲区,当子进程重写完映射关系的 AOF 日志时,就会追加旧 AOF 日志缓冲区的操作记录。

写操作,都会修改主线程自己的页面映射(新内存中的物理地址重新映射到主线程中)。

  RDB 文件内容是经过压缩的 二进制数据,文件很小。

 

5、数据同步:主从库实现数据一致

  主从库模式,主写,所有都可以读(读写分离。)

  实例2上执行: replicaof + 主库ip + 端口,就把实例2变为实例1的从库,并且从实例1上复制数据。

  主库和从库第一次同步:(全盘数据同步)

  从库发送 psync 需要同步数据的命令,包含了主库的 runID 和 offset 两个参数。

  runID 是每个 Redis 实例启动时都会自动生成的一个随机ID;第一次同步从库不知道主库的 runID。 runID=?也表示这次同步是第一次同步。

  offset 设为 -1 ,表示第一次复制。

 

 

 

 

  第一次复制采用的是全量复制。这个过程依赖内存快照生成的 RDB 文件。

  同步过程中,Redis 主库依然接收写请求。为了保证主从库的数据一致性,主库会在内存中针对每一个从库生成一个专用的 replication buffer,记录 RDB 文件生成后收到的所有写操作。最后再把 repl buffer 中的操作命令发送给从库,从库加载这些写命令。

  当从库较多时,可以使用主从级联模式(主 - 从 - 从模式),将 主库生成 RDB 和传输 RDB 的压力分散的从库上。(同步数据时的操作)

  完成全量复制后,主从库之间维护一个网络连接,主库传输后续接收到的写命令,同步给从库。称为:基于长连接的命令传播

  存在风险:网络断连和阻塞,这样就不能保证主从库之间的数据一致性。

  解决办法:

    主从库采用增量复制的方式继续同步,主要是用 repl_backlog_buffer 这个缓冲区。从库存在,repl_backlog_buffer 就会存在。主库的所有写命令除了传播给从库外(写到 repl_buffer 中),都会写进 repl_backlog_buffer 中。

  每个 client 和 Redis 通信,Redis 都会给每个 client 分配一个 repl_ buffer。所有数据交互都是主库先把数据写入到 repl_buffer 中,repl_bffer在发送给 client_socket 再通过网络发送出去。

  repl_backlog_buffer 是一个环形缓冲区(一个数组),主库记录自己写到的位置,从库记录已经读到的位置。

  主库在缓冲区写的位置的偏移量:master_repl_offset,主库接收的新写操作越多,这个值就越大。

  从库在缓冲区读的位置的偏移量:slave_repl_offset,也会逐渐增加。在未断连接的情况下,这两个偏移量基本相等。

  增量复制保持了同步断开连接,repl_buffer 就会消失。

  主库继续写命令到 repl_backlog_buffer 中,master 偏移量继续增大。

  主从连接恢复,从库给主库发送 psync 命令,并且把 slave_repl_offset 偏移量发送给主库,主库判断两个偏移量的差距,并且把两个偏移量之间的写命令,先写到 repl_buffer 中,再同步给对应的从库。

  但是 repl_backlog_buffer 缓冲区是环形的,在缓冲区中写满后,主库会继续写入,会覆盖掉之前的写入操作。如果从库读取过慢,缓冲区的某些数据被覆盖,导致主从库数据不一致。(master 指针又超过了 slave 指针)

  可以调整 repl_backlog_size 的大小。一般是缓冲区大小的2倍。并发量大的时候可以调整的大一些。(或者切片集群来分担单个主库的请求压力)(size 默认值 1M)

  思考题:

  一个 Redis 实例的数据库不要太大,几 GB 级别就合适,减少 RDB 文件生成和传输、重新加载的开销。

  主从库断连时间过长,从库的在主库的 repl_backlog_buffer 的 slave_backlog_offset 位置已经被覆盖,主从库就会进行全量复制。

  每个从库都有一个偏移量值,所以和主库重连时,从库会把偏移量发给主库,主库来判断是否需要全量复制。

 

 

 

 

 

 

 

 

 

 

 

 

  主从库之间依然会有数据不一致的情况。网络断开,主库修改,从库读取旧数据,就会产生数据不一致的问题。

  所有主从同步的数据都是要经过这个 repl_buffer 的,无论全量同步还是增量同步

6、哨兵机制

  主库故障后,客户端新的写操作,也无法进行主从同步。此时就用到了哨兵机制。

  哨兵:特殊模式的 Redis 进程,主从库实例运行时它也在运行。

  它的主要任务有:监控、选主、通知

  监控:周期性的给主从库发送 PING 命令,检测她们是否仍然在先运行。规定时间内,主从库分别没有响应,就下线该实例。主库开始 自动切换主库 的流程。

  选主:按一定规则选一个从库实例当做主库。

  通知:哨兵把新主库的连接信息发给其他从库,让它们执行 replicaof 命令,和新主库建立连接,进行数据复制。同时哨兵会把新主库的连接信息通知给客户端,让它们把请求发送到新主库。

  误判:集群网络压力大,网络拥塞或者主库压力大,可能会超时响应。

  监控主库时,需要避免误判。响应超时,哨兵会先把主库标记为 主观下线。但是并不能开始选主。

  哨兵采用集群模式部署,成为 哨兵集群

  多数哨兵判断主库已经主观下线,主库就会标记为客观下线。这时会出发主从切换。

  哨兵数量的标准是 N/2 + 1 个实例判断是否主观下线,是则把主库标记为客观下线。

  选择新主库

    筛选:检查从库当前在线状态,还要判断它之前的网络连接状态。

    down-after-milliseconds * 10:最大连接超时时间断连以及断连次数超过10次

    打分:从库优先级、从库复制进度、从库ID号。只要有从库在一轮中得分最高,就是主库,则选主过程结束。

    从库优先级:可以根据内存大小手动配置优先级。可以优先级一样,也可以不一样。

    从库和主库同步程度:从库的偏移量 slave_repl_offset 值最大,则同步程度最高

    ID 号:ID 号最小的从库得分最高,会被选为主库。

思考题

  哨兵在主从切换过程中,客户端使用了读写分离,能读,但是不能写。受影响的时间等于哨兵切换主从库的时间和客户端感应到新主库的时间。

  主库的响应时间可以配置。配长配短都有一定的风险。

  客户端需要访问主从库时,不写死实例地址,而是从哨兵集群中获取最新的地址。或者哨兵主动通知,将新主库的地址写入自己的 pubsub 中,客户端需要订阅 pubsub 中。

  在分片集群模式下,这些逻辑都可以做再 proxy 层。可以不使用时哨兵机制。

7、哨兵集群

  哨兵和主库建立连接

  可以在主库上发布消息。其他哨兵可以从主库上订阅消息。pub/sub 机制。

  只有订阅了同一种类别的消息,才可以通过发布的消息进行信息交换。哨兵是通过:_ _sentinel_ _:hello 频道互相通信的。

 

 

 

 

  哨兵和从库进行连接

    哨兵发送 INFO 命令给主库,从主库中获取从库列表。然后就可以建立连接。

  哨兵和客户端进行连接

  客户端需要知道哨兵在监控、选主过程中进行到哪一步了。

  每个哨兵也是一个 Redis 实例,但是只完成监听、选主、通知的任务。提供 pub/sub 机制,客户端可以从哨兵订阅消息。

  哨兵提供的消息订阅频道有很多,包含了主从切换过程中的不同关键事件。

 

 

 

 

哨兵选举

  任何一个实例判断了主库 主观下线后,就会给其他实例发送 is-master-down-by-addr 命令。其他实例会根据自己和主库的连接情况,做出Y或者N的响应。

  需要的赞成客观下线的票数是由哨兵配置文件中的 quorum 配置设定的。哨兵选择的投票过程成为 leader 选举。

  成为 leader 哨兵的条件:拿到半数以上的赞成票、需要大于哨兵配置文件中的 quorum 的值。

  如果没有选举出来,就可能是网络传输堵塞等,就再等待一段时间进行 leader 选举。

 

 

 

 

思考题

  要保证所有的哨兵实例的配置是一致的,尤其是主观下线的判断值 down-after-milliseconds。

 

8、切片集群

  分片集群,启动多个 Redis 实例组成一个集群。把数据分成多份,一份用一个实例来保存。

  可以升级单个 Redis ,也可以增加 Redis 实例的个数。

  纵向扩展:使用 RDB 持久化时,数据量大时,主线程 fork 子线程可能会阻塞。如果不要求持久化保存 Redis 数据,可选择升级耽搁 Redis 实例。还有硬件和成本的限制。

  横向扩展:数据切片后,多个实例之间如何分部?客户端怎么确定想要访问的数据在哪个实例上。

  Redis Cluster 方案中规定了数据和实例的对应规则。该方案采用哈希槽(hash slot)来处理数据和实例之间的映射关系。

  一个切片集群共有 16384 个哈希槽。可以指定也可以默认均分哈希槽到每个实例上。实例资源配置不一的时候,可以使用 cluster addslots 指定每个实例上的哈希槽个数。手动分配时要把 16384 个哈希槽分配完。

  类似数据分区,每个键值对都会根据它的 key,被映射到一个哈希槽中。(key 计算值后对 16384 取模)

  客户端定位数据

  定位键值对,可以根绝 key 算出哈希槽,再定位到实例。实例之间相互连接,互相通信哈希槽信息。客户端收到哈希槽信息,会将哈希槽信息缓存到本地。

  实例和哈希槽的对应关系的变化:实例的新增和删除,Redis 需要重新分配哈希槽。负载均衡,Redis 需要把哈希槽在所有实例上重新分部一遍。

  此时客户端缓存的哈希槽信息变化。需要 Redis Cluster 方案提供一种 重定向机制。客户端给一个实例发送数据读写操作,这个实例没有数据,客户端需要再给另一个实例发送操作命令。(没有数据的实例返回的 MOVED 命令响应结果,包含了新实例的访问地址。缓存实例-槽信息到客户端。)

  ASK 命令是返回槽中的数据正在迁移,不会将槽和实例映射缓存到客户端。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2021-03-30 14:27  几近虚年  阅读(423)  评论(0编辑  收藏  举报