redis学习笔记

一、简介

Rediskey-value(键值对)的存储系统。支持存储的value类型相对更多,包括stringlistset(集合)、zset(有序集合)和hash(哈希类型),算一种nosql内存型缓存数据库。

NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。NoSQL一般应用于超大规模数据的存储。关系型数据库已经不适合处理当今成倍数据量增加的大型互联网商务应用了。这时nosql数据库由于其更高的性能、更快的速度、更好的可扩展性等优势对解决大规模数据起到了良好的应用作用。

NoSQL四大分类

分类 Examples举例 典型应用场景 数据模型 优点 缺点
键值(key-value) Redis,Tokyo Cabinet/Tyrant, Voldemort, Oracle BDB 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。 Key 指向 Value 的键值对,通常用hash table来实现 查找速度快 数据无结构化,通常只被当作字符串或者二进制数据
列存储数据库 Cassandra, HBase, Riak 分布式的文件系统 以列簇式存储,将同一列数据存在一起 查找速度快,可扩展性强,更容易进行分布式扩展 功能相对局限
文档型数据库 CouchDB, MongoDb Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) Key-Value对应的键值对,Value为结构化数据 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 查询性能不高,而且缺乏统一的查询语法。
图形(Graph)数据库 Neo4J, InfoGrid, Infinite Graph 社交网络,推荐系统等。专注于构建关系图谱 图结构 利用图结构相关算法。比如最短路径寻址,N度关系查找等 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案。

NoSQL适用场景:1、数据模型比较简单;2、需要灵活性更强的IT系统;3、对数据库性能要求较高;4、不需要高度的数据一致性;5、对于给定key,比较容易映射复杂值的环境。

通俗来说,Oraclemysqlsqlserver等这样的关系型数据库主要用于存放持久化数据,将数据存储在硬盘中,读取速度较慢。而redis主要将数据存储在缓存中,缓存的读取速度快,能够大大的提高运行效率,但相应的保存时间有限。

使用Redis的情景一般是会话缓存(session cache),用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化,即可以将内存中的数据序列化到硬盘中,如此以确保用户数据的安全性。此外Redis哎集群方面具有很好的优势。

NoSQL优缺点

优点

数据高并发读写

海量数据存储和访问效率高

对数据可扩展且高可用

缺点

ACID相比于关系型数据库过于简单

无法做太复杂的关系数据库模型

二、常用命令大全

1. key相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ keys *  #查询当前库的所有键  
$ EXISTS key #检查给定 key 是否存在
$ DUMP key #序列化给定key,并返回被序列化的值
$ EXPIRE key seconds #为给定 key 设置过期时间,以秒计。
$ EXPIREAT key timestamp # EXPIREAT 的作用和EXPIRE 类似,都用于为 key 设置过期时间。 不同在于 EXPIREAT 命令接受的时间参数是 UNIX时间戳(unix timestamp)
$ KEYS pattern #查找所有符合给定模式( pattern)的 key。
$ MOVE key db #将当前数据库的 key移动到给定的数据库 db 当中。
$ PERSIST key #移除key的过期时间,key将持久保持。
$ PTTL key #以毫秒为单位返回key的剩余的过期时间。
$ TTL key #以秒为单位,返回给定key的剩余生存时间(TTL, time to live)。
$ RANDOMKEY #从当前数据库中随机返回一个 key。
$ RENAME key newkey #修改 key的名称
$ RENAMENX key newkey # 仅当newkey不存在时,将 key改名为 newkey。
$ TYPE key #返回key所储存的值的类型。

案例

(1)查询当前库的所有键

1
2
127.0.0.1:6379> keys *
(empty list or set)

(2)修改 key的名称

1
2
3
4
127.0.0.1:6379> set name qcmoke
OK
127.0.0.1:6379> rename name username
OK

2. String相关

1
2
3
4
5
6
7
$ set key value  #添加键值对
$ setnx key value #如果不存在key则设置,存在则不设置
$ get key #查询对应键值
$ del key #删除对应键值
$ setex key seconds value #设置key/value的有效期,到时后变为nil(空)
$ SETRANGE KEY_NAME OFFSET VALUE #指定的字符串覆盖给定 key 所储存的字符串值,覆盖的位置从偏移量 offset 开始
$ GETSET key value #将给定 key 的值设为 value ,并返回 key 的旧值(old value)。

案例

(1)添加键值对

  • set key value
1
2
127.0.0.1:6379> set name zhangsan
OK

如果已经存在相应的key/value,那么会将原本的值覆盖掉。添加完成后,会根据配置文件中的dir的路径每隔一段时间序列化生成dump.rdb文件。这个文件可以备份和删除,但删除了相应的数据库中就没有相应的数据了,故不要轻易的删除,这很危险。

  • setnx key value

如果不存在key则设置,如果存在key则不设置

1
2
127.0.0.1:6379> setnx name qcmoke
(integer) 1

(2)查询对应键值

  • get key
1
2
127.0.0.1:6379> get name
"zhangsan"

(3)删除已存在的键

  • del key
1
2
127.0.0.1:6379> del name
(integer) 1

3. 哈希(Hash)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ HDEL key field1 [field2]  #删除一个或多个哈希表字段
$ HEXISTS key field #查看哈希表 key 中,指定的字段是否存在。
$ HGET key field #获取存储在哈希表中指定字段的值。
$ HGETALL key #获取在哈希表中指定 key 的所有字段和值
$ HINCRBY key field increment #为哈希表 key 中的指定字段的整数值加上增量 increment
$ HINCRBYFLOAT key field increment #为哈希表 key 中的指定字段的浮点数值加上增量 increment 。
$ HKEYS key #获取所有哈希表中的字段
$ HLEN key #获取哈希表中字段的数量
$ HMGET key field1 [field2] #获取所有给定字段的值
$ HMSET key field1 value1 [field2 value2 ] #同时将多个 field-value (域-值)对设置到哈希表 key 中。
$ HSET key field value #将哈希表 key 中的字段 field 的值设为 value 。
$ HSETNX key field value #只有在字段 field 不存在时,设置哈希表字段的值。
$ HVALS key #获取哈希表中所有值
$ HSCAN key cursor [MATCH pattern] [COUNT count] #迭代哈希表中的键值对。

