Redis复制和哨兵

Redis学习笔记系列博客


一,为什么要集群

如果是小项目,一台Redis服务器就已经非常足够了,然而实际中很多大项目需要若干台Redis服务器的支持:

  1. 从结构上,单个Redis服务器会发生单点故障,同时一台服务器需要承受所有的请求负载。这就需要为数据生成多个副本并分配在不同的服务器上。
  2. 从容量上,单个Redis服务器的内存非常容易成为存储瓶颈,所以需要进行数据分片。

同时拥有多个Redis服务器后就面临如何管理集群的问题,包括如何增加节点。故障恢复等操作。


二,复制

为了避免单点故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。为此,Redis提供了复制(repliaction)功能,可以实现一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。

1,配置

在复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据看(slave)。主数据库可以进行读写操作,当写操作导致数据变化时自动将数据同步给从数据库。而从数据库一般是只读的,并接受主同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
这里写图片描述

在Redis中使用复制功能非常容易,只需要在从数据库的配置文件加入“slaveof 主数据库地址 主数据库端口”即可,主数据无需进行任何配置。
为了能够更能直观地展示复制流程,下面实现一个最简化的复制系统。我们要在一台服务器上启动两个Redis实例,监听不同的端口,其中一个作为主数据库,另一个作为从数据库。首先我们不加任何参数启动一个Redis实例作为主数据库

redis-server

该实例默认监听6379端口,然后加上slaveof参数启动另一个Redis实例作为从数据库,并让其监听6380端口

redis-server redis.conf --port 6380 --slaveof 127.0.0.1 6379

此时在主数据库中的任何数据库变化都会自动同步到从数据库中。

此时我们通过redis-cli -p 6379连接主数据库,redis-cli -p 6380连接从数据库。

这时我们使用info命令来分别在主数据库和从数据库中获取Replication节的相关信息。
主数据库:

D:\Redis>redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=337,lag=0
master_repl_offset:337
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:336
127.0.0.1:6379>

可以看到,实例的角色role是master,即主数据库,同时已连接的从数据库(connected_slaves)的个数为1.

从数据库:

D:\Redis>redis-cli -p 6380
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:589
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6380>

这里可以看到,实例的role是slave,即从数据库,同时其主数据的地址为127.0.0.1,端口为6379。

在主数据库中使用set命令设置一个键的值

D:\Redis>redis-cli -p 6379
127.0.0.1:6379> set test 1
OK
127.0.0.1:6379>

此时在从数据库中就可以获得该值了。

127.0.0.1:6380> get test
"1"
127.0.0.1:6380>

默认情况下,从数据库时只读的,如果直接改从数据库的数据会出现错误:

127.0.0.1:6380> set test 2
(error) READONLY You can't write against a read only slave.
127.0.0.1:6380>

可以通过设置从数据库的配置文件中的slave-read-only为no以使从数据库可写,但是因为对从数据库的任何更改都不会同步给任何其他数据库,并且一旦主数据库中更新了对应的数据就会覆盖从数据库的改动,所以通常的场景下不应该设置从数据库可写,以免导致易被忽略的潜在应用逻辑错误。
配置多台从数据库的方法也一样,在所有的从数据库配置文件中都加上slaveof参数指向同一主数据库即可。
除了通过配置文件或命令参数设置slaveof参数,还可以在运行时使用slaveof命令修改:

redis> slaveof 127.0.0.1 6379

如果该数据库已经是其他主数据库的从数据库了,slaveof命令会停止和原来数据库的同步转而和新数据库同步。此外对于从数据库来说,还可以使用slave no one命令来使当前数据库停止接受其他数据库的同步并转换成为主数据库。

2,原理

Redis采用了乐观复制(optimistic replication)的复制策略,容忍在一定时间内主从数据库的内容是不同的,但是两者的数据会最终同步。具体来说,Redis在主从数据库之间复制数据的过程本身是异步的,这意味着,主数据库执行完客户端请求的命令后会立即将命令在主数据库的执行结果返回给客户端,并异步地将命令同步给从数据库,而不会等待从数据库接收到命令后再返回给客户端,需要注意的是在同步的过程中从数据库并不会阻塞,而是可以继续处理客户端发来的命令。这一特性保证了启用复制后主数据库的性能不会受影响,但另一方面也会产生一个主从数据库数据不一致的时间窗口,当主数据库执行了一条写命令后,主数据库的数据已经发生变动,然而在数据库将该命令传送给从数据库之前,如果两个数据库之间的网络连接断开了,此时二者之间的数据就会不一致的。

3,图结构

从数据库不仅可以接受主数据的同步数据,自己也可以同时作为主数据库存在,形成类型图的结构。
这里写图片描述

