Replication(主从复制)

主从复制原理:

  • 从服务器连接主服务器,发送SYNC命令;
  • 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
  • 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
  • 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
  • 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
  • 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;(从服务器初始化完成)
  • 主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令(从服务器初始化完成后的操作)
  • 一个master可以拥有多个slave,但是一个slave只能对应一个master

优点:

  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离
  • 为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成
  • Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。
  • Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。
  • Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据

缺点:

  • Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
  • Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。

Redis的replication机制允许slave从master那里通过网络传输拷贝到完整的数据备份,从而达到主从机制。

slaveof配置选项可以将当前服务器转变为指定服务器的从属服务器(slave server)

IP:192.168.230.202
Master Port:6300
Slave1 Port:6301
Slave2 Port:6302
配置主服务器

Master编辑配置文件redis.conf设置端口6300

port 6300
配置从服务器

Slave编辑配置文件redis_slave1.conf设置端口6301

port 6301
slaveof 192.168.230.202 6300

Slave编辑配置文件redis_slave2.conf设置端口6302

port 6302
slaveof 192.168.230.202 6300

slave 默认只读模式,可以通过配置项slave-read-only来进行配置。

如果master通过requirepass配置项设置了密码,slave每次同步操作都需要验证密码,可以通过在slave的配置文件中添加以下配置项

masterauth <password>
测试
[root@master-all redis]# ./bin/redis-cli -h 192.168.230.202 -p 6300 
192.168.230.202:6300> set hello world
OK
(0.58s)
[root@master-all redis]# ./bin/redis-cli -h 192.168.230.202 -p 6301
192.168.230.202:6301> get hello
"world"
[root@master-all redis]# ./bin/redis-cli -h 192.168.230.202 -p 6302
192.168.230.202:6302> get hello
"world"

Sentinel(哨兵)

当主服务器中断服务后,可以将一个从服务器升级为主服务器,以便继续提供服务,但是这个过程需要人工手动来操作。 为此,Redis 2.8中提供了哨兵工具来实现自动化的系统监控和故障恢复功能。

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

(1)监控(Monitoring):不断地检查redis的主服务器和从服务器是否运作正常。
(2)提醒(Notification):如果发现某个redis服务器运行出现状况,可以通过 API 向管理员或者其他应用程序发送通知。
(3)自动故障迁移(Automatic failover):能够进行自动切换。当一个主服务器不能正常工作时,会将失效主服务器的其中一个从服务器升级为新的主服务器,并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

哨兵的工作方式:

  • 每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令。
  • 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN)
  • 如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态
  • 当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)
  • 在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
  • 当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
  • 若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。

优点:

  • 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
  • 主从可以自动切换,系统更健壮,可用性更高。

缺点:

  • Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
配置Sentinel(哨兵)

配置文件只需要配置master的信息就可,不用配置slave的信息,因为slave能够被自动检测到(master节点会有关于slave的消息)。

需要注意的是,配置文件在sentinel运行期间是会被动态修改的,例如当发生主备切换时候,配置文件中的master会被修改为另外一个slave。这样,之后sentinel如果重启时,就可以根据这个配置来恢复其之前所监控的redis集群的状态。

Sentinel 1 配置文件新建

[root@master-all redis]# cat sentinel.conf
port 26379
sentinel monitor mymaster 192.168.230.202 6300 2

Sentinel 2 配置文件新建

[root@master-all redis]# cat sentinel_1.conf
port 26378
sentinel monitor mymaster 192.168.230.202 6300 2

官方典型的配置如下:

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
  • down-after-milliseconds 选项指定了 Sentinel 认为服务器已经断线所需的毫秒数。
  • parallel-syncs 选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步, 这个数字越小, 完成故障转移所需的时间就越长。
sentinel monitor mymaster 192.168.230.202 6300 2