案例

(1)将哈希表 key 中的字段 field 的值设为 value

1
127.0.0.1:6379> hmset user age 21

(2)将多个 field-value (域-值)对设置到哈希表 key 中

1
127.0.0.1:6379> hmset user name zhangsan description "a good boy"

(3)从hash里获取指定字段的值

1
2
3
127.0.0.1:6379> hget user name
"zhangsan"
127.0.0.1:6379>

(4)从hash获取指定 key 的所有字段和值

1
2
3
4
5
6
7
127.0.0.1:6379> hgetall user
1) "name"
2) "zhangsan"
3) "description"
4) "a good boy"
5) "age"
6) "21"

(5)从hash获取指定 key 的所有值

1
2
3
4
127.0.0.1:6379> hvals user
1) "zhangsan"
2) "a good boy"
3) "21

4. 列表(List)

根据收尾添加的不同,既可以实现栈,也可以实现队列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ BLPOP key1 [key2 ] timeout # 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
$ BRPOP key1 [key2 ] timeout # 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
$ BRPOPLPUSH source destination timeout # 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
$ LINDEX key index # 通过索引获取列表中的元素
$ LINSERT key BEFORE|AFTER pivot value # 在列表的元素前或者后插入元素
$ LLEN key # 获取列表长度
$ LPOP key # 移出并获取列表的第一个元素
$ LPUSH key value1 [value2] # 将一个或多个值插入到列表头部,实现先进后出的效果(栈)
$ LPUSHX key value # 将一个值插入到已存在的列表头部
$ LRANGE key start stop # 获取列表指定范围内的元素
$ LREM key count value # 移除列表元素
$ LSET key index value # 通过索引设置列表元素的值
$ LTRIM key start stop # 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
$ RPOP key # 移除列表的最后一个元素,返回值为移除的元素。
$ RPOPLPUSH source destination # 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
$ RPUSH key value1 [value2] # 在列表中末尾添加一个或多个值,实现先进先出的效果(队列)
$ RPUSHX key value # 为已存在的列表添加值

案例

(1)将一个或多个值插入到列表头部

1
2
3
4
127.0.0.1:6379> lpush lkey zhangsan
(integer) 1
127.0.0.1:6379> lpush lkey lisi wangwu
(integer) 3

(2)获取列表指定范围内的元素

1
2
3
4
127.0.0.1:6379> LRANGE lkey 0 10
1) "wangwu"
2) "lisi"
3) "zhangsan"

5. 集合Set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ SADD key member1 [member2]    #向集合添加一个或多个成员
$ SCARD key #获取集合的成员数
$ SDIFF key1 [key2] #返回给定所有集合的差集
$ SDIFFSTORE destination key1 [key2] #返回给定所有集合的差集并存储在 destination 中
$ SINTER key1 [key2] #返回给定所有集合的交集
$ SINTERSTORE destination key1 [key2] #返回给定所有集合的交集并存储在 destination 中
$ SISMEMBER key member #判断 member 元素是否是集合 key 的成员
$ SMEMBERS key #返回集合中的所有成员
$ SMOVE source destination member #将 member 元素从 source 集合移动到 destination 集合
$ SPOP key #移除并返回集合中的一个随机元素
$ SRANDMEMBER key [count] #返回集合中一个或多个随机数
$ SREM key member1 [member2] #移除集合中一个或多个成员
$ SUNION key1 [key2] #返回所有给定集合的并集
$ SUNIONSTORE destination key1 [key2] #所有给定集合的并集存储在 destination 集合中
$ SSCAN key cursor [MATCH pattern] [COUNT count] #迭代集合中的元素

案例

(1)向集合添加一个或多个成员

1
2
3
4
5
6
127.0.0.1:6379> sadd skey zhangsan
(integer) 1
127.0.0.1:6379> sadd skey lisi
(integer) 1
127.0.0.1:6379> sadd skey wangwu zhaoliu
(integer) 1

(2)返回集合中的所有成员

1
2
3
4
5
127.0.0.1:6379> smembers skey
1) "zhaoliu"
2) "zhangsan"
3) "wangwu"
4) "lisi"

6. 有序集合(sorted set)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ ZADD key score1 member1 [score2 member2]  #向有序集合添加一个或多个成员,或者更新已存在成员的分数
$ ZCARD key #获取有序集合的成员数
$ ZCOUNT key min max #计算在有序集合中指定区间分数的成员数
$ ZINCRBY key increment member #有序集合中对指定成员的分数加上增量 increment
$ ZINTERSTORE destination numkeys key [key ...] # 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中
$ ZLEXCOUNT key min max # 在有序集合中计算指定字典区间内成员数量
$ ZRANGE key start stop [WITHSCORES] # 通过索引区间返回有序集合成指定区间内的成员
$ ZRANGEBYLEX key min max [LIMIT offset count] # 通过字典区间返回有序集合的成员
$ ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] # 通过分数返回有序集合指定区间内的成员
$ ZRANK key member # 返回有序集合中指定成员的索引
$ ZREM key member [member ...] # 移除有序集合中的一个或多个成员
$ ZREMRANGEBYLEX key min max # 移除有序集合中给定的字典区间的所有成员
$ ZREMRANGEBYRANK key start stop # 移除有序集合中给定的排名区间的所有成员
$ ZREMRANGEBYSCORE key min max # 移除有序集合中给定的分数区间的所有成员
$ ZREVRANGE key start stop [WITHSCORES] # 返回有序集中指定区间内的成员,通过索引,分数从高到底
$ ZREVRANGEBYSCORE key max min [WITHSCORES] # 返回有序集中指定分数区间内的成员,分数从高到低排序
$ ZREVRANK key member # 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
$ ZSCORE key member # 返回有序集中,成员的分数值
$ ZUNIONSTORE destination numkeys key [key ...] # 计算给定的一个或多个有序集的并集,并存储在新的 key 中
$ ZSCAN key cursor [MATCH pattern] [COUNT count] # 迭代有序集合中的元素(包括元素成员和元素分值)

