redis 学习笔记三
一、redis 复制
数据库复制指的是发生在不同数据库实例之间,单向的信息传播的行为,通常由被复制方和复制方组成,被复制方和复制方之间建立网络连接,复制方式通常为被复制方主动将数据发送到复制方,复制方接收到数据存储在当前实例,最终目的是为了保证双方的数据一致、同步。
Redis复制方式:
一种是主(master)-从(slave)模式,一种是从(slave)-从(slave)模式,因此Redis的复制拓扑图会丰富一些,可以像星型拓扑,也可以像个有向无环:
通过配置多个Redis实例独立运行、定向复制,形成Redis集群,master负责写、slave负责读。
复制优点
通过配置多个Redis实例,数据备份在不同的实例上,主库专注写请求,从库负责读请求,这样的好处主要体现在下面几个方面:
1、高可用性
在一个Redis集群中,如果master宕机,slave可以介入并取代master的位置,因此对于整个Redis服务来说不至于提供不了服务,这样使得整个Redis服务足够安全。
2、高性能
在一个Redis集群中,master负责写请求,slave负责读请求,这么做一方面通过将读请求分散到其他机器从而大大减少了master服务器的压力,另一方面slave专注于提供读服务从而提高了响应和读取速度。
3、水平扩展性
通过增加slave机器可以横向(水平)扩展Redis服务的整个查询服务的能力。
复制缺点
复制提供了高可用性的解决方案,但同时引入了分布式计算的复杂度问题,认为有两个核心问题:
-
数据一致性问题,如何保证master服务器写入的数据能够及时同步到slave机器上。
-
编程复杂,如何在客户端提供读写分离的实现方案,通过客户端实现将读写请求分别路由到master和slave实例上。
上面两个问题,尤其是第一个问题是Redis服务实现一直在演变,致力于解决的一个问题。
复制实时性和数据一致性矛盾
Redis提供了提高数据一致性的解决方案,一致性程度的增加虽然使得我能够更信任数据,但是更好的一致性方案通常伴随着性能的损失,从而减少了吞吐量和服务能力。然而我们希望系统的性能达到最优,则必须要牺牲一致性的程度,因此Redis的复制实时性和数据一致性是存在矛盾的。
Redis复制工作过程:
-
slave向master发送sync命令。
-
master开启子进程来讲dataset写入rdb文件,同时将子进程完成之前接收到的写命令缓存起来。
-
子进程写完,父进程得知,开始将RDB文件发送给slave。
-
master发送完RDB文件,将缓存的命令也发给slave。
-
master增量的把写命令发给slave。
二、redis集群
1、Redis Sharding集群,客户端分片
客户端分片是把分片的逻辑放在Redis客户端实现,通过Redis客户端预先定义好的路由规则,把对Key的访问转发到不同的Redis实例中,最后把返回结果汇集
优点:
- 所有的逻辑都是可控的,不依赖于第三方分布式中间件。开发人员清楚怎么实现分片、路由的规则,不用担心踩坑。
- 这种分片性能比代理式更好(因为少了分发环节),分发压力在客户端,无服务端压力增加
缺点:
-
静态的分片方案,需要增加或者减少Redis实例的数量,需要手工调整分片的程序。
-
不能平滑地水平扩容,扩容/缩容时,必须手动调整分片程序,出现故障不能自动转移,难以运维
2、Twemproxy
优点:
- 运维成本低。业务方不用关心后端 Redis 实例,跟操作 Redis 一样。Proxy 的逻辑和存储的逻辑是隔离的
- 支持无效Redis实例的自动删除。
- Twemproxy与Redis实例保持连接,减少了客户端与Redis实例的连接数。
缺点:
-
a. 代理层多了一次转发,性能有所损耗
-
b. 进行扩容/缩容时候,部分数据可能会失效,需要手动进行迁移,对运维要求较高,而且难以做到平滑的扩缩容
-
c. 出现故障,不能自动转移,运维性很差
3、Codis
优点:
- 支持平滑增加(减少)Redis Server Group(Redis实例),能安全、透明地迁移数据,这也是Codis 有别于Twemproxy等静态分布式 Redis 解决方案的地方
缺点:
- 需要更改redis的源代码(为了加入slot信息) 以及代理实现本身会有的问题
codis改进了一下redis缺点:
1 redis数据量太大的话(22G以上),他的处理性能就开始下降;
2 无法区分冷热数据,内存浪费严重;
3 RDB Block住整个服务;
4 写操作太频繁,AOF刷盘太多,很容易rewrite
4、Redis Cluster
优点:
-
无中心节点
-
数据按照 Slot 存储分布在多个 Redis 实例上
-
平滑的进行扩容/缩容节点
-
自动故障转移(节点之间通过 Gossip 协议交换状态信息,进行投票机制完成 Slave 到 Master 角色的提升)
- 降低运维成本,提高了系统的可扩展性和高可用性
缺点:
- 严重依赖外部 Redis-Trib
- 缺乏监控管理
- 需要依赖 Smart Client(连接维护, 缓存路由表, MultiOp 和 Pipeline 支持)
- Failover 节点的检测过慢,不如“中心节点 ZooKeeper”及时
- Gossip 消息的开销
- 无法根据统计区分冷热数据
- Slave“冷备”,不能缓解读压力
实际项目中该如何选型呢?
方案 1 使用nginx开发(OpenResty方式)
原因如下
a. 单 Master 多 Work 模式,每个 Work 跟 Redis 一样都是单进程单线程模式,并且都是基
于 Epoll 事件驱动的模式。
b. Nginx 采用了异步非阻塞的方式来处理请求,高效的异步框架。
c. 内存占用少,有自己的一套内存池管理方式,。将大量小内存的申请聚集到一块,能够比
Malloc 更快。减少内存碎片,防止内存泄漏。减少内存管理复杂度。
d. 为了提高 Nginx 的访问速度,Nginx 使用了自己的一套连接池。
e. 最重要的是支持自定义模块开发。
f. 业界内,对于 Nginx,Redis 的口碑可称得上两大神器。性能也就不用说了。
方案 2 codis (豌豆荚采用的基于代理的redis集群方案)
参考codis官方文档https://github.com/CodisLabs/codis
Codis是一整套缓存解决方案,包含高可用、数据分片、监控、动态扩态 etc.。
走的是 Apps->代理->redis cluster,一定规模后基本都采用这种方式。
方案3 自己独立开发redis智能客户端
主要实现redis slots管理,failover,一致性hash功能。
三、sentinel
Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自懂切换。
它的主要功能有以下几点
-
不时地监控redis是否按照预期良好地运行;
-
如果发现某个redis节点运行出现状况,能够通知另外一个进程(例如它的客户端);
-
能够进行自动切换。当一个master节点不可用时,能够选举出master的多个slave(如果有超过一个slave的话)中的一个来作为新的master,其它的slave节点会将它所追随的master的地址改为被提升为master的slave的新地址。
Sentinel支持集群
很显然,只使用单个sentinel进程来监控redis集群是不可靠的,当sentinel进程宕掉后(sentinel本身也有单点问题,single-point-of-failure)整个集群系统将无法按照预期的方式运行。所以有必要将sentinel集群,这样有几个好处:
- 即使有一些sentinel进程宕掉了,依然可以进行redis集群的主备切换;
- 如果只有一个sentinel进程,如果这个进程运行出错,或者是网络堵塞,那么将无法实现redis集群的主备切换(单点问题);
- 如果有多个sentinel,redis的客户端可以随意地连接任意一个sentinel来获得关于redis集群中的信息。
Redis Sentinel节点推荐3个以上。相比keepalived+redis实现高可用更靠谱,且keepalived+redis还不能管理多个实例
原理:
①sentinel集群通过给定的配置文件发现master,启动时会监控master。通过向master发送info信息获得该服务器下面的所有从服务器。
②sentinel集群通过命令连接向被监视的主从服务器发送hello信息(每秒一次),该信息包括sentinel本身的ip、端口、id等内容,以此来向其他sentinel宣告自己的存在。
③sentinel集群通过订阅连接接收其他sentinel发送的hello信息,以此来发现监视同一个主服务器的其他sentinel;集群之间会互相创建命令连接用于通信,因为已经有主从服务器作为发送和接收hello信息的中介,sentinel之间不会创建订阅连接。
④sentinel集群使用ping命令来检测实例的状态,如果在指定的时间内(down-after-milliseconds)没有回复或则返回错误的回复,那么该实例被判为下线。
⑤当failover主备切换被触发后,failover并不会马上进行,还需要sentinel中的大多数sentinel授权后才可以进行failover,即进行failover的sentinel会去获得指定quorum个的sentinel的授权,成功后进入ODOWN状态。如在5个sentinel中配置了2个quorum,等到2个sentinel认为master死了就执行failover。
⑥sentinel向选为master的slave发送SLAVEOF NO ONE
命令,选择slave的条件是sentinel首先会根据slaves的优先级来进行排序,优先级越小排名越靠前。如果优先级相同,则查看复制的下标,哪个从master接收的复制数据多,哪个就靠前。如果优先级和下标都相同,就选择进程ID较小的。
⑦sentinel被授权后,它将会获得宕掉的master的一份最新配置版本号(config-epoch),当failover执行结束以后,这个版本号将会被用于最新的配置,通过广播形式通知其它sentinel,其它的sentinel则更新对应master的配置。
①到③是自动发现机制:
- 以10秒一次的频率,向被监视的master发送info命令,根据回复获取master当前信息。
- 以1秒一次的频率,向所有redis服务器、包含sentinel在内发送PING命令,通过回复判断服务器是否在线。
- 以2秒一次的频率,通过向所有被监视的master,slave服务器发送当前sentinel,master信息的消息。
④是检测机制,⑤和⑥是failover机制,⑦是更新配置机制。
注意:因为redis采用的是异步复制,没有办法避免数据的丢失。但可以通过以下配置来使得数据不会丢失:min-slaves-to-write 1 、 min-slaves-max-lag 10。一个redis无论是master还是slave,都必须在配置中指定一个slave优先级。要注意到master也是有可能通过failover变成slave的。如果一个redis的slave优先级配置为0,那么它将永远不会被选为master,但是它依然会从master哪里复制数据。