这行配置指示 Sentinel 去监视一个名为 mymaster 的主服务器, 这个主服务器的 IP 地址为 192.168.230.202 , 端口号为 6300, 而将这个主服务器判断为失效至少需要 2 个 Sentinel 同意,只要同意 Sentinel 的数量不达标,自动故障迁移就不会执行。

不过要注意, 无论你设置要多少个 Sentinel 同意才能判断一个服务器失效, 一个 Sentinel 都需要获得系统中多数(majority) Sentinel 的支持, 才能发起一次自动故障迁移, 并预留一个给定的配置纪元 (configuration Epoch ,一个配置纪元就是一个新主服务器配置的版本号)。换句话说, 在只有少数(minority) Sentinel 进程正常运作的情况下, Sentinel 是不能执行自动故障迁移的。sentinel集群中各个sentinel也有互相通信,通过gossip协议。

除port外统一格式如下:

sentinel <option_name> <master_name> <option_value>
启动Sentinel
[root@master-all redis]# redis-server sentinel.conf --sentinel
[root@master-all redis]# redis-server sentinel_1.conf --sentinel
停止redis master 服务
[root@master-all redis]# redis-cli -h 192.168.230.202 -p 6300 shutdown

停止master服务后会发现Sentinel会将master切换到其他机器上,原master机器恢复后将变成slave

Redis官方 Cluster集群模式

redis的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台redis服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了cluster模式,实现的redis的分布式存储,也就是说每台redis节点上存储不同的内容。

Redis-Cluster采用无中心结构,它的特点如下:

  • 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  • 节点的fail是通过集群中超过半数的节点检测失效时才生效。
  • 客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。

工作方式:

在redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的key到达的时候,redis会根据crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。

为了保证高可用,redis-cluster集群引入了主从模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点A1都宕机了,那么该集群就无法再提供服务了。

Jedis sharding集群

Redis Sharding可以说是在Redis cluster出来之前业界普遍的采用方式,其主要思想是采用hash算法将存储数据的key进行hash散列,这样特定的key会被定为到特定的节点上。

庆幸的是,Java Redis客户端驱动Jedis已支持Redis Sharding功能,即ShardedJedis以及结合缓存池的ShardedJedisPool

Jedis的Redis Sharding实现具有如下特点:

  • 采用一致性哈希算法,将key和节点name同时hashing,然后进行映射匹配,采用的算法是MURMUR_HASH。采用一致性哈希而不是采用简单类似哈希求模映射的主要原因是当增加或减少节点时,不会产生由于重新匹配造成的rehashing。一致性哈希只影响相邻节点key分配,影响量小。
  • 为了避免一致性哈希只影响相邻节点造成节点分配压力,ShardedJedis会对每个Redis节点根据名字(没有,Jedis会赋予缺省名字)会虚拟化出160个虚拟节点进行散列。根据权重weight,也可虚拟化出160倍数的虚拟节点。用虚拟节点做映射匹配,可以在增加或减少Redis节点时,key在各Redis节点移动再分配更均匀,而不是只有相邻节点受影响。
  • ShardedJedis支持keyTagPattern模式,即抽取key的一部分keyTag做sharding,这样通过合理命名key,可以将一组相关联的key放入同一个Redis节点,这在避免跨节点访问相关数据时很重要。 当然,Redis Sharding这种轻量灵活方式必然在集群其它能力方面做出妥协。比如扩容,当想要增加Redis节点时,尽管采用一致性哈希,毕竟还是会有key匹配不到而丢失,这时需要键值迁移。 作为轻量级客户端sharding,处理Redis键值迁移是不现实的,这就要求应用层面允许Redis中数据丢失或从后端数据库重新加载数据。但有些时候,击穿缓存层,直接访问数据库层,会对系统访问造成很大压力。

2.5、利用中间件代理 中间件的作用是将我们需要存入redis中的数据的key通过一套算法计算得出一个值。然后根据这个值找到对应的redis节点,将这些数据存在这个redis的节点中。