案例

(1)向有序集合添加一个或多个成员,或者更新已存在成员的分数

1
2
3
4
5
6
7
127.0.0.1:6379> zadd sskey 1 zhangsan
(integer) 1
127.0.0.1:6379> zadd sskey 2 lisi
(integer) 1
127.0.0.1:6379> zadd sskey 2 wangwu
(integer) 1
127.0.0.1:6379> zadd sskey 3 zhaoliu

(2)过索引区间返回有序集合成指定区间内的成员

1
2
3
4
5
127.0.0.1:6379> zrange sskey 0 10
1) "zhangsan"
2) "lisi"
3) "wangwu"
4) "zhaoliu"

(3)取分数和值

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> zrange sskey 0 10 WITHSCORES
1) "zhangsan"
2) "1"
3) "lisi"
4) "2"
5) "wangwu"
6) "2"
7) "zhaoliu"
8) "3"

7. 高级命令

1
2
3
4
5
6
$ select [数据库下标] #选择数据库,数据库为0--15(16个),默认进入第0个
$ move [key] [数据库下标] #将当前数据库的key移动到其他数据库
$ info #查看redis信息
$ flushdb #清空当前数据库的key
$ flushall #清空所有数据库的key
$ dbsize #查看当前数据库key的数量

案例

(1)切换到下标为某个数字的数据库

1
2
127.0.0.1:6379> select 1
OK

(2)清空当前数据库的key

1
2
3
4
5
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379>

关键字可大小写

更多命令请参考:https://redis.io/commands

三、部署方案

为了实现Redis的高并发高可用应用,Redis支持单机主从哨兵集群多种架构模式部署方案。

单机模式

概念

image-20220827140851804

单机模式顾名思义就是安装一个 Redis,启动起来,业务调用即可。例如一些简单的应用,并非必须保证高可用的情况下可以使用该模式。

优缺点

优点

  • 部署简单;
  • 成本低,无备用节点;
  • 高性能,单机不需要同步数据,数据天然一致性。

缺点

  • 可靠性保证不是很好,单节点有宕机的风险。
  • 单机高性能受限于 CPU 的处理能力,Redis 是单线程的。

单机 Redis 能够承载的 QPS(每秒查询速率)大概在几万左右。取决于业务操作的复杂性,Lua 脚本复杂性就极高。假如是简单的 key value 查询那性能就会很高。假设上千万、上亿用户同时访问 Redis,QPS 达到 10 万+。这些请求过来,单机 Redis 直接就挂了。系统的瓶颈就出现在 Redis 单机问题上,此时我们可以通过主从复制解决该问题,实现系统的高并发。

安装部署

1. window版安装

步骤大致如下:

(1)下载

下载地址:https://github.com/microsoftarchive/redis/releases

比如下载 Redis-x64-3.0.504.zip

(2)启动和关闭

解压到任意目录后,点击安装目录里的redis-server.exe即可启动,关闭redis-server.exe启动的窗口即可关闭。

(3)开启后台守护进程

cmd进入安装目录并执行以下命令即可安装成系统守护进程服务。(当然你可以将安装目录放到环境变量中,就可以在任意目录执行redis命令了,这里为了简便就不做此操作了)

1
redis-server --service-install redis.windows-service.conf --loglevel verbose

(4)启动和关闭守护进程服务

1
2
3
4
#启动(如果启动失败,在安装目录里创建一个名称为Logs的文件夹即可)
redis-server --service-start
#关闭
redis-server --service-stop

(4)卸载

  • 如果没有开启守护进程服务,那么直接删除安装目录即可卸载。

  • 如果开启守护进程服务,那么可在安装目录里通过执行以下命令来卸载守护进程服务,执行该命令后删除安装目录即可完全卸载。

1
redis-server --service-uninstall

更多内容可参考菜鸟教程

2. Linux版安装

这里主要讲linux下编译安装。

(1)下载

下载地址: https://redis.io/download ,这里使用的是3.0版本。

1
2
#下载redis-3.0.0
$ wget http://download.redis.io/releases/redis-3.0.0.tar.gz

(2)安装

1
2
3
4
5
#解压到/usr/local/
$ tar -zxvf redis-3.0.0.tar.gz -C /usr/local/
# 进入到解压的文件中并执行make进行编译
$ cd /usr/local/redis-3.0.0
$ make

看到类似“Hint: It's a good idea to run 'make test' ;)”这样的提示表示编译完成。make 后编译好的文件会保存到src目录下。这些文件中有两个重要的文件:

redis-server 服务端程序

redis-cli 客户端程序

其实这个时候我们已经可以执行redis-serverredis-cli 直接使用了,但为了更好的管理程序,我们可以将这些编译好的文件和配置文件移动到其他目录去。如/usr/local/redis/bin//usr/local/redis/etc/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#创建两个目录用来存放redis配置文件和编译好的文件
$ mkdir -p /usr/local/redis/bin/
$ mkdir -p /usr/local/redis/etc/

#进入编译目录
$ cd /usr/local/redis-3.0.0/src
#copy编译好的可执行文件到/usr/local/redis/bin/,注意下面从“cp /usr/..”直到“/usr/local/redis/bin/”是一条完整的命令。
$ cp /usr/local/redis-3.0.0/src/mkreleasehdr.sh \
redis-benchmark \
redis-check-aof \
redis-check-dump \
redis-cli \
redis-server \
redis-sentinel \
redis-trib.rb \
/usr/local/redis/bin/

