前言

之前简略分享了一下单机版Redis。包括:

  • 为什么用 Redis 以及为什么不用 Redis
  • Redis 为什么这么快
  • Redis 的数据结构
  • Redis 单线程吞吐量高的原因

单机版 Redis 呼应快、支撑的并发量高,假如有耐久化的内存,乃至还能做到数据不丢掉。那咱们能够直接运用单机版 Redis 作为数据库吗

假定咱们运用单机版 Redis 作为存储介质。Redis 一般分为两种用处,缓存和耐久化数据库。

咱们先看最常见的一种情况,断电/重启:

  • 事务数据库: 运用耐久化内存能够确保重启后数据不丢掉。可是断电的过程中整个事务没有数据源就会瘫痪掉。
  • 缓存: 假如缓存断电瘫痪,倒不至于会对事务形成直接瘫痪,毕竟事务数据库中有完好的数据。假如 Redis 宕机,就会导致缓存雪崩——一切缓存都失效,一切的恳求都是直接打到数据库,导致数据库压力激增,导致服务呼应时刻添加。

在断电情况下,单机 Redis 作为事务数据库肯定是不满足条件的;作为缓存,假如数据量小、恳求量少的话,并不是很影响事务运用(这种场景也没有用缓存的必要)。

那假如不考虑重启的情况,单机 Redis 就没问题了吗

数据量、拜访量小的情况下,貌似确实不会有什么问题,咱们还是考虑数据量、拜访量大的情况。

比方现在有一个单机 redis, 需求存储几亿个key。当 Redis 耐久化时,假如数据量添加,需求的内存也会添加,主线程 fork 子进程时就可能会堵塞。即使在内存、CPU资源足够的情况下,一次数据备份就要耗费很长时刻,导致 Redis 的服务不可用

不过,假如你不要求耐久化保存 Redis 数据,那么,添加cpu的硬件功能和内存容量会是一个不错的挑选。可是这样又有第二个问题,便是硬件本钱问题:一块128G的内存条的价格远大于四块32G 的内存条的价格之和,cpu也是同理。硬件的本钱会随着功能的不断提高呈指数上升

综上所述,单机版 Redis 首要存在以下两个问题:

  1. 可用性(断电后,事务不可用)
  2. 数据臃肿(单机CPU对很多数据的操作)
    • 备份堵塞导致服务不可用
    • 本钱指数增加

那怎样办呢?单机不够用,就多加几台机器嘛!下面咱们来看一下 Redis 供给的在多机环境下几种常见的资源运用战略,看看能否处理上述的两个问题

一、主从同步(Master-Slave)

主从同步便是,多个 Redis 节点,一个主节点(master),多个副节点(slave)。读恳求会发送到各个节点,写恳求只会发给 master,然后 slave 定时会从 master 拉取新增的写操作以确保数据的一致性。

浅谈Redis(二)——集群版

那怎样从单机版加一个 slave 节点过渡到主从形式呢? 例如,现在有实例 1(ip:192.168.19.3)和实例 2(ip:192.168.19.5),咱们在实例 2 上履行以下这个指令后,实例 2 就变成了实例 1 的从库,并从实例 1 上仿制数据:

replicaof 192.168.19.3 6379

第一次恳求到 master 的 RDB 文件全量同步,在 RDB 文件内容之后的操作都是增量同步进行。

浅谈Redis(二)——集群版

之后便是重复1和3的过程。

二、岗兵机制(Sentinel)

上面讲的主从同步只是确保了 Redis 的数据有及时备份。能够分管一部分主库的拜访压力,以及确保了从库的可用性。可是假如主库发生故障了,那就直接会影响到从库的同步,由于从库没有相应的主库能够进行数据仿制操作了。

岗兵(Sentinel)便是为了处理这个问题而产生的。它完成了主从库主动切换的关键机制,它有效地处理了主从仿制形式下故障转移的三个问题。

  • 主库真的挂了吗?
  • 该挑选哪个从库作为主库?
  • 怎样把新主库的相关信息通知给从库和客户端呢?