常用的中间件有这几种

  • Twemproxy
  • Codis
  • nginx

Redis集群

IP:192.168.230.202
Redis1 Path:/usr/local/redis1 Port:6301
Redis2 Path:/usr/local/redis2 Port:6302
Redis3 Path:/usr/local/redis3 Port:6303
Redis4 Path:/usr/local/redis4 Port:6304
Redis5 Path:/usr/local/redis5 Port:6305
Redis6 Path:/usr/local/redis6 Port:6306

编辑启动脚本

[root@master-all ~]# vim start-all.sh
#!/bin/bash
for num in {1..6}
do
	/usr/local/redis${num}/bin/redis-server /usr/local/redis${num}/redis.conf
done

编辑停止脚本

[root@master-all ~]# vim stop-all.sh
#!/bin/bash
for num in {1..6}
do
	/usr/local/redis${num}/bin/redis-cli -h 192.168.230.202 -p "630${num}" shutdown
done

启动并查看进程

[root@master-all ~]# bash start-all.sh 
[root@master-all ~]# ps aux |grep redis
root      14532  0.0  0.5 163112 11828 ?        Ssl  11:57   0:00 /usr/local/redis1/bin/redis-server 192.168.230.202:6301 [cluster]
root      14537  0.0  0.5 163112 11832 ?        Ssl  11:57   0:00 /usr/local/redis2/bin/redis-server 192.168.230.202:6302 [cluster]
root      14542  0.0  0.3 156968  7744 ?        Ssl  11:57   0:00 /usr/local/redis3/bin/redis-server 192.168.230.202:6303 [cluster]
root      14547  0.0  0.3 156968  7744 ?        Ssl  11:57   0:00 /usr/local/redis4/bin/redis-server 192.168.230.202:6304 [cluster]
root      14552  0.0  0.5 163112 11828 ?        Ssl  11:57   0:00 /usr/local/redis5/bin/redis-server 192.168.230.202:6305 [cluster]
root      14557  0.0  0.3 156968  7744 ?        Ssl  11:57   0:00 /usr/local/redis6/bin/redis-server 192.168.230.202:6306 [cluster]
root      14562  0.0  0.0 112712   960 pts/3    S+   11:57   0:00 grep --color=auto redis
创建集群
redis-cli --cluster create 192.168.230.202:6301 192.168.230.202:6302 192.168.230.202:6303 192.168.230.202:6304 192.168.230.202:6305 192.168.230.202:6306 --cluster-replicas 1
--cluster-replicas表示一个主有几个slave
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.230.202:6305 to 192.168.230.202:6301
Adding replica 192.168.230.202:6306 to 192.168.230.202:6302
Adding replica 192.168.230.202:6304 to 192.168.230.202:6303
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 2cd610740526ad5229f18cc9a34b0e057488fd9a 192.168.230.202:6301
   slots:[0-5460] (5461 slots) master
M: a2b8f45385a278dd88f4fb5b063cf9d074fc5d1d 192.168.230.202:6302
   slots:[5461-10922] (5462 slots) master
M: e6a3d24ff4c864995abc668c1e6870d192347bcb 192.168.230.202:6303
   slots:[10923-16383] (5461 slots) master
S: ef58775b86274122371c716f53677183507de80b 192.168.230.202:6304
   replicates 2cd610740526ad5229f18cc9a34b0e057488fd9a
S: 6214d0bede8bea60d883d405236756982a9467b8 192.168.230.202:6305
   replicates a2b8f45385a278dd88f4fb5b063cf9d074fc5d1d