#copy redis配置文件到/usr/local/redis/etc/
$ cp /usr/local/redis-3.0.0/redis.conf /usr/local/redis/etc/

(3)卸载

直接删除/usr/local/redis-3.0.0/usr/local/redis/bin//usr/local/redis/etc/目录即可完成卸载。

(4)启动服务端

1
2
#redis-server + redis配置文件 一起启动服务
$ /usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf

可以通过打开另一个终端执行ps -ef | grep redis 或者 ps -ef | grep 6379查看redis线程。

可以按Ctrl+C中断停止服务端启动。

(5)配置服务端

默认开启redis-server需要保持当前终端开启才能使用,如果关闭当前终端或者中断进程了那么服务就关闭了,如果希望后台启守护进程,我们可以修改redis的配置文件,修改daemonize的值为yes来实现。

1
2
3
$ vim /usr/local/redis/etc/redis.conf
#默认daemonize为no,修改为yes表示开启守护进程
daemonize yes

注意重启redis-server后才能生效

以下是其他配置,但学习的话并不建议

1
2
3
4
5
port 6379  #默认端口,可以修改
timeout 0 #客户端空闲N秒后关闭连接

#dir ./ #指定工作目录,日志文件和数据文件等存放路径
dir /usr/local/redis/etc/

(6)启动服务端守护进程

1
$ /usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf

(7)关闭服务端守护进程

1
$ /usr/local/redis/bin/redis-cli shutdown

(8)客户端使用

1)启动客户端

1
$ /usr/local/redis/bin/redis-cli

2)关闭客户端

Ctrl+C就能停止客户端启动

3. docker版安装

可参考:https://www.qcmoke.site/devops/docker.html#5-安装redis

主从复制

概念

image-20220827140912887

