七叶笔记 » golang编程 » Redis集群做法的难点,百万并发客户端「实战」

Redis集群做法的难点,百万并发客户端「实战」

Redis集群详解

redis 有三种集群模式,分别是:

 * 主从模式 
*  sentinel 模式
 * Cluster模式  

三种集群模式各有特点,关于Redis介绍可以参考这里:NoSQL(二)——Redis

Redis官网: ,最新版本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 
# 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:]]*unixsocket\s/ { 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  
  • 全部启动redis:
 # 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节点上无法写入数据。

Sentinel模式

Sentinel模式介绍

主从模式的弊端就是不具备高可用性,当master挂掉以后,Redis将不能再对外提供写入操作,因此sentinel应运而生。

sentinel中文含义为哨兵,顾名思义,它的作用就是监控redis集群的运行状况,特点如下

功能作用

  1. 监控(monitoring) :Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  2. 提醒(Notifation) :当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  3. 自动故障转移(Automatic failover) :当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

部署

  同样,我们还是将每个哨兵部署在一个单独的容器中。

   sentinel配置文件

   redis-sentinel1:
  redis-sentinel2:
  redis-sentinel3:  

  这三个配置文件一模一样,都是监听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模式

部署

  • 这里使用ruby部署redis cluster,首先下载安装,只需要在一台服务器上安装即可
    yum install ruby
    yum install rubygems
  • 下载ruby运行需要的包,这里是redis-3.3.5.gem,安装
    gem install redis-3.3.5.gem
  • 拷贝redis/src/redis-trib.rb 到redis安装目的,我这里是/data/service/bin/
  • 这里需要6台服务器,为了方便,每一台服务器上起2个服务,端口分别是6379,6380
  • 修改每一个redis.conf(6台都需要),注释掉cluster-enabled yes
  • 请启动每一个redis服务
 创建启动集群的启动脚本,内容如下:
/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片区的读写,从而实现了分摊了读写的压力。

三、容灾恢复

  • 主节点(如1)宕机,从节点(如4)将上位成master
  • 主节点又活了,角色反转为slave
  • 主从成对的宕机,redis cluster将重新在剩下的节点上分配16384个片区(通过配置:cluster-require-full-coverage)

四、java如何使用

 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();  

探讨cluster模式存在的问题

  • 多键操作不支持(如mset k1 v1 k2 v2;k1,k2可能不在一个服务器上)
  • 由于多键操作不支持直接导致redis事物不支持
  • 由于多键操作不支持直接导致redis可能不支持luna脚本(未测试),那些希望通过redis搭配

总结;文章内容仅代表个人观点,如有不正之处,欢迎批评指正,谢谢大家。

luna脚本实现抢购秒杀方案的可能需要在测试一下,有空本人在测试一下。

有一起学习的可以后台私信“资料”领取视频资料 各大厂面试题内容包括C/C++,Linux,Nginx,golang,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,ffmpeg,流媒体, 音视频,CDN,P2P,K8S,Docker,Golang,TCP/IP,协程,嵌入式,ARM,DPDK等等。。。

相关文章