1. Redis介绍
www.redis.io
redis是一个基于内存的K-V存储数据库。支持存储的类型有string,list,set,zset(sorted set),hash等。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。redis支持各种不同方式的排序。保证效率的情况下,数据缓存在内存中。同时redis提供了持久化策略,不同的策略触发同步到磁盘或者把修改操作写入追加的记录文件,在此基础上实现了master-slave。
它是一个高性能的存储系统,能支持超过 100K+ 每秒的读写频率。同时还支持消息的发布/订阅,从而让你在构建高性能消息队列系统时多了另一种选择。
Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。
2. 主从
Redis支持master-slave模式,一主多从,redis server可以设置另外多个redis server为slave,从机同步主机的数据。配置后,读写分离,主机负责读写服务,从机只负责读。减轻主机的压力。redis实现的是最终会一致性,具体选择强一致性还是弱一致性,取决于业务场景。
Redis 主从同步有两种方式(或者所两个阶段):全同步和部分同步。
主从刚刚连接的时候,进行全同步;全同步结束后,进行部分同步。当然,如果有需要,slave 在任何时候都可以发起全同步。redis 策略是,无论如何,首先会尝试进行部分同步,如不成功,要求从机进行全同步,并启动 BGSAVE……BGSAVE 结束后,传输 RDB 文件;如果成功,允许从机进行部分同步,并传输积压空间中的数据。简单来说,主从同步就是 RDB 文件的上传下载;主机有小部分的数据修改,就把修改记录传播给每个从机。
3. Redis集群
主从模式存在的问题是,master宕机之后,从机只能读,不可写,不能保证高可用。redis集群技术是构建高性能网站架构的重要手段,试想在网站承受高并发访问压力的同时,还需要从海量数据中查询出满足条件的数据,并快速响应,我们必然想到的是将数据进行切片,把数据根据某种规则放入多个不同的服务器节点,来降低单节点服务器的压力。
Redis Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。节点之间使用gossip协议传播信息以及发现新节点。
Redis 集群是一个分布式(distributed)、容错(fault-tolerant)的 Redis 实现,集群可以使用的功能是普通单机 Redis 所能使用的功能的一个子集(subset)。
Redis 集群中不存在中心(central)节点或者代理(proxy)节点,集群的其中一个主要设计目标是达到线性可扩展性(linear scalability)。
Redis 集群为了保证一致性(consistency)而牺牲了一部分容错性:系统会在保证对网络断线(net split)和节点失效(node failure)具有有限(limited)抵抗力的前提下,尽可能地保持数据的一致性。
4. 安装部署
Redis安装较为简单,官网下载压缩包解压。集群模式需要ruby的编译环境,集群最小的配置为3台master,小于3则启动集群报错。
Redis版本:3.2.4
4.1 主从模式拓扑图
主从模式拓扑图
主从模式采用一主三从,主从都配置auth认证,读写分离。
主要实验的动作:
1)多个app 同时写,测定写速率;
2)多个app 同时写,同时有读的进程,测定读写速率;
3)master主机宕机,app依然进行读写。
4.2 Cluster拓扑图如下
cluster
集群模式采用四主四从,也是采用读写分离。
主要实验的动作:
1)有一个master宕机,观察日志,新的slave成为master;
2)master宕机后,重新启动,master成为slave;
3)集群全部宕机,redis主机重启,数据未丢失。
5. 原理
5.1 一致性
filesnapshot:默认redis是会以快照的形式将数据持久化到磁盘,在配置文件中的格式是:save N M表示在N秒之内,redis至少发生M次修改则redis抓快照到磁盘。
工作原理:当Redis需要做持久化时,Redis会fork一个子进程;子进程将数据写到磁盘上一个临时RDB文件中;当子进程完成写临时文件后,将原来的RDB替换掉,这样的好处就是可以copy-on-write。
Append-only:filesnapshotting方法在redis异常死掉时, 最近的数据会丢失(丢失数据的多少视你save策略的配置),所以这是它最大的缺点,当业务量很大时,丢失的数据是很多的。Append-only方法可 以做到全部数据不丢失,但redis的性能就要差些。AOF就可以做到全程持久化,只需要在配置文件中开启(默认是no),appendonly yes开启AOF之后,redis每执行一个修改数据的命令,都会把它添加到aof文件中,当redis重启时,将会读取AOF文件进行“重放”以恢复到 redis关闭前的最后时刻。
AOF文件刷新的方式,有三种,参考配置参数appendfsync :
appendfsync always每提交一个修改命令都调用fsync刷新到AOF文件,非常非常慢,但也非常安全;
appendfsync everysec每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒以内的数据;
appendfsync no依靠OS进行刷新,redis不主动刷新AOF,这样最快,但安全性就差。默认并推荐每秒刷新,这样在速度和安全上都做到了兼顾。
Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。因此我们可以将Redis的Replication架构视为图结构。
Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。
Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据。
为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成。即便如此,系统的伸缩性还是得到了很大的提高。
Master可以将数据保存操作交给Slaves完成,从而避免了在Master中要有独立的进程来完成此操作。
Redis在master是非阻塞模式,也就是说在slave执行数据同步的时候,master是可以接受客户端的请求的,并不影响同步数据的一致性,然而在slave端是阻塞模式的,slave在同步master数据时,并不能够响应客户端的查询。
5.2 Replication的工作原理
(1)Slave服务器连接到Master服务器。
(2)Slave服务器发送SYCN命令。
(3)Master服务器备份数据库到.rdb文件。
(4)Master服务器把.rdb文件传输给Slave服务器。
(5)Slave服务器把.rdb文件数据导入到数据库中。
在Slave启动并连接到Master之后,它将主动发送一个SYNC命令。此后Master将启动后台存盘进程,同时收集所有接收到的用于修改数据集 的命令,在后台进程执行完毕后,Master将传送整个数据库文件到Slave,以完成一次完全同步。而Slave服务器在接收到数据库文件数据之后将其 存盘并加载到内存中。此后,Master继续将所有已经收集到的修改命令,和新的修改命令依次传送给Slaves,Slave将在本次执行这些数据修改命令,从而达到最终的数据同步。如果Master和Slave之间的链接出现断连现象,Slave可以自动重连Master,但是在连接成功之后,一次完全同步将被自动执行。
5.3 一致性哈希
集群要实现的目的是要将不同的 key 分散放置到不同的 redis 节点,这里我们需要一个规则或者算法,通常的做法是获取 key 的哈希值,然后根据节点数来求模,但这种做法有其明显的弊端,当我们需要增加或减少一个节点时,会造成大量的 key 无法命中,这种比例是相当高的,所以就有人提出了一致性哈希的概念。
一致性哈希有四个重要特征:
均衡性:也有人把它定义为平衡性,是指哈希的结果能够尽可能分布到所有的节点中去,这样可以有效的利用每个节点上的资源。
单调性:对于单调性有很多翻译让我非常的不解,而我想要的是当节点数量变化时哈希的结果应尽可能的保护已分配的内容不会被重新分派到新的节点。
分散性和负载:这两个其实是差不多的意思,就是要求一致性哈希算法对 key 哈希应尽可能的避免重复。
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
使用哈希槽的好处就在于可以方便的添加或移除节点。
1. 当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;
2. 当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;
3. 当设置了主从关系后,slave 在第一次连接或者重新连接 master 时,slave 都会发送一条同步指令给 master;
4. master 接到指令后,开始启动后台保存进程保存数据,接着收集所有的数据修改指令。后台保存完了,master 就把这份数据发送给 slave,slave 先把数据保存到磁盘,然后把它加载到内存中,master 接着就把收集的数据修改指令一行一行的发给 slave,slave 接收到之后重新执行该指令,这样就实现了数据同步。
5. slave 在与 master 失去联系后,自动的重新连接。如果 master 收到了多个 slave 的同步请求,它会执行单个后台保存来为所有的 slave 服务。
5.4 节点失效检测
以下是节点失效检查的实现方法:
1. 当一个节点向另一个节点发送 PING 命令,但是目标节点未能在给定的时限内返回 PING 命令的回复时,那么发送命令的节点会将目标节点标记为PFAIL (possible failure,可能已失效)。
2. 等待 PING 命令回复的时限称为“节点超时时限(node timeout)”,是一个节点选项(node-wise setting)。
3. 每次当节点对其他节点发送 PING 命令的时候,它都会随机地广播三个它所知道的节点的信息,这些信息里面的其中一项就是说明节点是否已经被标记为 PFAIL 或者 FAIL 。
4. 当节点接收到其他节点发来的信息时,它会记下那些被其他节点标记为失效的节点。这称为失效报告(failure report)。
5. 如果节点已经将某个节点标记为 PFAIL ,并且根据节点所收到的失效报告显式,集群中的大部分其他主节点也认为那个节点进入了失效状态,那么节点会将那个失效节点的状态标记为 FAIL 。
6. 一旦某个节点被标记为 FAIL ,关于这个节点已失效的信息就会被广播到整个集群,所有接收到这条信息的节点都会将失效节点标记为 FAIL 。
简单来说,一个节点要将另一个节点标记为失效,必须先询问其他节点的意见,并且得到大部分主节点的同意才行。因为过期的失效报告会被移除,所以主节点要将某个节点标记为 FAIL 的话,必须以最近接收到的失效报告作为根据。
在以下两种情况中,节点的 FAIL 状态会被移除:
1. 如果被标记为 FAIL 的是从节点,那么当这个节点重新上线时, FAIL 标记就会被移除。保持(retaning)从节点的 FAIL 状态是没有意义的,因为它不处理任何槽,一个从节点是否处于 FAIL 状态,决定了这个从节点在有需要时能否被提升为主节点。
2. 如果一个主节点被打上 FAIL 标记之后,经过了节点超时时限的四倍时间,再加上十秒钟之后,针对这个主节点的槽的故障转移操作仍未完成,并且这个主节点已经重新上线的话,那么移除对这个节点的 FAIL 标记。
3. 在第二种情况中,如果故障转移未能顺利完成,并且主节点重新上线,那么集群就继续使用原来的主节点,从而免去管理员介入的必要。
5.5 从节点选举
一旦某个主节点进入 FAIL 状态,如果这个主节点有一个或多个从节点存在,那么其中一个从节点会被升级为新的主节点,而其他从节点则会开始对这个新的主节点进行复制。
新的主节点由已下线主节点属下的所有从节点中自行选举产生,以下是选举的条件:
1. 这个节点是已下线主节点的从节点。
2. 已下线主节点负责处理的槽数量非空。
3. 从节点的数据被认为是可靠的,也即是,主从节点之间的复制连接(replication link)的断线时长不能超过节点超时时限(node timeout)乘以REDIS_CLUSTER_SLAVE_VALIDITY_MULT 常量得出的积。
如果一个从节点满足了以上的所有条件,那么这个从节点将向集群中的其他主节点发送授权请求,询问它们,是否允许自己(从节点)升级为新的主节点。
如果发送授权请求的从节点满足以下属性,那么主节点将向从节点返回 FAILOVER_AUTH_GRANTED 授权,同意从节点的升级要求:
1. 发送授权请求的是一个从节点,并且它所属的主节点处于 FAIL 状态。
2. 在已下线主节点的所有从节点中,这个从节点的节点 ID 在排序中是最小的。
3. 从节点处于正常的运行状态:它没有被标记为 FAIL 状态,也没有被标记为 PFAIL 状态。
一旦某个从节点在给定的时限内得到大部分主节点的授权,它就会开始执行以下故障转移操作:
1. 通过 PONG 数据包(packet)告知其他节点,这个节点现在是主节点了。
2. 通过 PONG 数据包告知其他节点,这个节点是一个已升级的从节点(promoted slave)。
3. 接管(claiming)所有由已下线主节点负责处理的哈希槽。
4. 显式地向所有节点广播一个 PONG 数据包,加速其他节点识别这个节点的进度,而不是等待定时的 PING / PONG 数据包。
5. 所有其他节点都会根据新的主节点对配置进行相应的更新,特别地:
a. 所有被新的主节点接管的槽会被更新。
b. 已下线主节点的所有从节点会察觉到 PROMOTED 标志,并开始对新的主节点进行复制。
c.如果已下线的主节点重新回到上线状态,那么它会察觉到 PROMOTED 标志,并将自身调整为现任主节点的从节点。
在集群的生命周期中,如果一个带有 PROMOTED 标识的主节点因为某些原因转变成了从节点,那么该节点将丢失它所带有的 PROMOTED 标识。
6. 总结
Redis集群具有高可用,易于迁移,存取速度快等特点。也可以作为消息队列使用,支持pub/sub模式,具体优缺点总结如下:
首先优点:
1. Redis 在主节点下线后,从节点会自动提升为主节点,提供服务
2. redis 宕机节点恢复后,自动会添加到集群中,变成从节点
3. 动态扩展和删除节点,rehash slot的分配,基于桶的数据分布方式大大降低了迁移成本,只需将数据桶从一个Redis Node迁移到另一个Redis Node即可完成迁移。
4. Redis Cluster使用异步复制。
其缺点为:
1. 由于 Redis 的复制使用异步机制,在自动故障转移的过程中,集群可能会丢失写命令。然而 Redis 几乎是同时执行(将命令恢复发送给客户端,以及将命令复制到从节点)这两个操作,所以实际中,命令丢失的窗口非常小。
2. 普通的主从模式支持auth加密认证,虽然比较弱,但写或者读都要通过密码验证,cluster对密码支持不太友好,如果对集群设置密码,那么requirepass和masterauth都需要设置,否则发生主从切换时,就会遇到授权问题,可以模拟并观察日志。
参考资料:
1. www.redis.io
2. redis-cluster研究和使用(http://hot66hot.iteye.com/blog/2050676)
3. Redis Cluster 3.0搭建与使用(http://www.cnblogs.com/gomysql/p/4395504.html)