Redis 的复制(Replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(Master),而通过复制创建出来的复制品则为从服务器(Slave)。 只要主从服务器之间的网络连接正常,主服务器就会将写入自己的数据同步更新给从服务器,从而保证主从服务器的数据相同。

数据的复制是单向的,只能由主节点到从节点,简单理解就是从节点只支持读操作,不允许写操作。主要是读高并发的场景下用主从架构。主从模式需要考虑的问题是:当 Master 节点宕机,需要选举产生一个新的 Master 节点,从而保证服务的高可用性。

优缺点

优点

  • Master/Slave 角色方便水平扩展,QPS 增加,增加 Slave 即可;
  • 降低 Master 读压力,转交给 Slave 节点;
  • 主节点宕机,从节点作为主节点的备份可以随时顶上继续提供服务;

缺点

  • 可靠性保证不是很好,主节点故障便无法提供写入服务;
  • 没有解决主节点写的压力;
  • 数据冗余(为了高并发、高可用和高性能,一般是允许有冗余存在的);
  • 一旦主节点宕机,从节点晋升成主节点,需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预;
  • 主节点的写能力受到单机的限制;
  • 主节点的存储能力受到单机的限制。

安装部署

这里部署节点架构为:1主2从。

因为这里只是学习使用所以就都搭建在同一台服务器上了(即所谓为集群),如果要在生产上使用则应该每个redis节点都应该是一台服务器。为了简化部署过程,这里通过使用redis配置模板文件和docker compose来简化搭建过程。

redis配置

创建配置模板文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
mkdir -p /opt/redis/conf

##创建主节点配置文件模板
cat > /opt/redis/conf/redis-master.tmpl << 'EOF'
#节点端口6379、6380、6381
port ${PORT}
#访问认证密码
requirepass ${requirepass}
#如果主节点开启了访问认证,从节点访问主节点需要认证的密码
masterauth ${masterauth}
#保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
protected-mode no
#是否以守护线程的方式启动(后台启动),默认 no;
daemonize no
#是否开启 AOF 持久化模式,默认 no;
appendonly yes
#是否开启集群模式,默认 no;
cluster-enabled no
EOF


##创建从节点配置文件模板
cat > /opt/redis/conf/redis-slave.tmpl << 'EOF'
#节点端口6379、6380、6381
port ${PORT}
#访问认证密码
requirepass ${requirepass}
#如果主节点开启了访问认证,从节点访问主节点需要认证的密码
masterauth ${masterauth}
#保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
protected-mode no
#是否以守护线程的方式启动(后台启动),默认 no;
daemonize no
#是否开启 AOF 持久化模式,默认 no;
appendonly yes
#是否开启集群模式,默认 no;
cluster-enabled no
#给当前从节点指定主节点的ip和端口
slaveof ${masterIp} ${masterPort}
EOF

通过模板文件动态生成所有节点的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#主从节点的密码
requirepass=123456
#从节点访问主节点需要认证的密码
masterauth=123456
#主节点ip
masterIp=192.168.60.200
#主节点端口
masterPort=6379
#从节点的所有端口
masterPorts="6380 6381"

##生成主节点的配置文件
PORT=${masterPort} \
requirepass=$requirepass \
masterauth=$masterauth \
envsubst < /opt/redis/conf/redis-master.tmpl > /opt/redis/conf/redis-master.conf

#生成从节点的配置文件
count=1
for port in $masterPorts; do
masterIp=$masterIp \
masterPort=$masterPort \
PORT=${port} \
requirepass=$requirepass \
masterauth=$masterauth \
envsubst < /opt/redis/conf/redis-slave.tmpl > /opt/redis/conf/redis-slave-${count}.conf
let count++
done
docker配置
1
2
mkdir -p /opt/redis
vi /opt/redis/docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
version: '3'
services:
master:
image: redis
container_name: redis-master
restart: always
command: redis-server /etc/redis/redis.conf
#容器启动时执行的命令,以下是redis-server启动时指定配置文件,后面可加参数如"--port 6379 --appendonly yes"等参数覆盖配置文件里的一些配置
#command: redis-server /etc/redis/redis.conf --port 6379 --appendonly yes
ports:
- 6379:6379
volumes:
- /opt/redis/conf/redis-master.conf:/etc/redis/redis.conf
- /opt/redis/data/redis-master:/data
slave1:
image: redis
container_name: redis-slave-1
restart: always
command: redis-server /etc/redis/redis.conf
ports:
- 6380:6380
volumes:
- /opt/redis/conf/redis-slave-1.conf:/etc/redis/redis.conf
- /opt/redis/data/redis-slave-1:/data
slave2:
image: redis
container_name: redis-slave-2
restart: always
command: redis-server /etc/redis/redis.conf
ports:
- 6381:6381
volumes:
- /opt/redis/conf/redis-slave-2.conf:/etc/redis/redis.conf
- /opt/redis/data/redis-slave-2:/data
1
2
#创建并启动所有服务容器
docker-compose up -d

哨兵模式

概念

image-20220827141006536

主从模式中,当主节点宕机之后,从节点是可以作为主节点顶上来继续提供服务,但是需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预。

于是,在 Redis 2.8 版本开始,引入了哨兵(Sentinel)这个概念,在主从复制的基础上,哨兵实现了自动化故障恢复。如上图所示,哨兵模式由两部分组成,哨兵节点和数据节点:

  • 哨兵节点:哨兵节点是特殊的 Redis 节点,不存储数据;
  • 数据节点:主节点和从节点都是数据节点。

Redis Sentinel 是分布式系统中监控 Redis 主从服务器,并提供主服务器下线时自动故障转移功能的模式。其中三个特性为:

  • 监控(Monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常;
  • 提醒(Notification):当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知;
  • 自动故障迁移(Automatic failover):当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作。

优缺点

优点

  • 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都有;
  • 主从可以自动切换,系统更健壮,可用性更高;
  • Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。

缺点

  • 主从切换需要时间,会丢失数据;
  • 还是没有解决主节点写的压力;
  • 主节点的写能力,存储能力受到单机的限制;
  • 动态扩容困难复杂,对于集群,容量达到上限时在线扩容会变得很复杂。

安装部署

这里部署节点架构为:1主2从3哨兵。

因为这里只是学习使用所以就都搭建在同一台服务器上了(即所谓为集群),如果要在生产上使用则应该每个redis节点都应该是一台服务器。为了简化部署过程,这里通过使用redis配置模板文件和docker compose来简化搭建过程。

redis配置

创建配置模板文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
mkdir -p /opt/redis/conf

##创建主节点配置文件模板
cat > /opt/redis/conf/redis-master.tmpl << 'EOF'
#节点端口6379、6380、6381
port ${PORT}
#访问认证密码
requirepass ${requirepass}
#如果主节点开启了访问认证,从节点访问主节点需要认证的密码
masterauth ${masterauth}
#保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
protected-mode no
#是否以守护线程的方式启动(后台启动),默认 no;
daemonize no
#是否开启 AOF 持久化模式,默认 no;
appendonly yes
#是否开启集群模式,默认 no;
cluster-enabled no
EOF


##创建从节点配置文件模板
cat > /opt/redis/conf/redis-slave.tmpl << 'EOF'
#节点端口6379、6380、6381
port ${PORT}
#访问认证密码
requirepass ${requirepass}
#如果主节点开启了访问认证,从节点访问主节点需要认证的密码
masterauth ${masterauth}
#保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
protected-mode no
#是否以守护线程的方式启动(后台启动),默认 no;
daemonize no
#是否开启 AOF 持久化模式,默认 no;
appendonly yes
#是否开启集群模式,默认 no;
cluster-enabled no
#给当前从节点指定主节点的ip和端口
slaveof ${masterIp} ${masterPort}
EOF


##创建哨兵节点配置文件模板
cat > /opt/redis/conf/redis-sentinel.tmpl << 'EOF'
#节点端口 26379、26380、26381
port ${PORT}
#保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问
protected-mode no
#是否以守护线程的方式启动(后台启动),默认 no
daemonize no
#是否开启 AOF 持久化模式,默认 no
appendonly yes
# 第三个参数:哨兵名字,可自行修改。(若修改了,那后面涉及到的都得同步)
# 第四个参数:master主机ip地址
# 第五个参数:redis端口号
# 第六个参数:哨兵的数量。比如2表示,当至少有2个哨兵发现master的redis挂了,那么就将此master标记为宕机节点。这个时候就会进行故障的转移,将其中的一个从节点变为master
sentinel monitor mymaster ${masterIp} ${masterPort} 2
# 哨兵从master节点宕机后,等待多少时间(毫秒),认定master不可用。默认30s
sentinel down-after-milliseconds mymaster 30000
# 当替换主节点后,剩余从节点并行同步的数量,默认为 1
sentinel parallel-syncs mymaster 1
# master中redis的密码
sentinel auth-pass mymaster ${masterauth}
# 主备切换的时间,若在3分钟内没有切换成功,换另一个从节点切换
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
EOF

通过模板文件动态生成所有节点的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#主从节点的密码
requirepass=123456
#从节点或者哨兵节点访问主节点需要认证的密码
masterauth=123456
#主节点ip
masterIp=192.168.60.200
#主节点端口
masterPort=6379
#从节点的所有端口
masterPorts="6380 6381"
#哨兵的所有节点端口
sentinelPorts="26379 26380 26381"


##生成主节点的配置文件
PORT=${masterPort} \
requirepass=$requirepass \
masterauth=$masterauth \
envsubst < /opt/redis/conf/redis-master.tmpl > /opt/redis/conf/redis-master.conf

#生成从节点的配置文件
count=1
for port in $masterPorts; do
masterIp=$masterIp \
masterPort=$masterPort \
PORT=${port} \
requirepass=$requirepass \
masterauth=$masterauth \
envsubst < /opt/redis/conf/redis-slave.tmpl > /opt/redis/conf/redis-slave-${count}.conf
let count++
done

#生成所有哨兵节点的配置文件
count=1
for port in $sentinelPorts; do
masterIp=$masterIp \
masterPort=$masterPort \
PORT=${port} \
requirepass=$requirepass \
masterauth=$masterauth \
envsubst < /opt/redis/conf/redis-sentinel.tmpl > /opt/redis/conf/redis-sentinel-${count}.conf
let count++
done
docker配置
1
2
mkdir -p /opt/redis
vim /opt/redis/docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
version: '3'
services:
master:
image: redis
container_name: redis-master
restart: always
command: redis-server /etc/redis/redis.conf
#容器启动时执行的命令,以下是redis-server启动时指定配置文件,后面可加参数如"--port 6379 --appendonly yes"等参数覆盖配置文件里的一些配置
#command: redis-server /etc/redis/redis.conf --port 6379 --appendonly yes
ports:
- 6379:6379
volumes:
- /opt/redis/conf/redis-master.conf:/etc/redis/redis.conf
- /opt/redis/data/redis-master:/data
slave1:
image: redis
container_name: redis-slave-1
restart: always
command: redis-server /etc/redis/redis.conf
ports:
- 6380:6380
volumes:
- /opt/redis/conf/redis-slave-1.conf:/etc/redis/redis.conf
- /opt/redis/data/redis-slave-1:/data
slave2:
image: redis
container_name: redis-slave-2
restart: always
command: redis-server /etc/redis/redis.conf
ports:
- 6381:6381
volumes:
- /opt/redis/conf/redis-slave-2.conf:/etc/redis/redis.conf
- /opt/redis/data/redis-slave-2:/data

sentinel1:
image: redis
container_name: redis-sentinel-1
command: redis-sentinel /etc/redis/sentinel.conf
restart: always
ports:
- 26379:26379
volumes:
- /opt/redis/conf/redis-sentinel-1.conf:/etc/redis/sentinel.conf
sentinel2:
image: redis
container_name: redis-sentinel-2
command: redis-sentinel /etc/redis/sentinel.conf
restart: always
ports:
- 26380:26380
volumes:
- /opt/redis/conf/redis-sentinel-2.conf:/etc/redis/sentinel.conf
sentinel3:
image: redis
container_name: redis-sentinel-3
command: redis-sentinel /etc/redis/sentinel.conf
restart: always
ports:
- 26381:26381
volumes:
- /opt/redis/conf/redis-sentinel-3.conf:/etc/redis/sentinel.conf
1
2
#创建并启动所有服务容器
docker-compose up -d

参考

https://www.cnblogs.com/hckblogs/p/11186311.html

https://juejin.cn/post/6868426752789020685#heading-10

集群模式

概念

image-20220827141129880

1、主从复制模式只要解决了单机节点查询并发量瓶颈。但主节点出故障需要人为替换从节点顶替主节点。即主从复制模式解决了读并发,但为解决写并发,同时没有高可用性。

2、哨兵模式在主从复制模式的基础上实现了自动化故障恢复。即在主从复制模式的基础上解决了高可用性,但仍然未解决写并发。

3、为了解决哨兵模式的写并发问题,诞生了集群模式。

集群模式保留了主从复制的读并发能力,同时支持多主节点来解决写并发问题,另外集群中所有节点通过互相定时检测来实现高可用性(主节点故障则对应从节点重新选举替换主节点)。

高并发实现

哈希槽分片

由上文已经知道主从复制模式解决了读并发,为了解决写并发问题,cluster集群将写操作的数据根据key分配到不同的主节点中,而这个分配规则即“哈希槽分片”。

cluster集群的是采用“虚拟哈希槽分区”的方式进行分片存储的,它将整个数据库分为16384个槽位slot,所有key-value数据都存储在这些slot中的某一个上。一个slot槽位可以存放多个数据,key的槽位计算公式为:slot_number=crc16(key)%16384,其中crc16为16位的循环冗余校验和函数。

image-20210207034400433

每一个主节点集群(主节点和其从节点组成的小集群)都负责维护一部分槽以及槽所映射的键值数据(也就是说集群中不是每个节点都保存有所有的数据,每个主节点只负责一部分槽对应的数据存储,所有的主节点存储的数据合起来才是整个集群的数据)。扩容或缩容以后,槽需要重新分配,数据也需要重新迁移,但是服务不需要下线。

由于Redis集群无中心节点,客户端请求会随机发给任意主节点;主节点只会处理自己负责槽位的命令请求,对于其它槽位的命令请求该主节点会返回客户端一个转向错误,客户端再根据错误中包含的地址和端口重新向正确负责对应槽位的主节点发起命令请求。

高可用实现

Gossip协议

Redis Cluster实例间以Gossip协议进行通信的机制。Redis Cluster运行时,各实例间需要通过PING、PONG消息进行信息交换,这些心跳消息包含了当前实例和部分其它实例的状态信息,以及Slot分配信息。这种通信机制有助于Redis Cluster中的所有实例都拥有完整的集群状态信息,从而能够实现高可用。

扩容注意事项:

随着集群规模的增加,实例间的通信量也会增加。如果我们盲目地对Redis Cluster进行扩容,就可能会遇到集群性能变慢的情况。这是因为,集群中大规模的实例间心跳消息会挤占集群处理正常请求的带宽。而且,有些实例可能因为网络拥塞导致无法及时收到PONG消息,每个实例在运行时会周期性地(每秒10次)检测是否有这种情况发生,一旦发生,就会立即给这些PONG消息超时的实例发送心跳消息。集群规模越大,网络拥塞的概率就越高,相应的,PONG消息超时的发生概率就越高,这就会导致集群中有大量的心跳消息,影响集群服务正常请求。

最后,我也给你一个小建议,虽然我们可以通过调整cluster-node-timeout配置项减少心跳消息的占用带宽情况,但是,在实际应用中,如果不是特别需要大容量集群,我建议你把Redis Cluster 的规模控制在400~500个实例。

假设单个实例每秒能支撑8万请求操作(8万QPS),每个主实例配置1个从实例,那么,400~ 500个实例可支持 1600万~2000万QPS(200/250个主实例*8万QPS=1600/2000万QPS),这个吞吐量性能可以满足不少业务应用的需求。

节点故障判断

集群中的每个节点都会定期向其他节点发送ping命令,如果接受ping消息的节点在指定时间内没有回复pong,则发送ping的节点就把接受ping的节点标记为主观下线

如果集群半数以上的主节点都将主节点A标记为主观下线,则节点A将被标记为客观下线(通过节点的广播)即下线。

故障转移

当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移执行的步骤:

  • 故障的主节点对应的所有从节点进行选举,选举出一个成为新的主节点;
  • 新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己;
  • 新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。
  • 新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。

Cluster限制

cluster集群模式和单机模式下从使用上来看是由一些区别的:
1、需要客户端的支持(目前jedis、Lettuce、redisson等都有支持)
2、只支持一个数据库。单机模式Redis 默认自带有16个数据库,即db0~db15. 但是 Redis Cluster 集群架构下只有一个数据库空间,即db0,select 命令就不能用了,但实际也很少有人使用多数据库,所以这个限制并没什么影响。
3、多key操作受限(日常使用中最大的限制)。Redis Cluster要求,同一个redis操作多个key时需要这些key都在同一个slot时才能执行,否则报错。DEL删除多个key、SUNION、事务、LUA脚本等操作都可能有多key操作受限问题。

多key操作受限解决方案:
Redis Cluster提供了一个hash tag的机制,可以让我们把一组key映射到同一个 slot。
例如:user1000.following 这个 key 保存用户 user1000 关注的用户;user1000.followers 保存用户 user1000 的粉丝。
这两个 key 有一个共同的部分 user1000,可以指定对这个共同的部分做 slot 映射计算,这样他们就可以在同一个槽中了。
使用方式:
{user1000}.following 和 {user1000}.followers
就是把共同的部分使用 { } 包起来,计算 slot 值时,如果发现了花括号,就会只对其中的部分进行计算。

参考:https://www.cnblogs.com/lovezbs/p/13885569.html

优缺点

优点

  • 无中心架构;
  • 可扩展性,数据按照 Slot 存储分布在多个节点,节点间数据共享,节点可动态添加或删除,可动态调整数据分布;
  • 高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本。
  • 实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave 到 Master 的角色提升。

缺点

  • 数据通过异步复制,无法保证数据强一致性
  • 集群环境搭建复杂,不过基于 Docker 的搭建方案会相对简单。
  • 存在一些使用限制。比如支持一个数据库、多key操作受限等。

部署安装

环境准备

redis要求至少三主三从共6个节点才能组成redis集群,可以分布在一台或者多台主机上。

假设有两台服务器用于搭建Redis Cluster集群。两台服务器的IP分别是:

192.168.102.111

192.168.102.112

每个服务器跑三个不同端口(6371、6372、6373)的redis服务进程,那么就能在这两台服务器中搭建出6个redis节点出来。为了简化部署过程,这里通过使用redis配置模板文件和docker compose来简化搭建过程。

创建redis配置模板文件

每台服务器都执行如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
mkdir -p /opt/redis/conf

cat > /opt/redis/conf/redis-cluster.tmpl << 'EOF'
#节点端口
port ${PORT}
#访问认证密码
requirepass ${requirepass}
#如果主节点开启了访问认证,从节点访问主节点需要认证的密码
masterauth ${masterauth}
#保护模式,默认值 yes,即开启。开启保护模式以后,需配置 bind ip 或者设置访问密码;关闭保护模式,外部网络可以直接访问;
protected-mode no
#是否以守护线程的方式启动(后台启动),默认 no;
daemonize no
#是否开启 AOF 持久化模式,默认 no;
appendonly yes
#是否开启集群模式,默认 no;
cluster-enabled yes
#集群节点信息文件;
cluster-config-file nodes.conf
#集群节点连接超时时间;
cluster-node-timeout 15000
#集群中当前节点IP,填写宿主机的 IP;
cluster-announce-ip ${clusterIp}
#集群中当前节点端口;
cluster-announce-port ${PORT}
#集群节点总线端口
cluster-announce-bus-port 1${PORT}
EOF

每台服务器都执行如下操作,另外注意下面clusterIp的修改(每台服务器ip不一样):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#节点的密码
requirepass=123456
#从节点访问主节点需要认证的密码
masterauth=123456
#当前节点的ip(服务器1的ip)
clusterIp=192.168.60.201
#当前节点的ip(服务器2的ip)
#clusterIp=192.168.60.202
#集群的所有节点端口
clusterPorts=`seq 6371 6373`

for port in $clusterPorts; do
PORT=${port} \
requirepass=$requirepass \
masterauth=$masterauth \
clusterIp=$clusterIp \
envsubst < /opt/redis/conf/redis-cluster.tmpl > /opt/redis/conf/redis-${port}.conf
done
创建docker compose配置文件

每台服务器都执行如下操作

1
vim /opt/redis/docker-compose.yml

内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 描述 Compose 文件的版本信息
version: "3.1"

# 定义服务,可以多个
services:
redis-6371: # 服务名称
image: redis:6.2.7 # 创建容器时所需的镜像
container_name: redis-6371 # 容器名称
restart: always # 容器总是重新启动
volumes: # 数据卷,目录挂载
- /opt/redis/conf/redis-6371.conf:/etc/redis/redis.conf
- /opt/redis/data/6371/data:/data
ports:
- 6371:6371
- 16371:16371
command: redis-server /etc/redis/redis.conf # 覆盖容器启动后默认执行的命令

redis-6372:
image: redis:6.2.7
container_name: redis-6372
volumes:
- /opt/redis/conf/redis-6372.conf:/etc/redis/redis.conf
- /opt/redis/data/6372/data:/data
ports:
- 6372:6372
- 16372:16372
command: redis-server /etc/redis/redis.conf

redis-6373:
image: redis:6.2.7
container_name: redis-6373
volumes:
- /opt/redis/conf/redis-6373.conf:/etc/redis/redis.conf
- /opt/redis/data/6373/data:/data
ports:
- 6373:6373
- 16373:16373
command: redis-server /etc/redis/redis.conf

💁‍♂ 按照 Redis 官网:https://redis.io/topics/cluster-tutorial 的提示,为了使 Docker 与 Redis Cluster 兼容,推荐使用 Docker 的 host 网络模式。但这里发现不用也能支持,如果发现有网络上的问题可以改试host模式。另外使用host模式注意关闭宿主机防火墙。

每台服务器都执行如下操作创建并启动所有服务容器

1
docker-compose up -d
初始化并创建集群

首先确保两台服务器之间可以互相通信,然后选择任意一台并登录进一个redis服务,并通过redis客户端命令来初始化创建出一个Cluster集群。

1
2
3
4
5
6
7
8
9
10
# 进入容器
docker exec -it redis-6371 bash
# 切换至指定目录
cd /usr/local/bin/
#通过以下命令实现多服务器Redis Cluster集群的创建,出现选择提示信息,输入 yes
#-a是redis的登录密码,--replicas 1 代表的是主节点数/从节点数的比例,1表示1个master对应1个从,如果是2,则表示1个master对应2个slave
redis-cli -a 123456 --cluster create \
192.168.60.201:6371 192.168.60.201:6372 192.168.60.201:6373 \
192.168.60.202:6371 192.168.60.202:6372 192.168.60.202:6373 \
--cluster-replicas 1

查看集群状态、信息和节点信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 进入容器
docker exec -it redis-6371 bash
# 切换至指定目录
cd /usr/local/bin/
# 查看集群状态
redis-cli -a 123456 --cluster check 192.168.60.201:6371
# 连接至集群某个节点,-c表示以集群的方式操作
redis-cli -c -a 123456 -p 6371
# 查看集群信息
cluster info
# 查看集群结点信息
cluster nodes
# 查看槽位信息
cluster slots
集群扩容

假又增加一台服务器192.168.60.203,并且启动了一个端口为6371的redis节点,现在希望把它加入到集群中,可如下操作:

1
2
3
4
#新增一个节点(默认是主节点的方式加入),将新节点介绍给集群中的任意一个,那么整个集群的所有节点就能与新节点互相认识
redis-cli -a 123456 --cluster add-node 192.168.60.203:6371 192.168.60.201:6371
#给新节点分配槽,默认情况下新增的节点是没有分配到槽的
redis-cli -a 123456 --cluster reshard 192.168.60.203:6371
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
root@bogon:/data# redis-cli -a 123456 --cluster reshard 192.168.60.203:6371
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing Cluster Check (using node 192.168.60.203:6371)
M: cd33bd0ef64538dcc803dc60feb9779660bda76f 192.168.60.203:6371
slots: (0 slots) master
M: 6baffb00ad60dae2a1f55fc3307570ce9d95a10b 192.168.60.202:6372
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: e1e64960fabdafa2f7b803d83d72079e60c376ee 192.168.60.202:6371
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: ae8ec74c69e808a3f2a64604a4847297f1206397 192.168.60.202:6373
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: d07f345fc7140b471ec0fef272e73b0d7cb4edbd 192.168.60.201:6373
slots: (0 slots) slave
replicates e1e64960fabdafa2f7b803d83d72079e60c376ee
S: 5bd4fb5aeb3e051ebdfafd96e0b16ed633085b86 192.168.60.201:6372
slots: (0 slots) slave
replicates 6baffb00ad60dae2a1f55fc3307570ce9d95a10b
S: 2a4157b11b4b88e6b6413a75a8ef1f3c3e2db61e 192.168.60.201:6371
slots: (0 slots) slave
replicates ae8ec74c69e808a3f2a64604a4847297f1206397
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 4096 #要迁移多少个槽(总共16384个槽位,4个master节点的话可以输入4096个)
What is the receiving node ID? cd33bd0ef64538dcc803dc60feb9779660bda76f #接受节点的ndoe ID(这里填新节点的node ID)
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1: all #从所有节点中迁移槽到新节点中

Ready to move 4096 slots.
Source nodes:
M: 6baffb00ad60dae2a1f55fc3307570ce9d95a10b 192.168.60.202:6372
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
......
......
Do you want to proceed with the proposed reshard plan (yes/no)? yes #输入yes确认即可

集群模式常用命令

1
2
3
4
5
#在集群中使用keys *,只会返回当前会话所在的单个节点的key,并非整个集群的key
docker exec -it redis-6371 redis-cli -c -a 123456 -p 6371 keys \*

#获取整个集群的key(所有节点)
docker exec -it redis-6372 redis-cli -c -a 123456 --cluster call 192.168.60.201:6372 keys \*

参考

https://juejin.cn/post/6868426752789020685

https://juejin.cn/post/6870674824247640072

https://www.cnblogs.com/cqming/p/11191079.html

https://blog.csdn.net/weixin_36691991/article/details/125868968

https://zhuanlan.zhihu.com/p/347125538



----------- 本文结束 -----------




如果你觉得我的文章对你有帮助,你可以打赏我哦~
0%