4,读写分离与一致性

通过复制可以实现读写分离,以提高服务器的负载能力。在常见的场景中(如电子商务网站),读的频率大于写,当单机的Redis无法应付大量的读请求时(尤其是较耗资源的请求,如sort命令)可以通过复制功能建立多个从数据库的节点,主数据库只进行写操作,而从数据库负责读操作。这种一主多从的结构适合读多写少的场景。


三,哨兵

之前为了方便,我使用的是Windows版本的Redis,但实际上Redis官方是不支持Windows系统的,而且Windows的3.2.100版本的Redis的安装目录下没有redis-sentienl工具,所以下面的示例将是基于Linux系统的4.0版本的Redis。

1,什么是哨兵

哨兵的作用就是监控Redis系统的运行状况。它的功能包括以下两点。

  1. 监控主数据库和从数据库是否正常运行。
  2. 主数据库出现故障自动将从数据库转换为主数据库。

哨兵是一个独立的进程,使用哨兵的一个典型架构如下:
这里写图片描述

在一个一主多从的Redis系统中,可以使用多个哨兵进行监控任务以保证系统足够稳健。此时不仅哨兵会同时监控主数据库和从数据库,哨兵之间也会互相监控。
这里写图片描述

2,部署

首先建立起3个Redis实例,其中包括一个主数据库和两个从数据库。主数据库的端口为6379,两个从数据库的端口分别为6380和6381。使用Redis命令客户端来获取复制状态,以保证复制配置正确。
首先是主数据库:

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=882,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=882,lag=0
master_replid:680a7a4706ecde8d9032884512cda8ccacd9f852
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:882
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:882
127.0.0.1:6379>

可见其连接了两个从数据库,配置正确。然后用同样的方法查看两个从数据库的配置:

127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:10
master_sync_in_progress:0
slave_repl_offset:980
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:680a7a4706ecde8d9032884512cda8ccacd9f852
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:980
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:980
127.0.0.1:6380>
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_repl_offset:1036
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:680a7a4706ecde8d9032884512cda8ccacd9f852
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6381>

当出现的信息如上时,即证明一主二从的复制配置已经成功了。
接下来开始配置哨兵。这里使用安装Redis时自带的配置文件sentinel.conf。打开sentinel.conf文件,可以看到这样一行配置:

sentinel monitor mymaster 127.0.0.1 6379 2

其中mymaster表示要监控的主数据库的名字,可以自己定义一个。这个名字必须仅由大小写字母、数字和“.-_”这3个字符组成。后两个参数表示主数据库的地址和端口号。最后的2表示最低通过票数,这里我们先暂时把2修改为1,后面会再介绍这个参数。所以最终的配置为

sentinel monitor mymaster 127.0.0.1 6379 1

启动哨兵

./redis-sentinel ../sentinel.conf

需要注意的是,配置哨兵监控一个系统时,只需要配置其监控主数据库即可,哨兵会自动发现所有复制该主数据库的从数据库。
哨兵的输出内容如下:

25621:X 16 Jul 13:37:36.370 # Sentinel ID is 4fe944c9db9b58576c8bdc679582e0718a22b244
25621:X 16 Jul 13:37:36.371 # +monitor master mymaster 127.0.0.1 6379 quorum 1
25621:X 16 Jul 13:37:36.374 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
25621:X 16 Jul 13:37:36.378 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379

其中+slave表示发现了从数据库,可见哨兵成功地发现了两个从数据库。现在哨兵已经在监控这3个Redis实例了,这时我们将主数据库(即运行在6379端口上的Redis实例)关闭(杀死进程或使用shutdown命令),等待指定时间后(可以配置)哨兵会输出如下内容:

31081:X 18 Jul 09:48:36.177 # +sdown master mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:36.177 # +odown master mymaster 127.0.0.1 6379 #quorum 1/1
31081:X 18 Jul 09:48:36.177 # +new-epoch 2
31081:X 18 Jul 09:48:36.178 # +try-failover master mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:36.181 # +vote-for-leader 4fe944c9db9b58576c8bdc679582e0718a22b244 2
31081:X 18 Jul 09:48:36.182 # +elected-leader master mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:36.182 # +failover-state-select-slave master mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:36.242 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:36.242 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:36.299 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:36.395 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:36.395 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:36.447 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:37.453 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:37.455 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:37.554 # +failover-end master mymaster 127.0.0.1 6379
31081:X 18 Jul 09:48:37.555 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6381
31081:X 18 Jul 09:48:37.555 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6381
31081:X 18 Jul 09:48:37.555 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381

