Redis 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot), 数据库中的每个键都属于这 16384 个哈希槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
集群中的每个节点负责处理一部分哈希槽。 举个例子, 一个集群可以有三个哈希槽, 其中:
这种将哈希槽分布到不同节点的做法使得用户可以很容易地向集群中添加或者删除节点。 比如说:
因为将一个哈希槽从一个节点移动到另一个节点不会造成节点阻塞, 所以无论是添加新节点还是移除已存在节点, 又或者改变某个节点包含的哈希槽数量, 都不会造成集群下线。
为了使得集群在一部分节点下线或者无法与集群的大多数(majority)节点进行通讯的情况下, 仍然可以正常运作, Redis 集群对节点使用了主从复制功能: 集群中的每个节点都有 1 个至 N 个复制品(replica), 其中一个复制品为主节点(master), 而其余的 N-1个复制品为从节点(slave)。
在之前列举的节点 A 、B 、C 的例子中, 如果节点 B 下线了, 那么集群将无法正常运行, 因为集群找不到节点来处理 5501 号至11000 号的哈希槽。
另一方面, 假如在创建集群的时候(或者至少在节点 B 下线之前), 我们为主节点 B 添加了从节点 B1 , 那么当主节点 B 下线的时候, 集群就会将 B1 设置为新的主节点, 并让它代替下线的主节点 B , 继续处理 5501 号至 11000 号的哈希槽, 这样集群就不会因为主节点 B 的下线而无法正常运作了。
不过如果节点 B 和 B1 都下线的话, Redis 集群还是会停止运作。
每个节点在集群中都有一个独一无二的 ID , 该 ID 是一个十六进制表示的 160 位随机数, 在节点第一次启动时由 /dev/urandom 生成。
节点会将它的 ID 保存到配置文件, 只要这个配置文件不被删除, 节点就会一直沿用这个 ID 。
节点 ID 用于标识集群中的每个节点。 一个节点可以改变它的 IP 和端口号, 而不改变节点 ID 。 集群可以自动识别出 IP/端口号的变化, 并将这一信息通过 Gossip 协议广播给其他节点知道。
以下是每个节点都有的关联信息, 并且节点会将这些信息发送给其他节点:
以上信息的其中一部分可以通过向集群中的任意节点(主节点或者从节点都可以)发送 CLUSTER NODES 命令来获得。
以下是一个向集群中的主节点发送 CLUSTER NODES 命令的例子, 该集群由三个节点组成:
$ redis-cli cluster nodes
d1861060fe6a534d42d8a19aeb36600e18785e04 :0 myself – 0 1318428930 connected 0-1364
3886e65cc906bfd9b1f7e7bde468726a052d1dae 127.0.0.1:6380 master – 1318428930 1318428931 connected 1365-2729
d289c575dcbc4bdd2931585fd4339089e461a27d 127.0.0.1:6381 master – 1318428931 1318428931 connected 2730-4095
在上面列出的三行信息中, 从左到右的各个域分别是: 节点 ID , IP 地址和端口号, 标志(flag), 最后发送 PING 的时间, 最后接收 PONG 的时间, 连接状态, 节点负责处理的槽。
目前Redis持久化的方式有两种: RDB 和 AOF
首先,我们应该明确持久化的数据有什么用,答案是用于重启后的数据恢复。
Redis是一个内存数据库,无论是RDB还是AOF,都只是其保证数据恢复的措施。
所以Redis在利用RDB和AOF进行恢复的时候,都会读取RDB或AOF文件,重新加载到内存中。
RDB就是Snapshot快照存储,是默认的持久化方式。
可理解为半持久化模式,即按照一定的策略周期性的将数据保存到磁盘。
对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。
下面是默认的快照设置:
save 900 1 #当有一条Keys数据被改变时,900秒刷新到Disk一次
save 300 10 #当有10条Keys数据被改变时,300秒刷新到Disk一次
save 60 10000 #当有10000条Keys数据被改变时,60秒刷新到Disk一次
Redis的RDB文件不会坏掉,因为其写操作是在一个新进程中进行的。
当生成一个新的RDB文件时,Redis生成的子进程会先将数据写到一个临时文件中,然后通过原子性rename系统调用将临时文件重命名为RDB文件。
这样在任何时候出现故障,Redis的RDB文件都总是可用的。
同时,Redis的RDB文件也是Redis主从同步内部实现中的一环。
Slave向Master发出同步请求,Master先dump出rdb文件,然后将rdb文件全量传输给slave,然后Master把缓存的命令转发给Slave,初次同步完成。
Master将变量的快照直接实时依次发送给各个Slave。
但不管什么原因导致Slave和Master断开重连都会重复以上两个步骤的过程。
Redis的主从复制是建立在内存快照的持久化基础上的,只要有Slave就一定会有内存快照发生。
可以很明显的看到,RDB有它的不足,就是一旦数据库出现问题,那么我们的RDB文件中保存的数据并不是全新的。从上次RDB文件生成到Redis停机这段时间的数据全部丢掉了。
AOF(Append-Only File)比RDB方式有更好的持久化性。
由于在使用AOF持久化方式时,Redis会将每一个收到的写命令都通过Write函数追加到文件中,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
对应的设置参数为:
$ vim /opt/redis/etc/redis_6379.conf
appendonly yes #启用AOF持久化方式
appendfilename appendonly.aof #AOF文件的名称,默认为appendonly.aof
# appendfsync always #每次收到写命令就立即强制写入磁盘,是最有保证的完全的持久化,但速度也是最慢的,一般不推荐使用。
appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,是受推荐的方式。
# appendfsync no #完全依赖OS的写入,一般为30秒左右一次,性能最好但是持久化最没有保证,不被推荐。
AOF的完全持久化方式同时也带来了另一个问题,持久化文件会变得越来越大。
比如我们调用INCR test命令100次,文件中就必须保存全部的100条命令,但其实99条都是多余的。
因为要恢复数据库的状态其实文件中保存一条SET test 100就够了。
为了压缩AOF的持久化文件,Redis提供了bgrewriteaof命令。
收到此命令后Redis将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件,以此来实现控制AOF文件的增长。
由于是模拟快照的过程,因此在重写AOF文件时并没有读取旧的AOF文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的AOF文件。
对应的设置参数为:
$ vim /opt/redis/etc/redis_6379.conf
no-appendfsync-on-rewrite yes #在日志重写时,不进行命令追加操作,而只是将其放在缓冲区里,避免与命令的追加造成DISK IO上的冲突。
auto-aof-rewrite-percentage 100 #当前AOF文件大小是上次日志重写得到AOF文件大小的二倍时,自动启动新的日志重写过程。
auto-aof-rewrite-min-size 64mb #当前AOF文件启动新的日志重写过程的最小值,避免刚刚启动Reids时由于文件尺寸较小导致频繁的重写。
到底选择什么呢?下面是来自官方的建议:
通常,如果你要想提供很高的数据保障性,那么建议你同时使用两种持久化方式。
如果你可以接受灾难带来的几分钟的数据丢失,那么你可以仅使用RDB。
很多用户仅使用了AOF,但是我们建议,既然RDB可以时不时的给数据做个完整的快照,并且提供更快的重启,所以最好还是也使用RDB。
因此,我们希望可以在未来(长远计划)统一AOF和RDB成一种持久化模式。
在数据恢复方面:
RDB的启动时间会更短,原因有两个:
一是RDB文件中每一条数据只有一条记录,不会像AOF日志那样可能有一条数据的多次操作记录。所以每条数据只需要写一次就行了。
另一个原因是RDB文件的存储格式和Redis数据在内存中的编码格式是一致的,不需要再进行数据编码工作,所以在CPU消耗上要远小于AOF日志的加载。
我们使用3台虚拟机模拟6个节点,使用端口区分,每台虚拟机2个节点
HOST | IP | PORT | |
Redis1 | 192.168.44.10 | 7001 | 7004 |
Redis2 | 192.168.44.11 | 7002 | 7005 |
Redis3 | 192.168.44.12 | 7003 | 7006 |
Redis默认端口6379,这里为了方便,所以使用7001-7006来进行区分
下面的配置,每台机器都要做
[root@localhost ~]# vim /etc/hostname
我们使用网易的yum源,速度快
root@redis1 ~]# cd /etc/yum.repos.d/
[root@redis1 yum.repos.d]# mkdir old
[root@redis1 yum.repos.d]# mv CentOS-* old/
[root@redis1 yum.repos.d]# wget http://mirrors.163.com/.help/CentOS6-Base-163.repo
[root@redis1 yum.repos.d]# mv CentOS6-Base-163.repo CentOS7-Base-163.repo
[root@redis1 yum.repos.d]# vim CentOS7-Base-163.repo
把里面的所有baseurl属性值的$releasever为对应的OS主版本号(5,6,7)
改成下图
清理yum
[root@redis1 yum.repos.d]# yum clean all
[root@redis1 yum.repos.d]# yum makecache
[root@redis1 yum.repos.d]# vim /etc/selinux/config
[root@redis1 yum.repos.d]# setenforce 0
[root@redis1 yum.repos.d]# getenforce
都升级下到最新
[root@redis1 yum.repos.d]# yum update
[root@redis1 yum.repos.d]# yum -y install gcc gcc-c++ libstdc++-devel zlib-devel
[root@redis3 yum.repos.d]# cd /usr/local/
[root@redis3 local]# wget http://download.redis.io/releases/redis-4.0.9.tar.gz
[root@redis3 local]# tar zxvf redis-4.0.9.tar.gz
改下文件名
[root@redis3 local]# mv redis-4.0.9 redis
[root@redis3 local]# rm -rf redis-4.0.9.tar.gz
进行编译
[root@redis3 local]# cd redis/
[root@redis3 redis]# make
[root@redis3 redis]# make install
进入/usr/local/bin可以看到下面几个东西。
[root@redis1 redis]# cd /usr/local/bin/
[root@redis1 bin]# ls
查看下redis版本
[root@redis1 ~]# redis-server -v
[root@redis1 ~]# cd /usr/local/redis/
创建一个redis-cluster的文件夹并在里面分别创建7001和7004文件夹用来区分节点
[root@redis1 redis]# mkdir -p redis-cluster/{7001,7004}
[root@redis1 redis]# cp redis.conf redis-cluster/7001/redis-7001.conf
修改下配置
[root@redis1 redis]# vim redis-cluster/7001/redis-7001.conf
bind 192.168.44.10 #改为设定的端口
port 7001 #改为设定的端口
daemonize yes #后台启动
pidfile /var/run/redis_7001.pid #pidfile文件
appendonly yes #aof日志开启 有需要就开启,它会每次写操作都记录一条日志
cluster-enabled yes #启动集群模式
cluster-config-file nodes-7001.conf #集群内部配置文件,改掉端口号
cluster-node-timeout 15000 #节点超时时间,单位:毫秒
同样配置下7004端口,只要把端口和pid文件还有cluster nodes改成7004
[root@redis1 redis]# cp redis-cluster/7001/redis-7001.conf redis-cluster/7004/redis-7004.conf
[root@redis1 redis]# vim redis-cluster/7004/redis-7004.conf
[root@redis2 redis]# cd /usr/local/redis/
[root@redis2 redis]# mkdir -p redis-cluster/{7002,7005}
配置文件修改下端口同上
[root@redis3 redis]# cd /usr/local/redis/
[root@redis3 redis]# mkdir -p redis-cluster/{7003,7006}
配置文件修改下端口同上
在节点1上开通7001和7004,其他节点开通各自对应的端口,并且需要开通群集总线端口
集群总线端口为redis客户端连接的端口 + 10000
如redis端口为6379
则集群总线端口为16379
故,所有服务器的点需要开通redis的客户端连接端口和集群总线端口
不开通总线端口会发现到时候集群创建的时候一直显示join
[root@redis1 redis]# firewall-cmd –permanent –zone=public –add-port=7001/tcp
[root@redis1 redis]# firewall-cmd –permanent –zone=public –add-port=7004/tcp
[root@redis1 ~]# firewall-cmd –permanent –zone=public –add-port=17001/tcp
[root@redis1 ~]# firewall-cmd –permanent –zone=public –add-port=17004/tcp
[root@redis1 redis]# firewall-cmd –reload
[root@redis1 redis]# redis-server /usr/local/redis/redis-cluster/7001/redis-7001.conf
[root@redis1 redis]# redis-server /usr/local/redis/redis-cluster/7004/redis-7004.conf
redis-server /usr/local/redis/redis-cluster/7002/redis-7002.conf
redis-server /usr/local/redis/redis-cluster/7005/redis-7005.conf
redis-server /usr/local/redis/redis-cluster/7003/redis-7003.conf
redis-server /usr/local/redis/redis-cluster/7006/redis-7006.conf
检查各redis节点启动情况
redis是否启动成功和监听端口
[root@redis1 ~]# ps -ef | grep redis
[root@redis1 ~]# netstat -tnlp | grep redis
Redis 官方提供了 redis-trib.rb 这个工具,就在解压目录的 src 目录中,第三步中已将它复制到 /usr/local/bin 目录中,可以直接在命令行中使用了。使用下面这个命令即可完成安装。
[root@redis1 redis]# cd /usr/local/redis/src/
[root@redis1 src]# cp redis-trib.rb /usr/local/bin/
redis-trib.rb是采用Ruby实现的Redis集群管理工具。内部通过Cluster相关命令帮助我们简化集群创建、检查、槽迁移和均衡等常见操作,使用之前需要安装Ruby依赖环境
[root@redis1 src]# yum -y install ruby ruby-devel rubygems rpm-build
我们需要换下源来升级下ruby,默认的是2.0的版本太老了,redis需要2.2以上的版本,否则报下面的错误
需要解决上面的报错先安装rvm,在升级ruby。
先执行一条官方https://rvm.io/上面的命令
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
在执行一条
\curl -sSL https://get.rvm.io | bash -s stable
上面表示执行成功
[root@redis1 yum.repos.d]# source /etc/profile.d/rvm.sh
List一下可以安装的版本
[root@redis1 yum.repos.d]# rvm list known
安装ruby 2.4.1
[root@redis1 yum.repos.d]# rvm install 2.4.1
这部可能要等一会,速度很慢,国外网站下载来的
[root@redis1 yum.repos.d]# rvm use 2.4.1
[root@redis1 yum.repos.d]# rvm remove 2.0.0
[root@redis1 yum.repos.d]# ruby –version
[root@redis1 src]# gem install redis
[root@redis1 ~]# redis-trib.rb create –replicas 1 192.168.44.10:7001 192.168.44.11:7002 192.168.44.12:7003 192.168.44.10:7004 192.168.44.11:7005 192.168.44.12:7006
–replicas参数指定集群中每个主节点配备几个从节点,这里设置为1。
这里因为测试,只用了3台机器,如果部署节点使用不同的IP地址,redis-trib-rb会尽可能保证主从节点不分配在同一台机器下,因此会重新排序节点表顺序。节点列表顺序用于确定主从角色,先主节点之后是从节点。
创建过程中首先会给出主从节点角色分配的计划,如下图:
输入yes
[root@redis1 ~]# redis-trib.rb check 192.168.44.10:7001
[root@redis1 ~]# redis-trib.rb info 192.168.44.10:7001
[root@redis1 ~]# redis-cli -h 192.168.44.10 -p 7001 -c
[root@redis1 ~]# redis-cli -h 192.168.44.10 -p 7001 -c
192.168.44.10:7001> config set masterauth passwd123
192.168.44.10:7001> config set requirepass passwd123
每个节点都先这样设置一遍,最后在每个节点还要执行下
192.168.44.12:7003> auth passwd123
192.168.44.12:7003> config rewrite
我们这时候在去看redis的配置文件,发现密码都已经加入进去了
现在访问集群时需要加上密码
./redis-cli -c -p 7000 -a passwd123
安装的时候由于防火墙问题,导致群集第一次启动失败,然后再次启动报错
删除aof,nodes.conf,rdb的文件还是报错
最后在每个节点上执行了下面的命令终于成功
redis-cli -p 端口号-h 主机物理地址 FLUSHALL
redis-cli -p 端口号-h 主机物理地址 CLUSTER RESET SOFT