S: fde8b99fa7ac9f0e53dbe4371a88463cb2629576 192.168.230.202:6306
   replicates e6a3d24ff4c864995abc668c1e6870d192347bcb
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.....
>>> Performing Cluster Check (using node 192.168.230.202:6301)
M: 2cd610740526ad5229f18cc9a34b0e057488fd9a 192.168.230.202:6301
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: a2b8f45385a278dd88f4fb5b063cf9d074fc5d1d 192.168.230.202:6302
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 6214d0bede8bea60d883d405236756982a9467b8 192.168.230.202:6305
   slots: (0 slots) slave
   replicates a2b8f45385a278dd88f4fb5b063cf9d074fc5d1d
S: ef58775b86274122371c716f53677183507de80b 192.168.230.202:6304
   slots: (0 slots) slave
   replicates 2cd610740526ad5229f18cc9a34b0e057488fd9a
S: fde8b99fa7ac9f0e53dbe4371a88463cb2629576 192.168.230.202:6306
   slots: (0 slots) slave
   replicates e6a3d24ff4c864995abc668c1e6870d192347bcb
M: e6a3d24ff4c864995abc668c1e6870d192347bcb 192.168.230.202:6303
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

可以看到6个节点配分配成3个主节点,3个从节点。

查询集群信息

集群创建成功登陆任意redis结点查询集群中的节点情况。

客户端以集群方式登陆:

[root@master-all ~]# redis-cli -c -h 192.168.230.202 -p 6302
192.168.230.202:6302> cluster nodes
ef58775b86274122371c716f53677183507de80b 192.168.230.202:6304@16304 slave 2cd610740526ad5229f18cc9a34b0e057488fd9a 0 1586837304000 4 connected
a2b8f45385a278dd88f4fb5b063cf9d074fc5d1d 192.168.230.202:6302@16302 myself,master - 0 1586837302000 2 connected 5461-10922
2cd610740526ad5229f18cc9a34b0e057488fd9a 192.168.230.202:6301@16301 master - 0 1586837304413 1 connected 0-5460
e6a3d24ff4c864995abc668c1e6870d192347bcb 192.168.230.202:6303@16303 master - 0 1586837303000 3 connected 10923-16383
fde8b99fa7ac9f0e53dbe4371a88463cb2629576 192.168.230.202:6306@16306 slave e6a3d24ff4c864995abc668c1e6870d192347bcb 0 1586837303402 6 connected
6214d0bede8bea60d883d405236756982a9467b8 192.168.230.202:6305@16305 slave a2b8f45385a278dd88f4fb5b063cf9d074fc5d1d 0 1586837305422 5 connected

测试:

[root@master-all ~]# redis-cli -c -h 192.168.230.202 -p 6302
192.168.230.202:6302> set test hello
OK
192.168.230.202:6302> get test
"hello"
192.168.230.202:6302> quit
[root@master-all ~]# redis-cli -c -h 192.168.230.202 -p 6301
192.168.230.202:6301> get test
-> Redirected to slot [6918] located at 192.168.230.202:6302
"hello"
192.168.230.202:6302> quit
[root@master-all ~]# redis-cli -c -h 192.168.230.202 -p 6303
192.168.230.202:6303> get test
-> Redirected to slot [6918] located at 192.168.230.202:6302
"hello"
192.168.230.202:6302> quit
[root@master-all ~]# redis-cli -c -h 192.168.230.202 -p 6304
192.168.230.202:6304> get test
-> Redirected to slot [6918] located at 192.168.230.202:6302
"hello"
192.168.230.202:6302> quit
[root@master-all ~]# redis-cli -c -h 192.168.230.202 -p 6305
192.168.230.202:6305> get test
-> Redirected to slot [6918] located at 192.168.230.202:6302
"hello"
192.168.230.202:6302> quit
[root@master-all ~]# redis-cli -c -h 192.168.230.202 -p 6306
192.168.230.202:6306> get test
-> Redirected to slot [6918] located at 192.168.230.202:6302
"hello"

可以看到redis 集群会根据计算为我们分配hash槽,以分配不同的节点


该文章采用「CC 协议」,转载必须注明作者和本文链接.
分类: Linux