其中+sdown表示哨兵主观认为主数据库停止服务了,而+odown则表示哨兵客观认为主数据库停止服务了。此时哨兵开始执行故障转移,即挑选一个从数据库,将其升格为主数据库。+try-failover表示哨兵开始执行故障转移,+failover-end表示哨兵完成故障转移,期间涉及的内容比较负责,包括领头哨兵的选举、备选从数据库的选择等。+switch-master表示主数据库从6379端口转移到了6381端口,即6381端口的从数据库被升格为主数据库,同时两个+slave则列出了新的主数据库的两个从数据库,端口分别为6380和6379.其中6379就是之前停止服务的主数据库,可见哨兵并没有彻底清除停止服务的实例的信息,这是因为停止服务的实例有可能会在之后的某个时间恢复服务,这时哨兵会让其重新加入进来,所以当实例停止服务后,哨兵会更新该实例的信息,使得当其重新加入后可以按照当前信息继续对外提供服务。

3,原理

一个哨兵节点可以同时监控多个Redis主从系统,只需要提供多个sentinel monitor配置即可,如下:

sentinel monitor mymaster 127.0.0.1 6381 1
sentinel monitor mymaster 192.168.184.131 6381 3

配置文件可以通过sentinel down-after-milliseconds <master-name> <milliseconds> 选项指定时间,当某个数据库超过指定时间未响应,哨兵则认为其主观下线(subjectively down)。如果该节点时主数据库,则哨兵会进一步判断是否需要对其进行故障转移,通过询问其他哨兵节点了解他们是否也认为该主数据库主观下线,入股达到指定数量时,哨兵会认为其客观下线(objectively down),并选举领头的哨兵节点对主从系统发起故障转移。这个指定数量即为前面说的quorum参数。例如下面的配置:

sentinel monitor mymaster 127.0.0.1 6379 2

该配置表示只有当至少两个sentinel节点(包括当前节点)认为主数据库主观下线时,当前哨兵节点才会认为该主数据库客观下线了。选举领头哨兵后,领头哨兵将会开始对主数据库进行故障转移。

4,哨兵的部署

哨兵以独立进程的方式对一个主从系统进行监控。如果一个主从系统中配置的哨兵较少,哨兵对整个系统的判断的可靠性就会降低。极端情况下,当只有一个哨兵时,哨兵本身就可能会发生单点故障。整体来讲,相对稳妥的哨兵部署方案是使得哨兵的视角尽可能与每个节点的视角一致,即:

  • 为每个节点(无论是主数据库还是从数据库)部署一个哨兵;
  • 使每个哨兵与其对应的节点的网络环境相同或相近。

这样的部署方案可以保证哨兵的视角拥有较高的代表性和可靠性。
同时设置quorum的值为N/2 + 1(其中N为哨兵节点数量),这样使得只有当大部分哨兵节点同意后才会进行故障转移。

当系统中的节点较多时,考虑到每个哨兵都会和系统中的所有节点建立连接,为每个节点分配一个哨兵会产生较多连接,对性能有所损耗,所有配置哨兵时还需要根据实际的生产环境情况进行选择。


四,集群

即使使用哨兵,此时Redis集群的每个数据库依然存有集群中的所有数据,从而导致集群的总数据存储量受限于可用存储内存最小的数据库节点,形成木桶效应。由于Redis中的所有数据都是基于内存存储,这一问题尤为突出,尤其是当使用Redis做持久化存储服务时。

针对上述问题,可以通过手动分片对Redis进行进行水平扩容。
对于需要扩容的场景来说,在客户端分片后,如果想增加更多的节点,就需要对数据进行手工迁移,同时在迁移的过程中为了保证数据的一致性,还需要将集群暂时下线,相对比较复杂。
但是手动分片可以精准的控制哪个键落在哪个数据库节点上。

新版的Redis支持集群。Redis集群的特点在于拥有单机实例同样的性能,同时在网络分区后能够提供一定的可访问性以及对主数据库故障恢复的支持。
另外集群支持几乎所有的单机实例支持的命令,对于涉及多键的命令(如mget),如果每个键都位于同一个节点中,则可以正常支持,否则会提示错误。
除此之外集群还有一个限制是只能使用默认的0号数据库,如果执行select切换数据库则会提示错误。
集群支持动态增加节点。

使用集群,只需要将每个数据库节点的cluster-enabled配置选项打开即可。每个集群中至少需要3个主数据库才能正常运行。
为了防止当某个主数据库停止服务时能自动故障转移,每个主数据库都必须带有至少一个从数据库。

Redis集群通过Redis安装目录中的辅助工具redis-trib.rb实现。redis-trib.rb是用Ruby语言编写的,所以运行前需要在服务器上安装Ruby程序。

最后部署成功后,集群通过算法实现自动分片。


五,参考资料

《Redis入门指南(第2版)》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值