前言
之前简略分享了一下单机版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 首要存在以下两个问题:
- 可用性(断电后,事务不可用)
- 数据臃肿(单机CPU对很多数据的操作)
- 备份堵塞导致服务不可用
- 本钱指数增加
那怎样办呢?单机不够用,就多加几台机器嘛!下面咱们来看一下 Redis 供给的在多机环境下几种常见的资源运用战略,看看能否处理上述的两个问题
一、主从同步(Master-Slave)
主从同步便是,多个 Redis 节点,一个主节点(master),多个副节点(slave)。读恳求会发送到各个节点,写恳求只会发给 master,然后 slave 定时会从 master 拉取新增的写操作以确保数据的一致性。
那怎样从单机版加一个 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 文件内容之后的操作都是增量同步进行。
之后便是重复1和3的过程。
二、岗兵机制(Sentinel)
上面讲的主从同步只是确保了 Redis 的数据有及时备份。能够分管一部分主库的拜访压力,以及确保了从库的可用性。可是假如主库发生故障了,那就直接会影响到从库的同步,由于从库没有相应的主库能够进行数据仿制操作了。
岗兵(Sentinel)便是为了处理这个问题而产生的。它完成了主从库主动切换的关键机制,它有效地处理了主从仿制形式下故障转移的三个问题。
- 主库真的挂了吗?
- 该挑选哪个从库作为主库?
- 怎样把新主库的相关信息通知给从库和客户端呢?
下面咱们就从这三个问题来看 Sentinel 对应的三种职能,监控、选主和通知。
2.1 监控
岗兵需求判别主库是否处于下线状况。这儿要先说两个概念,即片面下线和客观下线,
岗兵进程会运用 PING 指令检测它自己和主、从库的网络连接情况,用来判别实例的状况。假如岗兵发现主库呼应超时了,那么,岗兵就会先把它标记为“片面下线”。假如检测的是从库,那么,岗兵简略地把它标记为片面下线就行了,由于从库的下线影响一般不太大,集群的对外服务不会间断。
假如检测的是主库,那么,岗兵还不能简略地把它标记为“片面下线”。由于很有可能存在这么一个情况:那便是岗兵误判了,其实主库并没有故障。可是,一旦发动了主从切换,后续的选主和通知操作都会带来额定的核算和通讯开销。
那怎样削减误判呢?在日常生活中,当咱们要对一些重要的工作做判别的时分,经常会和家人或朋友一起商量一下,然后再做决定。岗兵机制也是相似的,它通常会选用多实例组成的集群形式进行布置,这也被称为岗兵集群。引进多个岗兵实例一起来判别,就能够防止单个岗兵由于本身网络情况欠好,而误判主库下线的情况。同时,多个岗兵的网络同时不稳定的概率较小,由它们一起做决议计划,误判率也能降低。在判别主库是否下线时,不能由一个岗兵说了算,只有大多数的岗兵实例,都判别主库现已“片面下线”了,主库才会被标记为“客观下线”,这个叫法也是表明主库下线成为一个客观事实了。这个判别原则便是:少数服从多数。
“客观下线”的标准便是,当有 N 个岗兵实例时,最好要有 N/2 + 1 个实例判别主库为“片面下线”,才能最终判定主库为“客观下线”。
2.2 选主
当岗兵们判别主库客观下线的时分,就要开始下一个决议计划过程了,即从许多从库中,选出一个从库来做新主库。
一般来说,我把岗兵挑选新主库的过程称为“挑选 + 打分”。
挑选条件: 网络连接状况(现在 + 之前)。在选主时,除了要查看从库的当时在线状况,还要判别它之前的网络连接状况。假如从库总是和主库断连,并且断连次数超出了一定的阈值,咱们就有理由信任,这个从库的网络情况并不是太好,就能够把这个从库筛掉了。
打分:
- 首要,优先级最高的从库得分高。用户能够手动设置从库的优先级。
- 其次,和旧主库同步程度最接近的从库得分高。
- 最后,有一个默许的规则:在优先级和仿制进展都相同的情况下,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 集群无法正常工作。
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 实例,都能够查询集群中一切的key。比方连上实例2,查询key = “hello”。实例2会根据key核算 hash 槽的值。
- 假如槽就在实例2上,则直接查询成果回来;
- 假如不在实例2上,则回来存在key的实例地址。
客户端也会缓存恳求过的key对应实例的地址。
假如实例槽有变动,比方手动增删了几个实例,剩下的实例还是能够主动同步新的 hash 槽对应的实例方位。
3.3 实际使用
Redis Cluster 形式处理了数据臃肿的问题,可是独自的 Redis Cluster 形式并没有可用性,假如任意一个实例断电或者断网,这个实例一切 hash 槽的数据就会处于不可用的状况。
所以在实际运用场景下,上述的三种集群形式都会结合起来。
到这儿咱们就处理了开篇提出的两个问题。
- 确保可用性: cluster node 中的主从同步和岗兵选举机制
- 防止数据臃肿:cluster node 集群的数据切片
四、小结
开篇咱们提出的单机版 Redis 作为数据库的两个弊端。然后介绍了 Redis 官方的集群计划分别是怎样处理这两个弊端的。
其实除了 Redis 官方供给的集群计划,也有很多优异的开源 Redis 集群方法。 比方:
推特的 twemproxy
国内豌豆荚出品的 Codis
这两种开源计划和 Redis 官方的集群计划最大的区别便是数据节点的办理方法。
- Redis 是去中心化,hash 槽和节点的映射每个节点都会有保存,经过 Gossip 协议传播;
- 这两种计划是中心化。有独自的署理节点办理hash 槽和节点的映射。
那去中心化和中心化咱们又该怎样挑选呢?