02-Redis 部署模式01(重要)
官网保平安:https://redis.io/
分布式寻址算法
hash 算法
hashcode % size
:hash 算法是对 key 进行 hash 运算后取值,然后对节点的数量取模。接着将 key 存入对应的节点。
但是,一旦其中某个节点宕机,所有的请求过来,都会基于最新的存活的节点数量进行取模运算,这就会导致大多数的请求无法拿到缓存数据。
举个例子,一开始我有 3 个节点,这时大家正常的取模运算将数据基本均匀的存在了 3 个节点上,突然宕机了一台,现在只剩下 2 个有效的节点了,这时所有的运算都会基于最新的 2 个节点进行取模运算,原来的 key 对 2 进行取模运算后,显然和对 3 进行取模运算得到的结果是不一样的。结果是大量的数据请求会直接打在 DB 上,这是不可容忍的。
如果 size 发生变化,比如扩容或者缩容,几乎所有的历史数据都需要重 hash、移动,代价非常大(大量缓存重建)。
一致性 hash 算法
一致性 hash 算法将整个 hash 值空间组织成一个虚拟的圆环(大小为0-2^32-1
),整个空间按顺时针方向组织,下一步将各个 master 节点(使用服务器的 ip 或主机名)进行 hash。这样就能确定每个节点在其哈希环上的位置。
来看一个经典的一致性 hash 算法的环状示意图:
来了一个 key,首先计算 hash 值,并确定此数据在环上的位置,从此位置沿环顺时针「行走」,遇到的第一个 master 节点就是 key 所在位置。
在一致性哈希算法中,如果一个节点挂了,受影响的数据仅仅是此节点到环空间前一个节点(沿着逆时针方向行走遇到的第一个节点)之间的数据,其它不受影响。增加一个节点也同理。
一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。为了解决这种热点问题,一致性 hash 算法引入了虚拟节点机制,即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。
Python 中是否有相应模块?
模块:hash_ring
Redis Cluster 的 hash slot
Redis Cluster 的实现方案十分的聪明,它的分区方式采用了虚拟槽分区。
Redis Cluster 首先会预设虚拟槽,每个槽就相当于一个数字,有一定范围,每个槽映射一个数据子集。
Redis Cluster 会把 16384 个槽按照节点数量进行平均分配,由节点进行管理。
- 当一个 key 过来的时候,会对这个 key 按照 CRC16 规则进行 hash 运算。
- 把 hash 结果对 16383 进行取余。
- 把余数发送给 Redis 节点。
- 节点接收到数据,验证是否在自己管理的槽编号的范围
- 如果在自己管理的槽编号范围内,则把数据保存到数据槽中,然后返回执行结果。
- 如果在自己管理的槽编号范围外,则会返回重定向,客户重新发送请求到正确的节点。
虚拟槽分布方式中,由于每个节点管理一部分数据槽,数据保存到数据槽中。当节点扩容或者缩容时,对数据槽进行重新分配迁移即可,数据不会丢失。
部署方式
Redis 集群就是分区的一种的实现。
单机版
特点:简单。
问题:1、内存容量有限。2、处理能力有限。3、无法高可用。
主从复制
Redis 的复制功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中:
- 被复制的服务器为主服务器(master)
- 通过复制创建出来的服务器复制品则为从服务器(slave)。
只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,master 就会一直将发生在自己身上的数据更新同步给 slave,从而一直保证主从服务器的数据相同。
特点:
- master/slave 数据相同
- 降低 master 读压力,转交从库
问题:
- 无法保证高可用。
- 没有解决 master 写的压力。
注意:
如果采用主从架构,那么必须开启 master 的持久化,不建议用 slave 作为 master 的数据热备,因为那样的话,如果你关掉 master 的持久化,master 突然宕机,然后重启时数据是空的,然后经过复制,slave 的数据也丢了。
- slave 可以自动接管 master,但也可能 Sentinel 还没检测到 master failure,master 就自动重启了,还是可能导致上面所有的 slave 数据被清空。
另外,master 的各种备份方案也需要做。万一本地的所有文件丢失了,从备份中挑选一份 rdb 去恢复 master,这样才能确保启动的时候,是有数据的。
Redis 如何实现主从复制?以及数据同步机制?
一台服务器,启动两个 Redis 服务,分别创建 6379 和 6380 配置文件
redis.conf
:6379 为默认配置文件,作为 master 服务配置。redis_6380.conf
:6380 为同步配置,作为 slave 服务配置。
配置 slaveof 同步指令
- 在 slave 对应的 conf 配置文件中,添加以下内容:
slaveof 127.0.0.1 6379
。
数据同步步骤:
- 当 master 和 slave 建立主从关系后,会向 master 发送
SYNC
命令。 - master 接收到
SYNC
命令后会开始在后台保存快照(RDB持久化过程),并将期间接收到的写命令缓存起来。 - 当快照完成后,master 会将快照文件和所有缓存的写命令发送给 slave。
- slave 接收到后,会载入快照文件并且执行收到的缓存的命令。
- 之后,master 每当接收到写命令时就会将命令发送 slave,从而保证数据的一致。
主从复制的原理
1、主从架构的核心原理
当启动一个 slave 的时候,它会发送一个 PSYNC
命令给 master
- 如果这是 slave 重新连接 master,那么 master 仅仅会复制 slave 缺少的数据,
- 如果这是 slave 第一次连接 master,那么会触发一次 full resynchronization
开始 full resynchronization 的时候,master 会启动一个后台线程,开始生成一份 RDB 快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。RDB 文件生成完毕之后,master 会将这个 RDB 发送给 slave,slave 会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后 master 会将内存中缓存的写命令发送给 slave,slave 也会同步这些数据。
slave 如果跟 master 有网络故障,断开了连接,会自动重连。master 如果发现有多个 slave 都来重新连接,仅仅会启动一个 rdb save 操作,用一份数据服务所有 slave。
2、主从复制的断点续传
从 Redis 2.8 开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。
master 会在内存中常见一个 backlog,master 和 slave 都会保存一个 replica offset 还有一个 master id,offset 就是保存在 backlog 中的。如果 master 和 slave 网络连接断掉了,slave 会让 master 从上次的 replica offset 开始继续复制
但是如果没有找到对应的 offset,那么就会执行一次 resynchronization
3、无磁盘化复制
master 在内存中直接创建 rdb,然后发送给 slave,不会在自己本地落地磁盘了。
repl-diskless-sync repl-diskless-sync-delay,等待一定时长再开始复制,因为要等更多 slave 重新连接过来
4、过期 key 处理
slave 不会过期 key,只会等待 master 过期 key。如果 master 过期了一个 key,或者通过 LRU 淘汰了一个 key,那么会模拟一条 del 命令发送给 slave。
由于主从延迟导致读取到过期数据怎么处理?
通过 scan 命令扫库:当 Redis 中的 key 被 scan 的时候,相当于访问了该 key,同样也会做过期检测,充分发挥 Redis 惰性删除的策略。这个方法能大大降低了脏数据读取的概率,但缺点也比较明显,会造成一定的数据库压力,否则影响线上业务的效率。
Redis 加入了一个新特性来解决主从不一致导致读取到过期数据问题,增加了 key 是否过期以及对主从库的判断,如果 key 已过期,当前访问的 master 则返回 null;当前访问的是从库,且执行的是只读命令也返回 null。