2019独角兽企业重金招聘Python工程师标准>>>
现实中redis需要若干台redis服务器的支持:
(1)从结构上,单个Redis服务器会产生单点故障,同时一台服务器需要承受所有的请求负载。这就需要为数据生成多个副本并分配在不同的服务器上。
(2)从容量上,单个Redis服务器的内存非常容易成为存储瓶颈,所以需要进行数据分片。
同时拥有多个Redis服务器后就会面临如何管理集群的问题,包括如何增加节点,故障恢复等操作。
1. 复制
通过持久化功能,Redis保证了即使在服务器重启的情况下也不会损失(或少量损失)数据。
但是由于数据是存储在一台服务器上的,如果这台服务器出现硬盘故障等问题,也会导致数据丢失。为了避免单点故障,通常的做法是将数据库复制多个副本部署在不同的服务器上,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。为此,Redis提供了复制(replication)功能,可以实现当一台服务器中的数据更新后,自动更新的数据同步到其他数据库上。
2. 哨兵
在一个典型的一主多从的Redis系统中,从数据库在整个系统中起到了数据冗余备份和读写分离的作用。当主数据库遇到异常中断服务后,开发者可以通过手动的方式选择一个从数据库升级为主数据库,以使得系统能够继续提供服务。然而整个过程相对麻烦且需要人工介入,难以实现自动化。为此, Redis2.8中提供了哨兵工具来实现自动的系统监控和故障恢复功能。
3. 集群:
即使使用哨兵,此时的Redis集群的每个数据库依然存在集群中的所有的数据库,从而导致集群的总数据存储量受限于内存最小的数据库节点,形成木桶效应。由于Redis中的所有数据都是基于内存存储,这一问题就尤为突出了,尤其是当使用Redis做持久化存储服务使用时。
哨兵和集群是两个独立的功能,但从特性来看哨兵可以视为集群的子集,当不需要数据分片或者已经在客户端进行分片的场景下哨兵就足够使用了,但如果需要进行水平扩容,则集群是一个非常好的选择。
在Redis中使用复制功能非常容易,只需要在从数据库的配置文件中加入
salveof 主数据库地址 主数据库端口
salveof 127.0.0.1 6379
即可,主数据库无需进行任何配置。
哨兵的作用就是监控Redis系统的运行状况。它的功能包括以下两个:
* 监控主数据库和从数据库是否运行正常。
* 主数据库出现故障时自动将从数据库转换为主数据库。
sentinel monitor master-name ip redis-port quornum
sentinel monitor mymaster 127.0.0.1 6379 1
其中:
master-name是一个有大小写字母,数字和".-_"组成的主数据库的名字,因为考虑到故障恢复后当前监控系统的主数据库的地址和端口会产生变化,所以哨兵提供了命令可以通过主数据库的名字获取当前系统的主数据库的地址和端口号。
ip 表示当前系统中主数据库的地址,而redis-port则表示端口号。
quornum 用来表示执行故障恢复操作前至少需要几个哨兵结点同意。
需要注意的是,配置哨兵监控一个系统时,只需要配置其监控主数据库即可,哨兵会自动发现所有复制该主数据库的从数据库。
和主数据库的连接建立完成后,哨兵会定时执行下面3个操作:
* 每10秒哨兵会向主数据库和从数据库发送info命令。
* 每2秒哨兵会向主数据库和从数据库的_sentinel_:hello频道发送自己的信息。
* 每1秒哨兵会向主数据,从数据库和其他哨兵结点发送PING命令。
领头哨兵将从停止服务的主数据库的从数据中挑选一个来充当新的主数据库。挑选的依据如下:
(1)所有在线的从数据库中,选择优先级最高的从数据库。优先级可以通过slave-priority选项来设置。
(2)如果有多个最高优先级的从数据库,则复制的命令偏移量越大(即复制越完整)越优先。
(3)如果以上条件都一样,则选择运行ID较小的从数据库。
选出一个从数据库后,领头哨兵将向从数据库发送SLAVEOF NO ONE命令将其升格为主数据库。而后领头哨兵向其他从数据库发送SLAVEOF 命令来使其成为新主数据库的从数据库。
最后一步则是更新内部的记录,将已经停止服务的旧的主数据库更新为新的主数据库的从数据库,使得当其恢复服务时自动以从服务器的身份继续服务。
哨兵的部署:
哨兵以独立进程的方式对一个主从系统进行监控,监控的效果好坏与否取决于哨兵的视角是否有代表性。如果一个主从系统中配置的哨兵较少,哨兵对整个系统的判断的可靠性就会降低。极端情况下,当只有一个哨兵时,哨兵本身就可能会发生单点故障。整体来讲,相对稳妥的哨兵部署方案是使得哨兵的视角尽可能地与每个节点的视角一致,即:
(1)为每个结点(无论是主数据库还是从数据库)部署一个哨兵;
(2)使得每个哨兵与其对应的节点的网络环境相同或相近。
这样的部署方案可以保证哨兵的视角拥有较高的代表性和可靠性。
同时设置quorum的值为N/2+1(其中N为哨兵节点数量),这样使得只有当大部分哨兵节点同意后才会进行故障恢复
集群:
集群的特点在于拥有和单机实例同样的性能,同时在网络分区后能够提供一定的可访问性以及对主数据库故障恢复的支持。另外集群支持几乎所有的单机实例支持的命令,对于涉及多个的命令(如MGET),如果每个键都位于同一个节点中,才可以正常支持,否则会提示错误。除此之外集群还有一个限制是只能使用默认的0号数据库,如果执行SELECT 切换数据库则会提示错误。
哨兵和集群是两个独立的功能,但从特性来看哨兵可以视为集群的子集,当不需要数据分片或者已经在客户端进行分片的场景下哨兵就足够使用了,但如果需要进行水平扩容,则集群是一个非常好的选择。
配置集群:
使用集群,只需要将每个数据库节点的cluster-enabled配置选项打开即可。每个集群中至少需要3个主数据库才能正常运行。
cluster-enabled yescluster-config-file nodes.config
实验:
cluster-enabled yes
cluster-config-file nodes.7000.conf #个性化
cluster-node-timeout 5000
appendonly yes
appendfilename "appendonly.7000.aof" #个性化
daemonize yes
pidfile #个性化
log #个性化
以7000-70005六个不同的port启动。
[root@localhost 7005]# ps -aux|grep redis
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
root 4423 0.0 0.1 133540 7552 ? Ssl 14:54 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7000 [cluster]
root 4432 0.0 0.1 133540 7556 ? Ssl 14:54 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7001 [cluster]
root 4441 0.0 0.1 133540 7556 ? Ssl 14:55 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7002 [cluster]
root 4449 0.0 0.1 133540 7556 ? Ssl 14:55 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7003 [cluster]
root 4454 0.0 0.1 133540 7556 ? Ssl 14:55 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7004 [cluster]
root 4460 0.0 0.1 133540 7552 ? Ssl 14:56 0:00 /mnt/shares/redis/redis-3.2.0/src/redis-server 127.0.0.1:7005 [cluster]
root 4464 0.0 0.0 103252 840 pts/1 S+ 14:56 0:00 grep redis
初始化集群
其中create参数表示要初始化集群, --replicas 1 表示每个主数据库拥有的从数据库个数为1,所以下面整个集群共有3(6/2)个主数据库以及3个从数据库。
[root@localhost src]# ./redis-trib.rb create --replicas 1 172.16.81.107:7000 172.16.81.107:7001 172.16.81.107:7002 172.16.81.107:7003 172.16.81.107:7004 172.16.81.107:7005
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
172.16.81.107:7000
172.16.81.107:7001
172.16.81.107:7002
Adding replica 172.16.81.107:7003 to 172.16.81.107:7000
Adding replica 172.16.81.107:7004 to 172.16.81.107:7001
Adding replica 172.16.81.107:7005 to 172.16.81.107:7002
M: 9e3a1e4883393552357e41507f3da851370dbe4b 172.16.81.107:7000slots:0-5460 (5461 slots) master
M: de7a63cd01e477c49a31f53837b72c7814f4b274 172.16.81.107:7001slots:5461-10922 (5462 slots) master
M: c7b1fbf25def810a42f5f61a0b16e62f538861b7 172.16.81.107:7002slots:10923-16383 (5461 slots) master
S: d86d4a4f6246362e06f2a266f2a9fec3c5942541 172.16.81.107:7003replicates 9e3a1e4883393552357e41507f3da851370dbe4b
S: 43b99ad59f4519f915c86a6ff5bd6189e211e080 172.16.81.107:7004replicates de7a63cd01e477c49a31f53837b72c7814f4b274
S: 7ffe9792caf077cee3f8d545278d08afbf456d77 172.16.81.107:7005replicates c7b1fbf25def810a42f5f61a0b16e62f538861b7
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 172.16.81.107:7000)
M: 9e3a1e4883393552357e41507f3da851370dbe4b 172.16.81.107:7000slots:0-5460 (5461 slots) master
M: de7a63cd01e477c49a31f53837b72c7814f4b274 172.16.81.107:7001slots:5461-10922 (5462 slots) master
M: c7b1fbf25def810a42f5f61a0b16e62f538861b7 172.16.81.107:7002slots:10923-16383 (5461 slots) master
M: d86d4a4f6246362e06f2a266f2a9fec3c5942541 172.16.81.107:7003slots: (0 slots) masterreplicates 9e3a1e4883393552357e41507f3da851370dbe4b
M: 43b99ad59f4519f915c86a6ff5bd6189e211e080 172.16.81.107:7004slots: (0 slots) masterreplicates de7a63cd01e477c49a31f53837b72c7814f4b274
M: 7ffe9792caf077cee3f8d545278d08afbf456d77 172.16.81.107:7005slots: (0 slots) masterreplicates c7b1fbf25def810a42f5f61a0b16e62f538861b7
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
集群中所有节点信息:
[root@localhost src]# redis-cli -p 7001
127.0.0.1:7001> cluster nodes
de7a63cd01e477c49a31f53837b72c7814f4b274 127.0.0.1:7001 myself,master - 0 0 2 connected 5461-10922
7ffe9792caf077cee3f8d545278d08afbf456d77 127.0.0.1:7005 slave c7b1fbf25def810a42f5f61a0b16e62f538861b7 0 1464161662246 3 connected
9e3a1e4883393552357e41507f3da851370dbe4b 127.0.0.1:7000 master - 0 1464161661243 1 connected 0-5460
c7b1fbf25def810a42f5f61a0b16e62f538861b7 127.0.0.1:7002 master - 0 1464161661744 3 connected 10923-16383
43b99ad59f4519f915c86a6ff5bd6189e211e080 127.0.0.1:7004 slave de7a63cd01e477c49a31f53837b72c7814f4b274 0 1464161660741 2 connected
d86d4a4f6246362e06f2a266f2a9fec3c5942541 127.0.0.1:7003 slave 9e3a1e4883393552357e41507f3da851370dbe4b 0 1464161661744 1 connected
节点的增加:
CLUSTER MSET ip port
ip ,port 是集群中任意一个节点的地址和端口号,新节点(A)接收到客户端发来的命令后,
会与该地址和端口号的节点B进行握手,使B将A认作当前集群中的一员。当B与A握手成功后,B会使用Gossip协议将节点A的信息通知给集群中的每一个节点。通过这一方式,即使集群中有多个节点,也只需要选择MSET其中任意一个节点,即可使新节点最终加入整个集群中。
启动一个port为7006的节点:
[root@localhost 7006]# /mnt/shares/redis/redis-3.2.0/src/redis-server ./redis.conf
执行下面的命令把新节点添加到集群中:
[root@localhost src]# ./redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
>>> Adding node 127.0.0.1:7006 to cluster 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 9e3a1e4883393552357e41507f3da851370dbe4b 127.0.0.1:7000slots:0-5460 (5461 slots) master1 additional replica(s)
S: 7ffe9792caf077cee3f8d545278d08afbf456d77 127.0.0.1:7005slots: (0 slots) slavereplicates c7b1fbf25def810a42f5f61a0b16e62f538861b7
S: 43b99ad59f4519f915c86a6ff5bd6189e211e080 127.0.0.1:7004slots: (0 slots) slavereplicates de7a63cd01e477c49a31f53837b72c7814f4b274
S: d86d4a4f6246362e06f2a266f2a9fec3c5942541 127.0.0.1:7003slots: (0 slots) slavereplicates 9e3a1e4883393552357e41507f3da851370dbe4b
M: c7b1fbf25def810a42f5f61a0b16e62f538861b7 127.0.0.1:7002slots:10923-16383 (5461 slots) master1 additional replica(s)
M: de7a63cd01e477c49a31f53837b72c7814f4b274 127.0.0.1:7001slots:5461-10922 (5462 slots) master1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 127.0.0.1:7006 to make it join the cluster.
[OK] New node added correctly.
127.0.0.1:7000> cluster nodes
47a241ed17b89f501d3ea6e337456fab2ddd1e93 127.0.0.1:7006 master - 0 1464163321583 0 connected
7ffe9792caf077cee3f8d545278d08afbf456d77 127.0.0.1:7005 slave c7b1fbf25def810a42f5f61a0b16e62f538861b7 0 1464163320582 6 connected
43b99ad59f4519f915c86a6ff5bd6189e211e080 127.0.0.1:7004 slave de7a63cd01e477c49a31f53837b72c7814f4b274 0 1464163320082 5 connected
d86d4a4f6246362e06f2a266f2a9fec3c5942541 127.0.0.1:7003 slave 9e3a1e4883393552357e41507f3da851370dbe4b 0 1464163320082 4 connected
c7b1fbf25def810a42f5f61a0b16e62f538861b7 127.0.0.1:7002 master - 0 1464163322084 3 connected 10923-16383
9e3a1e4883393552357e41507f3da851370dbe4b 127.0.0.1:7000 myself,master - 0 0 1 connected 0-5460
de7a63cd01e477c49a31f53837b72c7814f4b274 127.0.0.1:7001 master - 0 1464163321083 2 connected 5461-10922
插槽的分配:
未完成
获取与插槽对应的结点:
当客户端向集群中的任意一个结点发送命令后,该结点会判断相应的键是否在当前结点中,
如果键在该结点中,则会像单机实例一样正常处理该命令;
如果键不在该结点中,就会返回一个MOVE重定向请求,告诉客户端这个键目前由哪个结点负责,然后客户端再将同样的请求向目标节点重新发送一次以获取结果。
Redis命令行客户端提供了集群模式来支持自动重定向,使用-c参数来启用:
redis-cli -c -p 6380
加入参数-c后,如果当前结点并不负责要处理的键,Redis命令行客户端会进行自动命令重定向。而这一过程正是每个支持集群的客户端应该实现的。
故障恢复:
在集群中,当一个主数据库下线时,就会出现一部分插槽无法写入的问题。这时如果该主数据库拥有至少一个从数据库,集群就进行故障恢复操作来将其中一个从数据库转变成主数据库来保证 集群的完整。选择哪个从数据库来作为主数据库的过程与在哨兵中选择领头哨兵的过程一样,都是 Raft算法,过程如下:
1. 发现复制的主数据库下线的从数据库(下面称作A)向每个集群中的节点发送请求,要求对方选自己成为主数据库。
2. 如果收到请求的节点没有选过其他人,则会同意将A设置为主数据库。
3. 如果A发现有超过集群中节点总数一半的节点同意选自己成为主数据库,则A则成功成为主数据库。
4. 当有多个从数据库结点同时参选主数据库,则会出现没有任何节点当选的可能。此时每个参选结点节点将等待一个随机时间重新发起参选请求,进行下一轮选举,直到选举成功。
当某个从数据库当选为主数据库后,会通过命令SLAVEOF ON ONE 将自己转换成主数据库,并将旧的主数据库的插槽转换给自己负责。
如果一个至少负责一个插槽的主数据库下线且没有相应的从数据库可以进行故障恢复,则整个集群默认会进入下线状态无法继续工作。如果想在这种情况下使集群仍能正常工作,可以
修改配置cluster-require-full-coverage 为no(默认yes)
cluster-require-full-coverage no