Redis有三种集群模式,分别是:
* 主从模式
* Sentinel模式
* Cluster模式
三种集群模式各有特点,关于Redis介绍可以参考这里:NoSQL(二)——Redis
Redis官网:https://redis.io/ ,最新版本5.0.4
主从模式是三种模式中最简单的,在主从复制中,数据库分为两类:主数据库(master)和从数据库(slave)。
其中主从复制有如下特点:
* 主数据库可以进行读写操作,当读写操作导致数据变化时会自动将数据同步给从数据库
* 从数据库一般都是只读的,并且接收主数据库同步过来的数据
* 一个master可以拥有多个slave,但是一个slave只能对应一个master
* slave挂了不影响其他slave的读和master的读和写,重新启动后会将数据从master同步过来
* master挂了以后,不影响slave的读,但redis不再提供写服务,master重启后redis将重新对外提供写服务
* master挂了以后,不会在slave节点中重新选一个master
工作机制:
当slave启动后,主动向master发送SYNC命令。master接收到SYNC命令后在后台保存快照(RDB持久化)和缓存保存快照这段时间的命令,然后将保存的快照文件和缓存的命令发送给slave。slave接收到快照文件和命令后加载快照文件和缓存的执行命令。
复制初始化后,master每次接收到的写命令都会同步发送给slave,保证主从数据一致性。
安全设置:
当master节点设置密码后,
客户端访问master需要密码
启动slave需要密码,在配置文件中配置即可
客户端访问slave不需要密码
缺点:
从上面可以看出,master节点在主从模式中唯一,若master挂掉,则redis无法对外提供写服务。
master节点 192.168.30.128 slave
节点 192.168.30.129 slave
节点 192.168.30.130
# cd /software
# wget http://download.redis.io/releases/redis-5.0.4.tar.gz
# tar zxf redis-5.0.4.tar.gz && mv redis-5.0.4/ /usr/local/redis
# cd /usr/local/redis && make && make install
# echo $? 0
服务文件
# vim /usr/lib/systemd/system/redis.service[Unit]Description=Redis persistent key-value database After=network.target After=network-online.target Wants=network-online.target[Service]ExecStart=/usr/local/bin/redis-server /usr/local/redis/redis.conf --supervised systemd ExecStop=/usr/libexec/redis-shutdown Type=notify User=redis Group=redis RuntimeDirectory=redis RuntimeDirectoryMode=0755[Install]WantedBy=multi-user.target
shutdown脚本
# vim /usr/libexec/redis-shutdown#!/bin/bash## WrApper to close properly redis and sentineltest x"$REDIS_DEBUG" != x && set -x
REDIS_CLI=/usr/local/bin/redis-cli# Retrieve service nameSERVICE_NAME="$1"if [ -z "$SERVICE_NAME" ]; then
SERVICE_NAME=redisfi# Get the proper config file based on service nameCONFIG_FILE="/usr/local/redis/$SERVICE_NAME.conf"# Use awk to retrieve host, port from config fileHOST=`awk '/^[[:blank:]]*bind/ { print $2 }' $CONFIG_FILE | tail -n1`PORT=`awk '/^[[:blank:]]*port/ { print $2 }' $CONFIG_FILE | tail -n1`PASS=`awk '/^[[:blank:]]*requirepass/ { print $2 }' $CONFIG_FILE | tail -n1`SOCK=`awk '/^[[:blank:]]*unixsockets/ { print $2 }' $CONFIG_FILE | tail -n1`# Just in case, use default host, portHOST=${HOST:-127.0.0.1}if [ "$SERVICE_NAME" = redis ]; then
PORT=${PORT:-6379}else
PORT=${PORT:-26739}fi# Setup additional parameters# e.g password-protected redis instances[ -z "$PASS" ] || ADDITIONAL_PARAMS="-a $PASS"# shutdown the service properlyif [ -e "$SOCK" ] ; then
$REDIS_CLI -s $SOCK $ADDITIONAL_PARAMS shutdownelse
$REDIS_CLI -h $HOST -p $PORT $ADDITIONAL_PARAMS shutdownfi123456789101112131415161718192021222324252627282930313233343536373839404142
# chmod +x /usr/libexec/redis-shutdown# useradd -s /sbin/nologin redis# chown -R redis:redis /usr/local/redis# chown -R reids:redis /data/redis# yum install -y bash-completion && source /etc/profile #命令补全# systemctl daemon-reload# systemctl enable redis
192.168.30.128
# mkdir -p /data/redis # vim /usr/local/redis/redis.conf bind 192.168.30.128
#监听ip,多个ip用空格分隔 daemonize yes
#允许后台启动 logfile "/usr/local/redis/redis.log"
#日志路径 dir /data/redis #数据库备份文件存放目录 masterauth 123456
#slave连接master密码,master可省略 requirepass 123456 #设置master连接密码,slave可省略 appendonly yes
#在/data/redis/目录生成appendonly.aof文件,将每一次写操作请求都追加到appendonly.aof 文件中
# echo 'vm.overcommit_memory=1' >> /etc/sysctl.conf # sysctl -p
192.168.30.129
# mkdir -p /data/redis
# vim /usr/local/redis/redis.conf bind 192.168.30.129 daemonize yes logfile "/usr/local/redis/redis.log" dir /data/redis replicaof 192.168.30.128 6379 masterauth 123456 requirepass 123456 appendonly yes
# echo 'vm.overcommit_memory=1' >> /etc/sysctl.conf
# sysctl -p
192.168.30.130
# mkdir -p /data/redis
# vim /usr/local/redis/redis.conf bind 192.168.30.130 daemonize yes logfile "/usr/local/redis/redis.log" dir /data/redis replicaof 192.168.30.128 6379 masterauth 123456 requirepass 123456 appendonly yes
# echo 'vm.overcommit_memory=1' >> /etc/sysctl.conf
# sysctl -p
# systemctl start redis
# redis-cli -h 192.168.30.128 -a 123456 Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. 192.168.30.128:6379> info replication
# Replication role:master connected_slaves:2 slave0:ip=192.168.30.129,port=6379,state=online,offset=168,lag=1 slave1:ip=192.168.30.130,port=6379,state=online,offset=168,lag=1 master_replid:fb4941e02d5032ad74c6e2383211fc58963dbe90 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:168 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:168
# redis-cli -h 192.168.30.129 -a 123456 info replication Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. # Replication role:slave master_host:192.168.30.128 master_port:6379 master_link_status:up master_last_io_seconds_ago:1 master_sync_in_progress:0 slave_repl_offset:196 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:fb4941e02d5032ad74c6e2383211fc58963dbe90 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:196 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:196
数据演示
192.168.30.128:6379> keys *
(empty list or set)
192.168.30.128:6379> set key1 100
OK
192.168.30.128:6379> set key2 lzx
OK
192.168.30.128:6379> keys *
1) "key1"
2) "key2"
123456789101112
# redis-cli -h 192.168.30.129 -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.30.129:6379> keys *
1) "key2"
2) "key1"
192.168.30.129:6379> CONFIG GET dir
1) "dir"
2) "/data/redis"
192.168.30.129:6379> CONFIG GET dbfilename
1) "dbfilename"
2) "dump.rdb"
192.168.30.129:6379> get key1
"100"
192.168.30.129:6379> get key2
"lzx"
192.168.30.129:6379> set key3 aaa
(error) READONLY You can't write against a read only replica.
1234567891011121314151617181920212223
# redis-cli -h 192.168.30.130 -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.30.130:6379> keys *
1) "key2"
2) "key1"
192.168.30.130:6379> CONFIG GET dir
1) "dir"
2) "/data/redis"
192.168.30.130:6379> CONFIG GET dbfilename
1) "dbfilename"
2) "dump.rdb"
192.168.30.130:6379> get key1
"100"
192.168.30.130:6379> get key2
"lzx"
192.168.30.130:6379> set key3 aaa
(error) READONLY You can't write against a read only replica.
可以看到,在master节点写入的数据,很快就同步到slave节点上,而且在slave节点上无法写入数据。
主从模式的弊端就是不具备高可用性,当master挂掉以后,Redis将不能再对外提供写入操作,因此sentinel应运而生。
sentinel中文含义为哨兵,顾名思义,它的作用就是监控redis集群的运行状况,特点如下
功能作用
部署
同样,我们还是将每个哨兵部署在一个单独的容器中。
sentinel配置文件
redis-sentinel1:https://github.com/Johnson19900110/johnsondock/blob/master/redis-sentinel/config/sentinel.conf
redis-sentinel2:https://github.com/Johnson19900110/johnsondock/blob/master/redis-sentinel/config/sentinel-slave1.conf
redis-sentinel3:https://github.com/Johnson19900110/johnsondock/blob/master/redis-sentinel/config/sentinel-slave2.conf
这三个配置文件一模一样,都是监听master的。要不你把这个配置文件copy到容器中,要不你就创建三份,分别挂载到容器中,这里选择了后面一种方法。
这里介绍几个基本的配置
sentinel monitor mymaster redis 6379 2 监听的master的容器别名为redis,端口是6379,最后面的2是当大于等于2个哨兵认为master主观下线后(无论这个值为多少,至少得有一半以上的哨兵判定master主观下线后,master才会被客观下线),master才会被客观下线,这是sentinel重新从slave中选举一个来当master。
sentinel auth-pass mymaster 123456 如果master配置了密码,则此项必须配置,否则sentinel会将master标记问主观下线(sdown)。
Docker-composer配置文件
### REDIS-SENTINEL ################################################
# master
redis-sentinel:
image: johnson19900110/redis-sentinel:latest
restart: always #如果master未开启数据持久化,此项应该删除
volumes:
- ./redis-sentinel/config/sentinel.conf:/usr/local/etc/redis/redis-sentinel.conf
networks:
- backend
depends_on:
- redis
# redis sentinel slave 1
redis-sentinel-slave1:
image: johnson19900110/redis-sentinel:latest
restart: always
volumes:
- ./redis-sentinel/config/sentinel-slave1.conf:/usr/local/etc/redis/redis-sentinel.conf
networks:
- backend
depends_on:
- redis-slave1
- redis-sentinel
# redis sentinel slave 2
redis-sentinel-slave2:
image: johnson19900110/redis-sentinel:latest
restart: always
volumes:
- ./redis-sentinel/config/sentinel-slave2.conf:/usr/local/etc/redis/redis-sentinel.conf
networks:
- backend
depends_on:
- redis-slave2
- redis-sentinel-slave1
启动容器
docker-compose up -d redis-sentinel-slave2
执行以上命令后,就启动了三个哨兵模式的容器
这是我们进入容器,查看是否redis-sentinel是否在工作。
我们可看到,已经与master建立连接,通过status=ok可以知道,master正在正常工作,并且有2个从节点和3个哨兵节点。现在你再打开sentinel的配置文件,会发现发生了改变。
conf文件被重写了,并且哨兵模式会自动检测到master的两个slave和另外两个sentinel。
故障演示
1、使master宕机,只需要关闭master的容器即可。
如果此时再去三个哨兵节点里用info sentinel查看信息。
会发现这时候master节点的address信息变了,这就说明哨兵模式起作用了。但他这里还是显示新的master有两个slave。是因为原master节点宕机了,一旦它重启,sentinel就会把它变成新的master节点的slave节点。我们可以去172.18.0.6这个容器中看下。
可以用以上docker命令查看容器的IP地址。进入容器后,还是在redis-cli下用info replication查看信息。
我们可以看到这个slave变成了新的master,另外一个slave也变成了新master节点的slave。如果你查看redis节点的配置文件,会发现也被重写了。 这是我们再重启原master节点试试(注意:当他重启成功后,就变成了slave节点,所以要打开持久化配置)。
当容器重启成功后,我们再去新的master节点中使用info replication查看下。
正如我们所料,它成为了新的master的slave节点。如果你查看原master的配置文件,会发现多了
最后,因为新的master节点是slave节点升级的,所以他的持久化配置还是存在的,如果你想要关掉它,只需要进入redis-cli,然后执行
至此,一次redis的master节点故障转移就演示完成了。这次演示实现了redis的监控和自动故障转移特性。
提醒特性是使用的订阅功能,需要后端代码开发配合的。
Cluster模式
创建启动集群的启动脚本,内容如下:
/redis-trib.rb create --replicas 1 192.168.25.128:7001 192.168.25.128:7002 192.168.25.128:7003 192.168.25.128:7004 192.168.25.128:7005 192.168.25.128:7006
执行此脚本Creating cluster
Connecting to node 192.168.25.128:7001: OK
Connecting to node 192.168.25.128:7002: OK
Connecting to node 192.168.25.128:7003: OK
Connecting to node 192.168.25.128:7004: OK
Connecting to node 192.168.25.128:7005: OK
Connecting to node 192.168.25.128:7006: OKPerforming hash slots allocation on 6 nodes…
Using 3 masters:
192.168.25.128:7001
192.168.25.128:7002
192.168.25.128:7003
Adding replica 192.168.25.128:7004 to 192.168.25.128:7001
Adding replica 192.168.25.128:7005 to 192.168.25.128:7002
Adding replica 192.168.25.128:7006 to 192.168.25.128:7003
M: 2e48ae301e9c32b04a7d4d92e15e98e78de8c1f3 192.168.25.128:7001
slots:0-5460 (5461 slots) master
M: 8cd93a9a943b4ef851af6a03edd699a6061ace01 192.168.25.128:7002
slots:5461-10922 (5462 slots) master
M: 2935007902d83f20b1253d7f43dae32aab9744e6 192.168.25.128:7003
slots:10923-16383 (5461 slots) master
S: 74f9d9706f848471583929fc8bbde3c8e99e211b 192.168.25.128:7004
replicates 2e48ae301e9c32b04a7d4d92e15e98e78de8c1f3
S: 42cc9e25ebb19dda92591364c1df4b3a518b795b 192.168.25.128:7005
replicates 8cd93a9a943b4ef851af6a03edd699a6061ace01
S: 8b1b11d509d29659c2831e7a9f6469c060dfcd39 192.168.25.128:7006
replicates 2935007902d83f20b1253d7f43dae32aab9744e6
Can I set the above configuration? (type ‘yes’ to accept): yesNodes configuration updated
。。。。。。。。。。省略。。。。。。
test it!
注意:
可能遇到到问题
1、xxx is not Empty,请停着个xxx的节点,删除bin目录下的dump.rdb,notes.conf,note6380.conf,必要时删除/var/run/redis.pid
2、cannot connect to xxxx,你着redis配置密码了,在ruby的client里面写上你配置的密码,具体怎么找这个文件,find命令
3、执行集群启动基本一直卡在:Waiting for the cluster to join …,请在你服务器上开启16379和16380端口,反正就是10000+你redis的端口
原理图如下
eids cluster模式最小得6个服务,每个服务之间通过ping-pong机制实现相互感知(注意:此模式没有单独的哨兵监控,集群内部完全自制)。redis集群数据存储类似于hashmap,其将数据划成16484个片区(又叫插槽,每个槽可以放n多个数据),通过对key进行一定的算法与16384取模,得到其在16384中间的一个片区中。如上图姑且认为1-4;2-5;3-6,两两结对,前者为master负责写(其实也具备读能力),后者slave负责读,而且是只读,防止数据出现不一致,同时slave从master同步数据。按照上述配对,1-4主从将只负责0-5500片区的读写;2~5主从只负责5501-11000片区的读写;3-6主从负责11001-16383片区的读写,从而实现了分摊了读写的压力。
public void jedisCluster() throws Exception { // 这里建议能把所有节点都写上就写上,防止单点连接失败 Set<HostAndPort> nodes = new HashSet<>(); nodes.add(new HostAndPort(yourIp, 7001)); nodes.add(new HostAndPort(yourIp, 7002)); nodes.add(new HostAndPort(yourIp, 7003)); nodes.add(new HostAndPort(yourIp, 7004)); nodes.add(new HostAndPort(yourIp, 7005)); nodes.add(new HostAndPort(yourIp, 7006)); JedisCluster jedisCluster = new JedisCluster(nodes); jedisCluster.set("k1", "100"); String result = jedisCluster.get("k1"); System.out.println(result); jedisCluster.close();
总结;文章内容仅代表个人观点,如有不正之处,欢迎批评指正,谢谢大家。
luna脚本实现抢购秒杀方案的可能需要在测试一下,有空本人在测试一下。
有一起学习的可以后台私信“资料”领取视频资料 各大厂面试题内容包括C/C++,linux,Nginx,golang,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,ffmpeg,流媒体, 音视频,CDN,P2P,K8S,Docker,Golang,TCP/IP,协程,嵌入式,ARM,DPDK等等。。。