下面咱们就从这三个问题来看 Sentinel 对应的三种职能,监控、选主和通知。

2.1 监控

岗兵需求判别主库是否处于下线状况。这儿要先说两个概念,即片面下线客观下线

岗兵进程会运用 PING 指令检测它自己和主、从库的网络连接情况,用来判别实例的状况。假如岗兵发现主库呼应超时了,那么,岗兵就会先把它标记为“片面下线”。假如检测的是从库,那么,岗兵简略地把它标记为片面下线就行了,由于从库的下线影响一般不太大,集群的对外服务不会间断。

假如检测的是主库,那么,岗兵还不能简略地把它标记为“片面下线”。由于很有可能存在这么一个情况:那便是岗兵误判了,其实主库并没有故障。可是,一旦发动了主从切换,后续的选主和通知操作都会带来额定的核算和通讯开销。

那怎样削减误判呢?在日常生活中,当咱们要对一些重要的工作做判别的时分,经常会和家人或朋友一起商量一下,然后再做决定。岗兵机制也是相似的,它通常会选用多实例组成的集群形式进行布置,这也被称为岗兵集群。引进多个岗兵实例一起来判别,就能够防止单个岗兵由于本身网络情况欠好,而误判主库下线的情况。同时,多个岗兵的网络同时不稳定的概率较小,由它们一起做决议计划,误判率也能降低。在判别主库是否下线时,不能由一个岗兵说了算,只有大多数的岗兵实例,都判别主库现已“片面下线”了,主库才会被标记为“客观下线”,这个叫法也是表明主库下线成为一个客观事实了。这个判别原则便是:少数服从多数。

“客观下线”的标准便是,当有 N 个岗兵实例时,最好要有 N/2 + 1 个实例判别主库为“片面下线”,才能最终判定主库为“客观下线”。

浅谈Redis(二)——集群版

2.2 选主

当岗兵们判别主库客观下线的时分,就要开始下一个决议计划过程了,即从许多从库中,选出一个从库来做新主库。

一般来说,我把岗兵挑选新主库的过程称为“挑选 + 打分”。

浅谈Redis(二)——集群版

挑选条件: 网络连接状况(现在 + 之前)。在选主时,除了要查看从库的当时在线状况,还要判别它之前的网络连接状况。假如从库总是和主库断连,并且断连次数超出了一定的阈值,咱们就有理由信任,这个从库的网络情况并不是太好,就能够把这个从库筛掉了。

打分:

  • 首要,优先级最高的从库得分高。用户能够手动设置从库的优先级。
  • 其次,和旧主库同步程度最接近的从库得分高。
  • 最后,有一个默许的规则:在优先级和仿制进展都相同的情况下,ID 号最小的从库得分最高,会被选为新主库。

岗兵也是集群形式,也要确保服务的可用性,所以也会有相应的确保机制。这儿不在本篇的 Redis 集群讨论规模之内,所以就不打开描绘了。

三、 Redis Cluster

看到这儿,还记得咱们一开始发现的 Redis 单机版的两个问题吗?可用性数据臃肿

上面主从形式 + 岗兵形式足以确保 Redis 服务的可用性,可是看起来并没有处理数据臃肿的问题, Redis Cluster 就为咱们供给了处理这个问题的计划。

3.1 数据切片

切片集群,也叫分片集群,便是指发动多个 Redis 实例组成一个集群,然后依照一定的规则,把收到的数据划分成多份,每一份用一个实例来保存。

这样单份数据能够缩小到备份不影响该实例的主进程,并且还能够处理硬件本钱问题。

那数据都分散了,用户需求查某一条数据,Redis 怎样知道这条数据在哪个实例呢

咱们要先弄理解切片集群和 Redis Cluster 的联系与区别。 切片集群是一种保存很多数据的通用机制,这个机制能够有不同的完成计划。 Redis Cluster 计划选用 hash 槽(Hash Slot,接下来我会直接称之为 Slot),来处理数据和实例之间的映射联系。在 Redis Cluster 计划中,一个切片集群共有 16384 个 hash 槽,这些 hash 槽相似于数据分区,每个键值对都会根据它的 key,被映射到一个 hash 槽中。具体的映射过程分为两大步:

  • 首要根据键值对的 key,依照CRC16 算法核算一个 16 bit 的值;
  • 然后,再用这个 16bit 值对 16384 取模,得到 0~16383 规模内的模数,每个模数代表一个相应编号的 hash 槽。

关于 CRC16 算法,不是咱们讨论的要点。咱们只需求每一个 key 都能算出来一个 0~16383 的数就好。

每一个 0~16383 的数被称为一个slot(槽)。一个 Redis 实例有多个槽,集群中的一切 Redis 实例的槽加起来一定等于 16384。换句话说,在手动分配 hash 槽时,需求把 16384 个槽都分配完,否则 Redis 集群无法正常工作。

浅谈Redis(二)——集群版

3.2 客户端怎样定位数据?

在定位键值对数据时,它所处的 hash 槽是能够经过核算得到的,这个核算能够在客户端发送恳求时来履行。可是,要进一步定位到实例,还需求知道 hash 槽分布在哪个实例上。

Redis 实例会把自己的 hash 槽信息发给和它相连接的其它实例,来完成 hash 槽分配信息的扩散,Redis集群选用P2P的Gossip(谣言)协议, Gossip协议工作原理便是节点彼此不断通讯交流信息,一段时刻后一切的节点都会知道集群完好的信息,这种方法相似谣言传播 当实例之间相互连接后,每个实例就有一切 hash 槽的映射联系了。相当于每一个实例都有一份 slot 与实例地址的映射联系表

当客户端把一个键值对的操作恳求发给一个实例时,假如这个实例上并没有这个键值对映射的 hash 槽,那么,这个实例就会给客户端回来下面的 MOVED 指令呼应成果,这个成果中就包含了新实例的拜访地址。

GET hello:key
(error) MOVED 13320 192.168.19.5:6379

浅谈Redis(二)——集群版

如上图,用户连接上任意一个 Redis 实例,都能够查询集群中一切的key。比方连上实例2,查询key = “hello”。实例2会根据key核算 hash 槽的值。

  • 假如槽就在实例2上,则直接查询成果回来;
  • 假如不在实例2上,则回来存在key的实例地址。

客户端也会缓存恳求过的key对应实例的地址。

假如实例槽有变动,比方手动增删了几个实例,剩下的实例还是能够主动同步新的 hash 槽对应的实例方位。

3.3 实际使用

Redis Cluster 形式处理了数据臃肿的问题,可是独自的 Redis Cluster 形式并没有可用性,假如任意一个实例断电或者断网,这个实例一切 hash 槽的数据就会处于不可用的状况。

所以在实际运用场景下,上述的三种集群形式都会结合起来。

浅谈Redis(二)——集群版

浅谈Redis(二)——集群版

到这儿咱们就处理了开篇提出的两个问题。

  • 确保可用性: cluster node 中的主从同步和岗兵选举机制
  • 防止数据臃肿:cluster node 集群的数据切片

四、小结

开篇咱们提出的单机版 Redis 作为数据库的两个弊端。然后介绍了 Redis 官方的集群计划分别是怎样处理这两个弊端的。

其实除了 Redis 官方供给的集群计划,也有很多优异的开源 Redis 集群方法。 比方:

推特的 twemproxy

国内豌豆荚出品的 Codis

这两种开源计划和 Redis 官方的集群计划最大的区别便是数据节点的办理方法

  • Redis 是去中心化,hash 槽和节点的映射每个节点都会有保存,经过 Gossip 协议传播;
  • 这两种计划是中心化。有独自的署理节点办理hash 槽和节点的映射。

那去中心化和中心化咱们又该怎样挑选呢?