一、Redis概述
1.是什么
是存在内存中的数据库
是Key-Value数据库(MySQL是关系数据库)
2.能干什么
一个程序中大部分操作都是查询,少部分操作是写入
所以用MySQL作存储,Redis作查询
所有查询先查询Redis,没有再查询MySQL,Redis会把自己没有的数据存到自己里面
- 所以Redis和MySQL不是竞争的关系而是合作的关系,挡在MySQL前面的带刀护卫
- 能够将内存中的数据异步写导硬盘上,同时不影响性能
- 如果只有一台Redis,它挂了之后,会产生穿透、雪崩等事件。所以可以多搞几台Redis。
- 分布式锁
- 队列
- 优势:
- 性能极高(每秒可读11万次,可写8万次)
- 数据类型丰富(不仅支持k-v,还支持list,set等数据结构)
- 支持数据的持久化(可以将内存的数据保存在硬盘上)
- 支持数据的备份
3.去哪下
官网地址:https://redis.io/
4.Redis的特性了解
-
命名规则:
版本号第二位如果是奇数,则为非稳定版 如:2.7,2.9,3.1
版本号第二位如果是偶数,则为稳定版本 如:2.6
二、Redis安装及启动连接
-
确定Linux系统是64位
getconf LONG_BIT
-
查看是否具有gcc编译环境
gcc --version
-
如果没有gcc环境就使用命令安装C++库
yum -y install gcc-c++
-
将redis的安装包移动到opt目录下
-
解压安装包
tar -zxvf 安装包名
-
进入解压后的目录
-
进行编译安装
make && make install
-
如果显示
It's a good idea to run 'make test'
就说明安装成功 -
默认安装目录:
/usr/local/bin
-
将/opt/redis的解压包下的redis.conf复制一份,放到自己定义的路径下
redis.conf是redis的配置文件
这样做是为了:如果我们把本来的改坏了,还有备份可以使用
cp redis.conf /yfjconfig/redis.conf
-
修改配置文件【复制的那个】
- 默认的daemonize no 改为daemonize yes 【作为服务器后端启动】
- 默认的protected-mode yes 改为protected-mode no 【关掉后可以让别人连接】
- 默认的 bind 127.0.0.1 -::1 直接注释掉【允许访问的ip地址,默认只能本机访问】
- 将注释的requirepass foobared打开,然后改为自己的密码
vim redis.conf #打开配置文件 /daemonize #查找daemonize【然后修改为yes】 /protected-mode #查找保护模式【改为no】 /bind 127.0.0.1 -::1 #允许访问的外部ip【注释掉】 /requirepass #设置密码【打开注释,改为自己的】
-
启动服务【按自定义配置文件】
redis-server /yfjconfig/redis.conf
-
查看是否正常启动
ps -ef|grep redis|grep -v grep
-
连接redis服务
不写 -p 端口号 那么默认使用6379
redis-cli -a 密码 -p redis的端口号 #默认6379 #进去后会报个警告是正常的
-
验证是否可以正常使用
ping #如果出现PONG就是正常启动
-
添加数据
set k1 helloworld
-
获取数据
get k1
-
退出客户端【没有关闭服务器】
quit
-
关闭redis服务器
-
单实例关闭
redis-cli -a 密码 shutdown #在redis里可以直接执行shutdown命令
-
多实例管理【可以关闭多个端口的】
redis-cli -p 端口号 shutdown
-
三、Redis的卸载
-
停止redis-server服务【看上面的关闭服务器】
-
删除/usr/local/lib目录下与redis相关的文件
rm -rf /usr/local/bin/redis-*
四、Redis10大类型介绍
注意:这里说的10大类型是value的数据类型,key的数据类型只有字符串
0.一图介绍
1. redis字符串(String)
- 单key,单value
- string是redis最基本的类型,一个key对应一个value
- string类型是二进制安全的,意思是redis的string可以包含任何数据,比如jpg图片或者序列化的对象 。
- string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
2. redis列表(List)
- 单key,多value
- Redis列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)
- 它的底层实际是个**双端链表**,最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表最多支持超过40亿个元素)
- 左右两边都可以添加删除
- 主要功能有Push/Pop等,一般用在栈、队列、消息队列等场景
- 如果key不存在则新建链表;如果key已经存在则添加数据
- 如果值全移除,对应的键也会消失
3. redis哈希表(Hash)
- 单key,value是多个键值对【可以看下面的命令那里有图】
- hash 是一个 string 类型的 Key 和 value(值:可以存放任意类型)的映射表,hash 特别适合用于存储对象
- Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)
4. redis集合(Set)
- 单Key,多value【value不能重复】
- Redis 的 Set 是 String 类型的**无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据**,集合对象的编码可以是 intset 或者 hashtable。
- Redis 中Set集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
- 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)
5. redis有序列表(ZSet)
- 可排序的set集合
- Redis zset 和 set 一样也是string类型元素的集合,且**不允许重复的成员**
- 不同的是每个元素都会关联一个double类型的分数,redis正是通过分数来为集合中的成员进行从小到大的排序
- zset的成员是唯一的,但分数(score)却可以重复
- zset集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 2^32 - 1
6. redis地理空间(GEO)
- Redis GEO 主要用于存储**地理位置信息**,并对存储的信息进行操作,包括:
- 添加地理位置的坐标。
- 获取地理位置的坐标。
- 计算两个位置之间的距离。
根据用户给定的经纬度坐标来获取指定范围内的地理位置集合
案例:我打车,车离我有多远
7. redis基数统计(HyperLogLog)
- HyperLogLog 是用来做**基数统计(不重复)**的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定且是很小的
- 在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比
- 但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
- 案例:统计今天访问天猫的人数(有人可能重复访问)
8. redis位图(bitmap)
位图:由0和1状态表现的二进制的bit数组
作用:如果我的一个东西只有两种状态
例如:每天是否打卡。那么就可以使用这个
9. redis位域(bitfield)
- 通过bitfield命令可以一次性操作多个比特位域(指的是连续的多个比特位),它会执行一系列操作并返回一个响应数组,这个数组中的元素对应参数列表中的相应操作的执行结果
- 说白了就是通过bitfield命令我们可以一次性对多个比特位域进行操作。
10. redis流(Steam)
- Redis Stream 是 Redis 5.0 版本新增加的数据结构
- Redis Stream 主要用于消息队列(MQ,Message Queue),Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃
- 简单来说发布订阅 (pub/sub) 可以分发消息,但无法记录历史消息
- 而 Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访 问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失
五、操作命令
命令不区分大小写,但是键值区分大小写
0.命令查询
- 帮助文档:Redis命令中心(Redis commands) – Redis中国用户组(CRUG)
- 帮助命令:
help
1.key的相关操作命令
1.1 查询所有key
-
操作命令:
keys *
1.2 判断key是否存在
-
操作命令:
exists key名
-
案例:查询 key1 key2是否存在
EXISTS key1 key2 #如果返回的值大于0就存在,存在几个返回几
1.3 查看key对应的值的类型
-
操作命令:
type key名
-
案例:查看key1对应的value的值的类型
type key1
1.4 删除键值【阻塞删除】
阻塞删除,如果删除一个很大的数据,用时很长事件,它删不完,其他的操作也会阻塞到这里,直到它执行结束
-
操作命令:
del key名
-
案例:删除key1和他的值
del key1
1.5 删除键值【非阻塞删除】
仅仅将kyes从keyspace元数据中删除,真正的删除会在后续异步中进行
不会阻塞其他线程
- 操作命令:
unlink key名
1.6 查看过期时间
查看还有多少秒过期,-1表示永不过期,-2表示已过期
-
操作命令:
ttl key名
-
案例:查看key1还有多少秒过期
ttl key1
1.7 设置键值生存时长
-
操作命令:
expire key名 生存时间(秒)
-
案例:设置key1的生存时长为1000s
expire key1 1000
1.8 切换数据库
Redis默认有16个数据库【0-15号】
默认使用0号库
-
操作命令:
select 数据库索引号
-
案例:使用3号数据库
select 3 #数据库索引从0开始
1.9 移动键值到指定库
-
操作命令:
move key名 数据库索引
-
案例:把key1移动到3号数据库
move key1 3 #数据库索引从0开始
1.10 查看当前库的存储数量
相当于 select count(key)
- 操作命令:
dbsize
1.11 清空当前库
- 操作命令:
flushdb
- 案例:把key1移动到3号数据库
1.12 清空所有库
- 操作命令:
flushall
- 案例:把key1移动到3号数据库
2.String类型相关命令
2.1 set命令
设置键值
-
基本语法:
SET key value [NX | XX] [GET] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]
-
参数说明:
- EX seconds:设置以秒为单位设置过期时间
- PX milliseconds:设置以毫秒为单位设置过期时间
- EXAT unix-time-seconds:设置以秒为单位的unix时间戳所对应的过期时间
- PXAT unix-time-milliseconds:设置以毫秒为单位的unix时间戳所对应的过期时间
- NX:当键不存在的时候执行语句
- XX:当键存在的时候执行语句
- KEEPTTL:保存设置前指定的生存时间
- GET:返回指定键原本的值,若键不存在时返回null
-
基本使用:
#NX XX的使用 set key1 yfj NX #如果key1不存在就会设置键值 set key1 yfj XX #如果存在key1就会设置键值 set key1 666 GET #先获取原本key1的值,然后设置新的值666进去 set key2 yls ex 10 #设置key2,生存时间10s set key2 yls2 px 8000 #设置key2,生存时间8s set key3 123 exat 2012012012 #设置key到期时间为2012012012 set key3 345 keepttl #继承原本key3的过期时间(如果只剩3秒,那么就会继承这三秒)
2.2 get命令
通过键获取值
-
基本语法:
get 键
2.3 mset命令
批量设置键值
-
基本语法:
mset key1 123 key2 456 key3 789 #将这三个键值全放进去
2.4 mget命令
通过键批量获取值
-
基本语法:
mget key1 key2 key3 #获取这三个的值
2.5 msetnx命令
当键不存在的时候批量设置值
只要有一个键存在,整个命令就都不会被执行
-
基本语法:
msetnx key1 v1 key2 v2 #如果存在键key1或者,那么整条语句都不会被执行
2.6 getrange命令
获取value的部分值(字符串截取)
-
基本语法:
getrange 键名 起始索引 结束索引
-
基本说明:
- 如果索引位置 0 -1那么会显示所有
-
基本案例:
set k1 123345abc #设置键值对 getrange k1 0 -1 #获取完整的值 getrange k1 0 3 #获取索引0-3的值【闭区间】
2.7 setrange命令
从指定位置开始用指定的值替换原来的值
-
基本语法:
setrange 键名 起始索引 替换的值
-
基本说明:
输入几个字符就会替换几个字符
-
基本案例:
set k1 123456 #设置原值 SETRANGE k1 0 ab #从索引为0的位置开始替换 #替换后的值为 ab3456
2.8 数值的增减
一定要是数字才能进行增减
2.8.1 incr命令
给指定的值进行自增
默认加1
-
基本语法:
incr 键名 [步长] #步长可以选择输入,没有输入默认为1
-
基本案例:
set k1 123 incr k1 #这里k1会变成124 incr k1 3 #这里k1变成127
2.8.2 decr命令
给指定的值进行自减
默认减1
-
基本语法:
decr 键名 [步长] #步长可以选择输入,没有输入默认为1
-
基本案例:
set k1 123 decr k1 #这里k1会变成122 decr k1 3 #这里k1变成119
2.9 strlen命令
获取值的长度
-
基本语法:
strlen 键名
2.10 append命令
在值的末尾追加内容
-
基本语法:
append 键名 内容
2.11 分布式锁
2.11.1 setex命令
将set和expire命令合成一个命令
setex是一个原子命令,创建和设置时间是同是的
-
基本语法:
setex 键名 时长 值
2.11.2 setnx命令
将set和nx属性合成一个命令
-
基本语法:
setnx 键名 值
2.12 getset命令
先get再set
和set使用get属性一样
-
基本语法:
getset k1 v1 #先获取值,然后再设置
3.List链表类型相关命令
3.1 lpush命令
从左侧添加数据到key(如果key已经存在就添加;key不存在就创建新的链表)
-
基本命令:
lpush 键名 值1 值2 值3 ...
-
基本案例:
lpush list1 123 456 789 #创建链表list1,添加三个数据 lpush list1 abc #向list1中添加数据 lpush list2 a #创建list2,添加1个数据
3.2 rpush命令
从右侧添加数据到key(如果key已经存在就添加;key不存在就创建新的链表)
-
基本命令:
rpush 键名 值1 值2 值3 ...
3.3 lrange命令
遍历指定范围的链表数据
如果范围是 0 -1则遍历所有
没有rrange
-
基本命令:
lrange 键名
-
基本案例:
lrange list1 0 -1 #遍历list1中的所有数据 lrange list1 2 5 #遍历索引为2-5的数据
3.4 lpop命令
从左侧开始将数据弹出【默认是1个】
-
基本命令:
lpop 键名 [个数] #个数可以不写,默认是1个
-
基本案例:
lpop list1 #从左侧弹出一个数据 lpop list1 3 #从左侧弹出三个数据
3.5 rpop命令
从右侧开始将数据弹出【默认是1个】
-
基本命令:
rpop 键名 [个数] #个数可以不写,默认是1个
-
基本案例:
rpop list1 #从右侧弹出一个数据 rpop list1 3 #从右侧弹出三个数据
3.6 lindex命令
从左侧开始获取指定索引的数据
-
基本命令:
lindex 键名 索引
-
基本案例:
lindex list 0 #获取左边第一个数据 lindex list 3 #获取左边第四个数据
3.7 llen命令
获取链表的长度
-
基本命令:
llen 键名
-
基本案例:
llen list1
3.8 lrem命令
删除n个等于指定值的数据
-
基本命令:
lrem 键名 个数 值
-
基本案例:
lrem list1 3 abcd #删除list中的3个值等于abcd的数据 lrem list1 2 123 #删除2个123
3.9 ltrim命令
截取指定范围的值然后再赋值给key
-
基本命令:
ltrim 键名 起始索引 结束索引
-
基本案例:
ltrim list1 3 5 #将索引3-5截取出来,然后再赋值给list1
3.9 rpoplpush命令
将源列表的1个数据从右侧弹出,添加到目的列表的左侧
-
基本命令:
rpoplpush 源列表 目的列表
-
基本案例:
rpoplpush list1 list2 #从list1中弹出1个加到list2中
3.10 lset命令
修改链表指定索引的值为指定的值
-
基本命令:
lset 键名 索引号 值
-
基本案例:
lset list1 2 mysql #修改list1的索引为2的位置的值为mysql
3.10 linset命令
将指定的目的数据插入到指定的已有数据前面/后面
-
基本命令:
linsert 键名 before/after 已有值 目的值
-
基本案例:
linsert list1 before mysql java #将java添加到mysql前面 linsert list1 after mysql c++ #将c++添加到mysql后面
3.10 应用场景
微信公众号:右两个人分别发了文章,就会依次加入到你的list中
4. Hash类型相关命令
KV模式不变,V里面存的是KV
应用场景:可以用于购物车【添加商品就是hset,增加数量hincrby,商品总数hlen,全选hgetall】
4.1 hset命令
-
基本命令:V里面是键值对
hset 键名 内部键名1 值1 内部键名2 值2 ...
-
基本案例:
hset user name yfj age 12 tall 180 #设置键user里面的内容是键值对形式 hget user name #获取user里面的name hget user age #获取user里面的age
4.2 hget命令
-
基本命令:
hget 键名 内部键名
-
基本案例:
hget user name #获取user 里面的name的值 hget user age #获取user里面的age的值
4.3 hmset命令
和hset一样
4.4 hmget命令
允许获取一个K里面的多个属性
-
基本命令:
hmget 键名 内部键名1 内部键名2...
-
基本案例:
hmget user name age #获取user里面的name,age的值
4.5 hgetall
获取Key里面的所有键和值
-
基本命令:
hgetall 键名
-
基本案例:
hgetall user #获取user里面的所有键和值
4.6 hdel命令
删除key里面的键和值
-
基本命令:
hdel 键名 内部键名1
-
基本案例:
hdel user age#删除user里面的age键和值
4.7 hlen
-
基本命令:
hlen 键名 #获取里面的长度【键的值里面存了多少个键】
-
基本案例:
hlen user #获取user里面的键的个数
4.8 hexists命令
判断这个key里面是否存在某个key
存在返回1,不存在返回0
-
基本命令:
hexists 键名 内部键名1 #判断内部键是否在键中存在
-
基本案例:
hexists user big#判断user里面是否存在big键
4.9 hkeys命令
列举出里面所有的键
-
基本命令:
hkeys 键名 #列举出键里面的所有键
-
基本案例:
hkeys user #列举出user里面所有的键
4.9 hvals命令
列举出key里面所有的值
-
基本命令:
hvals 键名 #列举出键里面的所有的值
-
基本案例:
hvals user #列举出user里面所有的值
4.10 hincrby命令
数字自增【默认自增1】
-
基本命令:
hincrby 键名 内部键名 [n]#内部键的值自增【默认为1】
-
基本案例:
hincrby user age #age自增1 hincrby user age 3 #age自增3
4.11 hincrbyfloat命令
小数增加
-
基本命令:
hincrbyfloat 键名 内部键名 n #内部键的值增加n
-
基本案例:
hincrby user score 0.5 #score增加0.5
4.12 hsetnx命令
不存在命令生效
-
基本命令:
hsetnx 键名 内部键名 值1 #如果内部键不存在,则语句生效
-
基本案例:
hsetnx user score 100
5.Set类型相关命令
无序、单key,多value【value不能重复】
会自动去除重复的值
应用场景:
- QQ和抖音推荐共同好友,或者可能认识的人【集合的运算】
- 抽奖程序【spop或者,srandmember】
- 微信点赞的好友
- 判断是否点赞过
5.1 sadd命令
-
基本命令:
sadd 键名 值1 值2 值3 ... #添加数据
-
基本案例:
sadd key1 age name tall #添加了3个数据到里面
5.2 smembers命令
遍历集合中的所有元素
-
基本命令:
smembers 键名 #遍历集合中的所有数据
-
基本案例:
smembers key1 #遍历key1的所有数据
5.3 sismember命令
判断元素是否在集合中
-
基本命令:
sismember 键名 值 #判断值是否在集合中
-
基本案例:
sismember key1 big #判断big是否在集合中
5.4 srem命令
删除集合中的元素
-
基本命令:
srem 键名 值 #在集合中删除值
-
基本案例:
srem key1 tall #删除集合key1中的tall
5.5 scard命令
统计集合中有多少个元素
-
基本命令:
scard 键名 #统计集合中有多少个元素
-
基本案例:
scard key1 #统计集合key1的长度
5.6 srandmember命令
随机展示N个元素,但是**不会从集合中删除**
-
基本命令:
srandmember 键名 [N] #随机展示N个元素【默认是1个】
-
基本案例:
srandmember key1 #随机展示1个元素 srandmember key1 4 #随机展示4个元素
5.6 spop命令
随机弹出N个元素,弹出一个删除一个
-
基本命令:
spop 键名 [N] #随机展示N个元素,并删除
-
基本案例:
spop key1 #随机展示1个元素,并删除 spop key1 4 #随机展示4个元素,并删除
5.7 smove命令
将指定的元素从一个集合中迁移到另一个集合中
-
基本命令:
smove 键名1 键名2 元素 #将键名1中的元素移动到键名2中
-
基本案例:
smove key1 key2 age #将key1中的age迁移到key2中
5.8 集合运算【重要】
5.8.1 计算差集【A-B】
-
基本命令:
sdiff 键名1 键名2 #计算属于A但是不属于B的元素
-
基本案例:
sadd set1 a b c 1 2 #set1集合添加数据 sadd set2 1 2 3 a x #set2集合添加数据 SDIFF set1 set2 #计算差集【a b】
5.8.2 计算并集【A+B】
-
基本命令:
sunion 键名1 键名2 #计算两者的并集
-
基本案例:
sadd set1 a b c 1 2 #set1集合添加数据 sadd set2 1 2 3 a x #set2集合添加数据 sunion set1 set2 #计算并集【1 2 3 a b c x】
5.8.3 计算交集
-
基本命令:
sinter 键名1 键名2 #计算AB都有的
-
基本案例:
sadd set1 a b c 1 2 #set1集合添加数据 sadd set2 1 2 3 a x #set2集合添加数据 sinter set1 set2 #计算交集【1 2 a】
5.8.4 计算交集的个数
返回的是个数,上面返回的是具体数据
-
基本命令:
sintercard 键的个数 键名1 键名2 键名3 [limit N]#计算N个键的交集个数【limit是规定最大显示几】
-
基本案例:
sintercard 2 set1 set2 #计算2个集合set1 set2的交集的个数 sintercard 3 set1 set2 set3 #计算3个集合set1 set2 set3交集的个数 sintercard 3 set1 set2 set3 limit 3 #计算3个集合set1 set2 set3交集的个数,如果个数大于3个则就显示3
6.Zset类型相关命令
可排序的set集合
在set的基础上每个 val值前加了一个score分数值
之前set是k1 v1 v2 v3,现在zset是 k1 score1 v1 score2 v2
可以做排行榜
6.1 zadd命令
-
基本命令:
zadd 键名 score1 值1 score2 值2 score3 值3
-
基本案例:
zadd set1 25 b1 33 b2 50 b3 100 b4 80 b5
6.2 zrange命令【重要】
遍历指定索引范围的值【按score从小到大】
-
基本命令:
zrange 键名 起始索引 结束索引 [withscores] #遍历zset集合[withscores表示展示分数]
-
基本案例:
ZRANGE set1 0 -1 #展示集合的所有值 ZRANGE set1 0 -1 withscores #展示所有值和score
6.3 zrevrange命令【重要】
遍历指定索引范围的值【按score从大到小】
用法和zrange一样
6.4 zrangebyscore命令【重要】
获取指定分数范围的元素
-
基本命令:
zrange 键名 开始分数 结束分数 [withscores]#遍历指定分数范围的zset集合[withscores表示展示分数] zrange 键名 (开始分数 结束分数 [withscores] #表示左侧是开区间 zrange 键名 开始分数 结束分数) [withscores] #表示右侧是开区间 zrange 键名 开始分数 结束分数 [withscores] limit 开始索引 步长 #同时限制长度
-
基本案例:
ZRANGEBYSCORE set1 50 100 #获取分数在50-100的值 ZRANGEBYSCORE set1 50 100 withscores #获取分数在50-100的值同时显示分数 ZRANGEBYSCORE set1 (50 100 withscores #获取分数大于50,小于等于100 ZRANGEBYSCORE set1 50 100 withscores limit 0 2 #表示获取分数为50-100的值,从索引0开始,显示2个
6.5 zscore命令
获取查询值的分数
-
基本命令:
zscore 键名 值 #通过值获取分数
-
基本案例:
zscore set1 b1 #获取set1集合的b1的分数
6.6 zcard命令
获取集合的长度
-
基本命令:
zcard 键名 #获取集合的长度
-
基本案例:
zcard set1
6.7 zrem命令
删除指定的值
-
基本命令:
zrem 键名 值 #删除指定集合的指定值
-
基本案例:
ZRANGE set1 0 -1 #展示集合的所有值 ZRANGE set1 0 -1 withscores #展示所有值和score
6.8 zincrby命令【重要】
给指定的值加分数
-
基本命令:
zincrby 键名 分数 值 #给指定的值加上分数
-
基本案例:
zincrby set1 2 b1 #给set1集合的b1的分数加2分
6.9 zcount命令
获得指定分数范围内元素的个数
-
基本命令:
zcount 键名 起始分数 结束分数 #获取指定分数范围内元素的个数
-
基本案例:
zcount set1 10 100 #获得分数在10-100的元素个数
6.10 zrank命令
获得指定元素的下标值【0开始往下计数】
-
基本命令:
zrank 键名 值 #根据值获得他的下标
-
基本案例:
zrank set1 b1 #获得b1在set1集合中的下标【0】
6.11 zrevrank命令
获得下标值【逆序:从下面往上计数】
-
基本命令:
zrevrank 键名 值 #根据值获得他的下标
-
基本案例:
zrevrank set1 b1 #获得b1在set1集合中的下标【正序是0,逆序就是最后一个】
7.bitmap类型相关命令
里面只能存0或1
位图的本质是一个String数组
应用场景:用户是否登陆过、钉钉上班打卡、百词斩打卡、广告是否点击过
7.1 setbit
设置索引位置的值【从0开始】
-
基本命令:
setbit 键名 偏移量 值 #根据偏移量设置值【偏移量是几索引就是几】
-
基本案例:
setbit k1 1 1 #设置索引为1的位置值为1 setbit k1 7 1 #设置索引为7的位置值为1
7.2 getbit
获取指定索引位置的值
-
基本命令:
getbit 键名 索引值 #获取指定索引位置的值
-
基本案例:
getbit k1 0 #获取索引为0的位置的值
7.3 strlen
统计字节数占用了多少【8位一个字节,8位1组,占用1位也是一个字节】
-
基本命令:
strlen 键名 #统计占用了多少个字节
-
基本案例:
setbit k1 0 1 setbit k1 7 1 strlen k1 #统计k1占用了多少个字节【这里是一个字节】 setbit k1 8 1 strlen k1 #这里是2个字节
7.4 bitcount
统计全部键里面有多少个1
-
基本命令:
bitcount 键名 [起始索引 结束索引]#统计里面有多少个1
-
基本案例:
bitcount k1 #统计k1里面有多少个1 bitcount k1 0 30 #统计k1位图的0-30位有多少个1
7.5 bitop
将两个位图进行逻辑操作(and、or、not)操作,结果存到另一个位图中
-
基本命令:
bitop and 键名3 键名1 键名2 #将位图1,位图2进行与运算的结果存到位图3中
-
基本案例:
bitop and k3 k1 k2 #将k1,k2与运算的结果存放到k3中
8.HyperLogLog类型相关命令
可以去重复进行基数统计,计算基数所需的空间总是固定的。
只会根据输入数据来进行计算基数,而**不会存储数据本身**【例如:有100万人访问我的网站,这里面只会存数字100万,而不会存这100万个人的数据】
人话就是:一个可以根据条件自动去重的计数器
基数:去重复后的真实个数【我访问了3次淘宝,去除重复那么只能算1】
案例:
- 统计今天访问网站的人(UV),需要去重
- 用户搜索网站关键词的数量
8.1 pfadd命命令
将数据去重后添加到里面
-
基本命令:
pfadd 键名 值1 值2 值3 值4 #将去数据去重后添加到里面
-
基本案例:
pfadd key1 a b c a a c #这里他保存的结果就是【a b c】
8.2 pfcount命令
返回给定HyperLogLog的基数估
-
基本命令:
pfcount 键名 #将去数据去重后添加到里面
-
基本案例:
pfcount key1 #返回去重后的个数
8.3 pfmerge命令
将多个HyperLogLog去重后合并到一个HyperLogLog中
-
基本命令:
pfmerge 键名3 键名1 键名2#将键名1和键名2的数据去重后合并到键名3中
-
基本案例:
pfmerge key3 key1 key2 #将key1,key2合并去重后保存到key3中
9.GEO类型相关命令
可以用于地理位置的计算【嘀嘀打车:车离我还有多远】
底层是zset
9.1 geoadd命令
将精度、维度、位置名称添加到指定的key中
-
基本命令:
geoadd 键名 经度 维度 名称 [经度 维度 名称]#将精度维度名称添加到key中【可以添加多组数据】
-
基本案例:
geoadd city 127.654646 32.654564 河北 #插入数据 geoadd city 127.654646 32.654564 河北 128.654646 31.654564 山东 #插入多组数据 ZRANGE city 0 -1 #因为底层是zset所以可以使用zrange查看所有城市【如果又乱码 登录的时候后面加--raw】
9.2 geopos命令
根据名字返回经纬度
-
基本命令:
geopos 键名 名称 [名称] #根据名称获取键内存储的经纬度
-
基本案例:
geopos city 北京 河北 #获取city键中存储的北京、河北的经纬度 #返回结果 #127.65464454889297485 #32.65456302572950875
9.3 geodist命令
两个位置之间的举例
单位:m 米 km 千米
-
基本命令:
geodist 键名 名称 名称 单位 #返回两个位置之间的距离
-
基本案例:
geodist city 河北 北京 km #以km为单位返回北京和河北的距离
9.4 georadius命令【重点】
查找某个位置多大半径内附近的XXX
withdist:将自己的位置也一并返回
withcoodr:将经纬度一并返回
withhash:将hash值一并返回
count:返回最大记录数
-
基本命令:
georadius 键名 经度 维度 半径 [withdist][withcoodr] [withhash] count 记录条数
-
基本案例:
georadius city 117.123456 39.123456 10km withdist withhash count 10 desc #以某个位置为中心,查找10km以内的东西
9.5 georadiusbymember命令
和georadius一样,只不过将经纬度换成了具体的名字
9.6 geohash命令
返回坐标的hash值【因为geopos返回的值小数点位数很多,所以将经纬度转化为hash值】
-
基本命令:
geohash 键名 名称 [名称] #根据名称获取键内存储的经纬度的hash值
-
基本案例:
geohash city 河北 #返回结果wvfcbbe68g0
9.7 中文乱码问题
登录的时候后面加上–raw
redis-cli -a 密码 --raw
10 Stream流【没学明白,学了RabbitMQ再来】
这里是redisStream,不是java的Stream
就是Redis的消息中间件+阻塞队列
键名:就表示一个消息队列
10.1 队列相关指令
10.1.1 xadd命令
添加消息到队列末尾,消息内容以key-value形式存在
一个时间戳是一条完整的消息
要求:
- 消息ID必须比上个ID大
- 默认用星号表示自动生成id
-
基本命令:
xadd 键名 * 消息key 消息内容 [消息key 消息内容]#添加消息到键中,id自动生成
-
基本案例:
XADD message * tell hello answ hi #添加了两个消息内容到一个消息中
10.1.2 xrange命令
获取消息列表
start表示开始值,-表示最小值
end表示结束值,+表示最大值
count表示最多获取多少个值
-
基本命令:
xrange 键名 开始值 结束值 [count 最大显示数] #显示消息队列中的消息
-
基本案例:
XRANGE message - + #显示所有消息 XRANGE message - + count 2 #只显示两条消息
10.1.2 xrevrange命令
将消息队列反转过来【+ -也需要调转】【看上面的xrange】
10.1.3 xdel命令
按照时间戳删除消息
-
基本命令:
xdel 键名 时间戳编号 #根据时间戳将指定的消息删除
-
基本案例:
XDEL message 1684491742924-0 #将编号为这个的消息删除
10.1.4 xlen命令
显示消息队列中有多少条消息
-
基本命令:
XLEN 键名
-
基本案例:
XLEN message #显示message队列中消息数量
10.1.5 xtrim命令
对消息的长度进行截取,如果超长了会进行截取
maxlen:允许的最大程度
minid:允许的最小id【比该id小的值会被抛弃】
-
基本命令:
xtrim 键名 manlen 个数 #只允许N个存在,超过的截掉 xtrim 键名 minid id #只允许比他大的id保存
-
基本案例:
xtrim 键名 manlen 2 #只允许有2条消息 xtrim 键名 minid 1684491768963-0 #只允许id比他大的存在
10.1.6 xread命令
读取消息
count:最多允许读取多少条消息
block:是否以阻塞的方式读取消息,默认不阻塞,如果为0表示永远阻塞
0-0:表示从最小的读起
$:表示等待最新的消息【比当前最后一个还要新】
-
基本命令:
xread [count 最多条数] [block 阻塞时间] streams 键名 0-0
-
基本案例:
xread count 2 streams message 0-0 #表示最多读取两条消息 xread count 1 block 0 streams message $ #表示一直阻塞,知道有新消息到来,然后读取
10.2 消费组相关指令
10.2.1 xgroup create
用于创建消费者组
$:表示从stream尾部开始消费
0:表示从stream头不开始消费
-
基本命令:
xgroup create 键名 组名 $ #给需要被读取的消息队列创建组
-
基本案例:
xgroup create message group1 $ #给message消息队列创建一个分组,从尾部开始消费 xgroup create message group2 0 #给message消息队列创建一个分组,从头开始消费
10.2.2 xreadgroup
表示读取消息
stream中的消息一旦被阻力的消费者读取,就不能在被该消费组内的其他消费者读取
count:每个消费者允许读几条
-
基本命令:
xreadgroup group 组名 消费者名 streams 消息队列 > [count 数量]#让这个组里的某个用户从头开始读取所有的消息
-
基本案例:
XREADGROUP group group2 costumer1 streams message >
10.2.3 xpending
学了RabbitMQ再来
10.2.4 xack命令
学了RabbitMQ再来
11 bitfield类型相关命令
了解即可
将Resis字符串转化为二进制数组,然后可以对数组中任意偏移进行访问和修改。
例如:Hello–》0110 1000 1100 1010…,然后可以对任意位进行访问
基本命令:
- GET——返回指定位域
- SET——设置指定位域的值并返回它的原值
- INCRBY——自增或自减,并返回新值
- OVERFLOW——溢出控制
基本使用:BITFIELD key 命令
11.1 bitfield key get
set k1 hello
bitfield k1 get i8 0 #i表示有符号,后面的表示起始索引
#这个命令就是:从第0位开始返回k1有符号的8位进制数
#结果104
六、Reids持久化
如何把内存中数据写道磁盘中:通过RDB、AOF的一种或者组合进行写入
RDB:快照机制
AOF:记录下写的操作【学渣抄学霸作业】
1.RDB
1.1 RDB介绍
以**指定的时间间隔执行对数据集的全量快照**
快照文件就成为RDB文件(dump.rdb),RDB就是Redis DataBase的缩写
例如:我们设置一分钟进行一次快照保存如果某个时间修改次数到了,Redis就会生成一份快照文件(dump.rdb),保存到硬盘上。如果服务器挂了,重启之后就会把rdb文件重新写回内存中
Redia7之前的保存规则
- 每间隔900s,如果有超过1个数据进行了变化,就重新写一份新的RDB
- 每隔300s,如果有超过10个数据发生了百安话,就重新写一份RDB
- 每隔60s,如果有超过10000个数据发生了变化,就写一份新的RDB文件
Redia6.2之后的保存规则
不是必须几秒内必须修改几次,而是每多少秒检查一次,如果次数到了就会保存
- 每隔3600s检查一次,如果有超过1个数据进行了变化,就重新写一份新的RDB
- 每隔300s检查一次,如果有超过100个数据发生了百安话,就重新写一份RDB
- 每隔60s检查一次,如果有超过10000个数据发生了变化,就写一份新的RDB文件
1.2 RDB触发的方式【两种】
1.2.1 自动触发
默认就是按上面的规则,可以自己修改配置文件,设置自己想要的频率
-
案例演示:每隔5s,如果有2次修改就保存
-
修改触发条件
在后面添加上我们的触发条件即可
save 时间 触发次数
save 5 2 #每5秒钟如果修改2次就触发
-
修改配置文件中dump文件保存路径
505行 有个dir 后面的就是我们的保存路径,修改为我们自己需要的
:set nu #显示行号 505 + G #直接跳转到对应行 dir /yfjconfig/dumpfile #保存的路径【文件夹需要提前保存好】
-
指定dump文件保存的名称
482 行有一个 dbfilename 后面的就是保存的名字,单机不修改也可以,但是后面如果有多台redis服务器,那么会重名,所以推荐加上端口端口号
/dbfilename #查找这个字符串的位置【n是下一个】 dbfilename dump6379.rdb #保存的文件名
-
重启服务器
如果不放心就重启服务器
redis-cli -a 密码 #登录客户端 shutdown #结束进程 quit #退出客户端 redis-server /配置文件地址 #按配置文件启动进程
-
验证是否修改成功
redis中支持使用
config get
查看配置文件的信息redis-cli -a 密码 #登录客户端 config get dir #查看保存目录是否正确 config get dbfilename #查看文件名是否正确
-
触发RDB
- 触发方式1:上面添加了触发规则,那么我们只需要5s内修改两次数据即可
- 触发方式2:写入数据,超过5秒后在次写入数据也会触发【因为他检测到了2个数据变化】
- 触发方式3:使用flushdb/flushall也会触发RDB,但是里面是空的
- 触发方式4:当使用shutdown的时候也会触发RDB
触发后,保存的快照在我们指定的路径下
-
恢复数据
redis重启的时候就会自动读取配置文件中指定路径下的快照
当产生dump文件的时候,一定不要将文件放在和redis同一台机器上,应该隔离开,防止物理机损坏后备份文件也挂了
-
1.2.2 手动触发【只允许使用bgsave】
如果有一个非常重要的数据进来的,但是没有达到触发自动保存的条件,这个时候就可以手动触发保存
Redis提供了两个命令来shengchengRDB文件,分别是
save
和bgsave
,这两个命令一样,当使用这个命令之后,会开启一个子进程,用来立即保存快照。生产上只允许用
bgsave
不允许使用save
save命令在主程序执行会阻塞当前redis服务器,直到持久化工作完成,在执行save命令期间,Redis不能处理其他命令,所以线上禁止使用,用了就坐牢
bgsave
命令的子进程是在后台完成,所以允许主机进行操作
可以使用lastsave命令获取最后一次执行成功的时间
lastsave #redis内获取上一次执行的时间戳 date -d @时间戳 #linux中将时间戳转化为可以看懂的时间
1.3 RDB的优点
- 适合大规模的数据恢复
- 按照业务定时备份
- 对数据完整性和一致性要求不高
- RDB文件在内存中的加载速到要比AOF快得多
1.4 RDB的缺点
- 如果需要在Redis停止工作时(例如断电后)将数据丢失的可能性降到最低,RDB并不好,可能会丢失最新的数据
- RDB需要经常fork以便使用子进程在磁盘上持久化,如果数据集很大,fork可能会很耗时间,并且对cpu的性能不是很好,可能会导致停止对客户端服务
1.5 RDB修复命令
如果redis正在写入数据,可是写入一半命令的时候,服务器突然断电了,从而导致这个文件破损了
可以使用redis-check-rdb 命令修复,如果报错那就倒霉了
-
redis-check-rdb命令
redis-check-rdb 需要修复的rdb文件 #将指定的rdb文件修复
1.6 产生RDB快照的情况
- 满足配置文件中的条件
- 手动sava/bgsave命令
- 执行flushdb/flushall命令【里面是空的】
- 执行shutdown且没有开启AOF持久化
- 主从复制时,主节点自动触发【后面会讲】
1.7 如何禁用RDB快照【两种方法】
-
方法一:命令行执行
redis-cli config set save "" #只会在这一次有效
-
方法二:配置文件禁用【推荐使用】
save "" #永久有效
1.8 配置文件参数优化
1.8.1 stop-write-on-bgsave-error
stop-write-on-bgsave-error:当后台保存出错的时候是否停止写入【默认yes】【推荐yes】
如果配置成no,表示不在乎数据不一致,或者有其他的手段发现和控制这种不一致,那么在快照写入失败的时候,也能确保redis继续接收新的写入请求
1.8.2 rdbcompression
rdbcompression:是否压缩存储【默认yes】
对于存储到磁盘中的快照,设置是否进行压缩存储。
如果是,那么redis会采用LZF算法进行压缩
如果不想消耗CPU压缩,可以设置为关闭
1.8.3 rdbchecksum
rdbchecksum:rdb文件的合法性校验【默认yes】【就用yes】
1.8.4 rdb-del-sync-files
rdb-del-sync-files:主从复制的时候产生的选项【使用默认的即可】
2.AOF
2.1 AOF简介
以日志的形式记录每个写操作,将redis执行过的所有写指令记录下来(读操作不记录)【例如:set记录都记录下来,get操作不记录】
只许追加文件,但是不可以改写文件。
redis重启的时候就根据日志文件的内容**将写指令从前到后执行一次**完成数据的恢复工作
默认情况下没有开启AOF功能,需要修改配置文件开启
allendonly yes
AOF保存的文件名叫:appendonly.aof
2.2 AOF的持久化工作流程
①客户端作为命令的来源,会有很多个源头源源不断的写入命令
②这些命令到redis后,不会直接写入aof文件,而是先放入aof缓存区进行保存,当这些命令达到一定量以后再写入磁盘,从而避免频繁的磁盘IO操作
③aof缓冲区会根据写回策略【后面会讲】将命令写入到磁盘的AOF上
④随着写入内容的增加为了避免AOF膨胀,会根据规则进行命令的合并【又称AOF重写】,从而达到AOF文件的压缩效果
2.3 AOF缓冲区的三种写回策略
三种写回策略:Always、everysec【每一秒】、no
默认写回策略:everysec【配置文件中appendsync定义的】
- always:每个写命令执行完立刻同步的将日志写入磁盘【会频繁的进行磁盘IO操作】
- everysec:先把 日志写入到AOF的缓存区中,每隔一秒把缓存区中的数据写入到磁盘中
- no:只是把日志写入到AOF的缓存中,由操作系统决定什么时候将缓存内容写入到磁盘【不会进行频繁的进行IO,但是丢数据的概率增大】
2.4 开启AOF功能
-
开启AOF支持
appendonly yes #默认是no关闭,设置为yes就是打开aof
-
设置写回策略
appendfsync everysec #【默认】每秒写入一次【把另外两个注释掉】
-
设置AOF的保存路径
redis6以前AOF的保存路径和RDB一样,都是通过dir设置
redis7之后RDB和AOF区分开了,会在dir里面创建一个文件夹【appenddirname属性】单独保存aof文件
-
修改aof目录的名字
appenddirname "文件夹名字" #使用默认的即可 #aof文件夹路径:dir属性+appenddirname属性
-
-
设置aof文件保存路径
Redis6之前只有一个AOF文件
Redis7之后会有1-3个AOF文件,分别是:
- base:【表示基础AOF】一般由子进程重写产生
- incr:【表示增量AOF】保存的命令一般写道这里面
- history:【表示历史AOF】会被redis自动删除
-
修改aof文件名字:
appendfilename "XXX.aof"#默认即可
2.5 AOF正常恢复演示
- 重启redis-server服务
- 登录客户端
- 添加数据,aof的目录里是否由对应文件
- 然后关闭redis-server服务
- 然后重新启动验证是否可以正常加载
2.6 AOF异常恢复演示【修复文件】
AOF写到一般突然挂了,怎么修复
如果模拟AOF文件异常,重启redis之后会进行AOF文件的载入,发现启动都启动不了
可以使用:
redis-check-aof --fix 需要修复的aof.incr文件
2.7 AOF优缺点
-
优点:
可以更好的保护数据不丢失,性能更高,可以紧急恢复
-
缺点:
- AOF文件通常比相同数据集的等效RDB文件大,恢复速度慢于rdb
- aof运行效率慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同
2.8 AOF重写机制
只保留可以恢复数据的最小指令集
base文件会进行重写,incr会开一个新的文件
重写机制不会对旧文件进行整理,而是直接读取服务器现有的键值对,然后用一条命令代替之前记录的多条键值对的命令,然后生成新的文件替换原来的AOF文件
2.8.1 默认重写机制
配置文件中已经配置进行**默认重写的条件**:
auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb #达到多少M
- 根据上次重写后的aof大小,判断当前aof大小是不是增长了1倍
- 文件大小是否满足条件
只有同时满足,才会触发默认的重写机制
2.8.2 手动触发重写机制
客户端向服务器发送
bgrewriteaof
命令
3.RDB+AOF混合持久化【推荐使用】
如果AOF和RDB同时开启,会优先加载AOF
3.1 数据恢复顺序和加载流程
同时开启RDB和AOF持久化的时候,重启只会加载AOF,不会加载RDB
3.2 打开混合模式的方式
-
打开AOF
-
设置配置文件中
aof-use-rdb-preamble
的值为yes【yes表示开启,no表示禁用】
3.3 结论
使用RDB做全量持久化,AOF做增量持久化
4.纯缓存模式
只让redis做缓存,不做备份
同时关闭RDB和AOF
-
关闭RDB【仍然可以使用bgsave生成RDB文件】
save "" #修改配置文件中的save
-
关闭AOF【仍然可以私用bgrewriteaof生成aof文件】
appendonly no #禁用aof
七、Redis事务
1.Redis事务介绍
MySQL事务:一次会话中执行的的多条命令要么一起成功,要么一起失败【案例:银行转账】
Redis事务:一次操作可以按顺序执行多个命令,而不会被其他命令插入、加塞
2.Redis事务和传统数据库事务对比
- 单独的隔离操作:Redis的事实**仅仅保证事务里的操作会被连续独占的执行【Redis的命令执行是单线程的,在执行完事务执行李倩不可能再去执行其他客户端的请求】**
- 没有隔离级别的概念:因为事务提交前任何指令都不会被实际执行
- 不保证原子性:不保证所有的指令同时成功或同时失败【只有决定是否执行的能力,没有执行到一般回滚的能力】
- 排他性:Redis会保证一个事务内的命令依次执行,而不会被其他命令插入
2.基本命令
2.1 multi命令
开启事务
2.2 exec命令
执行事务
2.3 discard命令
取消事务【放弃执行事务内所有的命令】
2.4 watch命令
监控一个或多个key
一旦执行了exec,之前加的监控锁都会被取消掉
当客户端连接丢失的时候,所有的监控锁都会被取消
2.2 unwatch命令
取消watch对所有key的监控
3.基本使用
3.1 正常执行
只使用multi和exec命令
3.2 放弃执行
只使用multi和discard命令【写着写着,这个事务不想要了】【那么这个事务的所有操作都不会被执行】
3.3 全体连坐
编译的时候已经发现错误
有一条命令出错,整个事务全部取消【有错误命令会影响这个事务】
3.4 冤头债主
编译的时候没有发现错误,但是执行的时候发现了错误
事务中对的命令正常执行,错的也不管他【有错误命令不影响事务的执行】
3.5 watch监控
乐观锁:每次去拿数据的时候都会认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有更新这个数据【提交的版本必须大于当前版本才能更新】
悲观锁:每次拿数据的时候都会认为会被别人修改,所以在每次拿数据的时候都会加锁,阻塞别人访问这个数据,知道它访问结束
Redis支持乐观锁,如果发现访问的数据在提交的时候已经被改动了,那么事务就会执行失败
watch是用来监控key的
3.5.1 没有人动的情况
需要先监控key,再开启事务
3.5.2 有人动的情况
先监控key,当我进入事务后,在事务提交之前监控key被改变了,那么事务就会执行失败
八、Redis管道
1.管道理论简介
问题:如果同时需要执行大量的命令,那么就需要等待上一条命令应答后在执行【例如:执行三次set,每一次执行都需要等待上一次的结果】。这样就对进程性能有了很大影响
解决思路:可以一次性发送多条命令给服务器【例如:将上面三条set命令,整合成一条mset】,服务器一次处理完毕后,一次性将结果返回,从而降低了通信次数
管道定义:为了解决往返时长,仅仅**将命令进行了打包一次性发送**,对这个Redis的执行不造成其他任何影响
一句话总结:是批处理命令的变种优化措施,类似Redis的原生批命令【mget和mset】【但是mget和mset只能批处理string类型,其他的不可以】
2.管道实操
-
将需要操作的命令写到一个文件中
-
然后再linux中使用管道将结果传到redis中**【使用–pipe】**
cat 文件 | redis-cli -a 密码 --pipe #使用cat打开文件,然后将结果传到redis中
#例如 vim cmd.txt #创建文件,将命令写入 set k1 v1 set k2 v2 hset k3 v300 lpush list1 a b 2 3 h cat cmd.txt | redis-cli -a 密码 --pipe #执行reids的管道【将cmd.txt中的命令批量执行】
3.管道小结
3.1 管道与原生批命令的对比
- 原生批命令具有原子性(例如:mset,mget),管道pipe是非原子命令
- 原生批命令一次只能执行一种命令,pipe支持批量执行不同命令
- 原生批命令是服务端实现,而pipe需要服务端与客户端共同完成
3.2 管道与事务的对比
- 事务具有原子性,管道不具有原子性
- 管道一次性将多条命令发送到服务器,事务时一条一条的发送,事务之后又在介绍到exec命令之后才会执行,管道不会
- 执行事务时会阻塞其他命令的执行,而执行管道中的命令不会阻塞
3.3 使用管道的注意事项
- 管道会依次执行所有命令,但是不保证原子性,如果执行中有指令发生异常,将会继续执行后续命令
- 使用管道组装的命令个数不能太多,不然数据量过大客户端阻塞时间可能过久,同时服务端此时也会被迫回复一个队列答复,占用很多内存
九、Redis发布订阅
了解即可
是一种消息通信模式:发送者(publish)发送消息,订阅者(subscribe)接收消息,可以实现进程间的消息传递
Redis可以实现消息中间件MQ的功能,通过发布订阅实现消息的引导和分流【但是不推荐是使用,专业的事情交给专业的中间件处理】
缺点:
- 发布的消息在redis中不能持久化,必须先订阅然后在执行订阅【所以会丢失消息】
- 消息只管发送,不管用户是否接收到【因此实际生产中没没有用武之地】
十、Redis复制(replica)
1.主从复制理论简介
master(主机)以写为主,slave(从机)以读为主
当主机数据变化的时候,自动将新的数据异步同步到从机
2.能干什么
-
读写分离
学了主从复制之后:写操作去找主机,读操作去找从机,从而减轻主机的负担
-
容灾恢复
当主机挂了之后,从机上还有对应的数据
-
数据备份
-
水平扩容支撑高并发
一台主机可以支持多台从机,从而面对高并发的数据
3.怎么使用
-
配从库,不配主库(小弟拜大哥)
如果主机配置了==
requirepass
参数(登录密码),那么从机需要配置masterauth
==来设置校验密码,否则主机就会拒绝从机访问
4.基本命令
4.1、info replication
查看主从机器的关系
4.2、replicaof
主库IP,主库端口(一般是在redis.conf中配置)
4.3、slaveof
可以理解为是上面replicaof的及时版(因为上面那个是写在配置文件中的,而这个命令是在命令行中运行的)
每次与master断开之后,都需要重新连接(除非已经配置到redis.conf中),这个命令可以在运行期间修改slave的节点信息(如果该库已经是某个库的从机,那么会立即停止和原来主机的同步关系,转而和新的主数据库进行数据同步)
4.4、slaveof no one
使当前数据库停止与其他数据库的同步,转成主数据库,自立为王
5.案例演示
架构说明:一主,两从
要求:三台主机能够互相ping通(注意防火墙配置)
5.1 配置文件
所有的配置文件都切换为最原始的,所以需要重新修改配置文件(修改主机的,然后从机复制过来文件之后,修改对应的信息即可)
-
开启后台运行
daemonize yes
-
注销绑定的ip
#bind 127.0.0.1 -::1
-
关闭保护模式
protected- mode no
-
修改端口(从机需要修改为自己的)
port 6379 #这是主机的端口,从机需要修改为自己的
-
设置保存路径
dir /myredis
-
进程的id【可以使用默认的】
pidfile /var/run/redis_6379.pid #默认即可
-
日志文件路径【从机设置为对应的】
logfile "/myredis/6379.log"
-
登录密码
requirepass 密码
-
RDB文件名【从机改为自己的】
dbfilename dump6379.rdb #后面加上端口号即可
-
是否开启aof【看上面的】
appendonly yes
-
从机配置主机的地址、密码【只给从机配置】
replicaof 主机ip 主机端口号 #从机才需要这一步,主机不需要 masterauth 主机密码 #从机连接主机的密码
5.2 一主二仆
- 注意事项
- 从机只可以读,不能写
- 从机会复制主机的所有数据(自己上线前的数据也会复制下来)
- 主机挂了之后,从机还是从机,只不过连接状态由up改成了down
5.2.1 正常的启动
-
先启动主机,然后启动两台从机
redis-server 配置文件地址 redis-cli -a 密码
-
然后可以查看主从信息
info replication
-
主机写东西,会同步到从机
5.2.2 改换门庭
还是主从复制,只不过是用命令行确定主从关系
-
从机的配置文件中注释掉主从关系(密码不要注释)
#replicaof 主机ip 主机端口号
-
机器全部启动
-
从机的命令行中连接(等一会就会连接上)
slaveof 主机地址 端口号
5.2.3 自立为王
使用slaveof命令确定的主从关系,在从机重启之后就会删除
5.3 薪火相传
某台从机是另外机器的主机(上一个slave可以是下一个slave的master(还是不能自己写),从而减轻master的写压力)
中途变更转向:会清除之前的数据,重新建立拷贝新的数据
使用slaveof命令
-
在上面一主二仆的基础上,使用slaveof改变一台从机的主机【为另一台从机】
slaveof 另一台从机ip 另一台从机的端口号
5.4 反客为主
原本是从机,使用slaveof no one命令,从而变成自由个体,自立为王(主机的数据也都会从这里消失)
6.主从复制工作流程总结
- slave首次全新连接master之后,发送一个同步请求,全量复制数据会自动执行,slave自带的数据会被自动清空
- master节点收到同步命令后会在后台保存快照(RDB),同时收集所有用于修改数据集的命令并缓存起来,执行完RDB后,master会将RDB快照和缓存的命令发送到slave,以完成第一期全量同步。而slave服务在接收到数据库文件数据后,会将其加载到内存中,从而完成第一次数据同步
- 每10s钟发送一次心跳包,保证通信
- master继续将新的收集到的修改命令自动依次传给slave,完成同步
- 从机下线,然后又上线后,master会检查backlog里面的offset,master和slave都会保存一个复制的offset还有一个masterid,offset是保存在backlog中的,Master只会把已经复制的offset后面的数据复制给slave,类似端点续传
7.主从复制的缺点
-
复制延时,信号衰减
-
master挂了不会自动在slave节点中自动重选一个master
十一、Redis哨兵(sentinel)
1.哨兵监控理论简介
是什么:吹哨人巡查监控后台master主机是否故障,如果故障了根据投票数自动将某一个库转换为新的主库,继续对外服务
(解决了主从复制中主机挂掉后从机不能自主上位的问题)
作用:监控redis运行状态,包括master和slave;当master down机后,能自动将slave前换成新的master
2.能干什么
2.1 主从监控
监控主从redis库是否运行正常
2.2 消息通知
哨兵可以将故障转移的结果发送给客户端
2.3 故障转移
如果master异常,则会进行主从切换,将其中一个slave作为新的master
2.4 配置中心
客户端通过连接哨兵获得当前redis服务的主节点地址
3.实操案例
3.1 操作架构
一共6台机器
- 3个哨兵:自动监控和维护集群,不存放数据,只是吹哨人
- 1主2从:用于数据读取和存放
3.2 哨兵配置文件说明
-
拷贝一份redis安装路径下的sentinel.conf文件(防止后续改错了没法没法恢复)
-
重点参数说明:
- sentinel monitor 主机名 主机地址 端口号 最少投票数:设置要监听的master服务器【只有达到最少投票数才能认定为master下线】
- sentinel auth-pass 主机名 密码:master设置了密码,需要master密码
-
其他参数:
3.3 哨兵通用配置文件
由于硬件因素,三个哨兵都配置到一台主机上,所以需要拷贝三份配置文件【可以直接vim一个sentinel端口号.conf文件,然后内容直接复制到里面】
下面的配置内容根据自己的需要修改成自己的
#下面的配置内容根据自己的需要修改成自己的
#后台运行
daemonize yes
#查找保护模式【改为no】
protected-mode no
#允许访问的外部ip【默认只能是是本机】
bind 0.0.0.0
#哨兵的端口号
port 端口号
#日志文件地址【会自动创建这个日志文件】
logfile "/yfjconfig/sentinel端口号.log"
#进程名
pidfile /var/run/redis-sentine端口号.pid
#保存地址
dir /myredis
#监控的master信息
sentinel monitor mymaster 192.168.111.169 6379 2
#master密码
sentinel auth-pass mymaster 111111
3.4 实操开始
-
给master主机的配置文件设置masterauth【master主机密码】然后在启动
因为当它挂掉之后,哨兵会选一个新的主机,当他上线之后,他就是从机了
-
从机使用之前主从复制的配置文件启动
-
启动哨兵,没有报错就是运行正常
redis-sentinel sentinel26379.conf --sentinel
-
自己关闭master主机【模拟master挂了】
-
从机会有一个上位【数据不会丢失】
-
重新启动之前挂掉的机器,这个机器会变为从机
3.5 相关说明
- 配置主从机的配置文件内容会被哨兵修改,这也是为什么一开始的主机下线之后能够作为从机连接到新的主机
- 主从角色切换之后,master_redis.conf、slave_redis.conf、sentinel.conf的内容都会发生变化
- 一开始的master_redis.conf中会多一行slaveof的配置
- sentinel.conf的监控目标会替换
4.哨兵运行流程和选举原理
4.1 运行流程
4.2 运行流程
SDown主观下线:单个哨兵发送ping心跳后,在一定时间内没有收到合法的回复,就达到了主观下线的条件
ODown客观下线:多个哨兵达成一致意见才能认为一个master客观上已经下线
选举领导者哨兵(由谁进行指定master操作):当主节点被判断客观下线之后,各个哨兵会相互协商,选出一个领导者哨兵,该领导者会进行故障迁移【下面的几个步骤都是由leader自己完成】
选举领导者哨兵的算法**Raft算法**:
基本思路是先到先得,如果A跟B说我想当兵王,如果B没有投给其他人,那么B就会投给A
由兵王推动故障迁移,并选出一个新的master
- 某个slaver被选为master【规则:先判断谁的权限高,如果一样再判断谁的复制偏移量大,如果还一样就判断谁的id小】
- leader会对新选举出来的master执行slaveof no one命令,然后将其提升为master,然后leader向其他slave发送命令,让他们称为新的master的slave
- 当老master重新上线会被设置为新master的从节点
5.哨兵使用建议
- 哨兵节点应该为多个,哨兵本身应该为集群,保证高可用
- 各个哨兵的配置应该一致
- 哨兵节点的数量应该为奇数
- 如果哨兵节点部署在Docker等容器里面,尤其要注意端口的正确映射
- 哨兵集群+主从复制并不能保证数据的零丢失【发现、迁移需要时间】
十二、Redis集群(cluster)
1.集群是什么
上面的复制和哨兵是一个主机多个从机,只有一台主机负责写,当主机挂掉之后会有写的真空期,所以可以绑定多个主机进行写操作,每个主机只进行一部分写操作,从而形成一个集群
一句话:Redis集群是一个提供在多个Redis节点间共享数据的程序集(Redis集群支持多个Master)
个人理解:用户不关心从哪一个master写入,只要能写入,这个集群全部共享数据,当一台Master挂掉之后,不会影响整个系统的运行
2.集群能做什么
-
Redis集群支持多个Master,每个Master又可以挂在多个slave
从而实现:读写分离、数据的高可用、海量数据的读写存储操作
-
集群自带故障转移机制,所以无需再去使用哨兵功能
-
客户端与Redis连接时,不需要再连接集群中的所有节点,只需要任意连接集群中的一个可用节点即可
-
槽位slot负责分配到各个物理服务器节点,由对应的集群来负责维护节点、插槽和数据之间的关系
3.槽位slot
先记两个重要结论:
- 槽位最多16384个
- Reids集群建议小于等于1000个
每个key写入的时候都会根据一个算法去计算自己的槽位,槽位被所有节点平分,然后这个key会被存储到对应的槽位中。
例如当前的集群有三个节点:
4.集群分片
4.1 分片是什么
使用Redis集群的时候我们会将存储的数据分散到多台Redis机器上,这称为分片。
简而言之:集群中的每个Redis实例(主机)都被认为是整个数据集的一个分片
例如:上面槽位的案例,16384个槽位被分到了3片中,每个key存的时候去对应的片找对应的槽位
4.2 分片优势
添加或移除设备不会造成集群不可用,不会造成服务停止
5.三种分片算法
5.1 哈希取余分区
直接根据**key的hash值%Redis主机数**,算出哈希值,从而决定映射到哪一个节点上
- 优点:简单粗暴,直接有效,只需要预估好数据、规划好节点,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分数据落到同一台服务器上,这样每台服务器固定处理一部分请求,起到负载均衡的作用
- 缺点:公式的机器数是写死的,所以发生扩容和停机后,会导致数据全部洗牌
5.2 一致性哈希算法分区
目的:当服务器个数发生变动时,尽量较少影响客户端到服务器的映射关系
5.2.1 三大步骤
-
构建一致性哈希环
原本模的是机器台数,现在模的是2^32-1
-
redis服务器节点ip映射
将集群中各个ip节点映射到环上的某个位置
-
key落到服务器的落键规则
计算出key的hash值, 从这个值顺时针行走,第一台遇到的服务器就是该定位到的服务器
5.2.2 一致性哈希算法优缺点
-
优点:
- 容错性,如果有一台机器挂了,只会影响那一段数据的位置
- 扩展性,机器数量增减少只会导致hash区域全部数据重新洗牌
-
缺点:在服务节点太少是,容易节点分布不均匀从而造成**数据倾斜**(缓存的数据大部分都存在同一台服务器上)
5.3 哈希槽分区【最推荐】
哈希槽实际上是一个数组[0,2^14-1]形成的hash slot空间
在数据和节点之间又加入了一层哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据
槽解决的是粒度问题,粒度变大了,方便数据移动,哈希解决的是映射问题
一个集群有16384个槽
为什么槽位为16384个?
16484发送的心跳包为2kb,65536发送的心跳包为8kb,浪费宽带
槽位越小,节点少的情况下,压缩比高,容易传输
6.集群环境搭建
集群环境3主3从,因为电脑配置原因,主机和从机在一台电脑上
-
创建一个文件夹,用来存放集群配置文件
mkdir /yfjconfig/cluster #创建一个文件夹存放集群的配置文件 vim redisCluster6381.conf #创建这台机器的集群配置文件
-
将配置信息复制进去改为自己需要的【主机的】
bind 0.0.0.0 daemonize yes protected-mode no port 6379 #日志地址改成自己需要的 logfile "/yfjconfig/cluster/cluster6381.log" pidfile /yfjconfig/cluster6379.pid dir /yfjconfig/cluster dbfilename dump6379.rdb appendonly yes appendfilename "appendonly6379.aof" requirepass redis密码 masterauth 主机密码 #是否打开集群 cluster-enabled yes #集群配置文件的名字【联通成功后会自己创建】 cluster-config-file nodes-6379.conf #集群超时时间 cluster-node-timeout 5000
-
复制一份,改一下里面的端口号【从机的】
-
启动六台机器
redis-server 集群配置文件名
-
在一台主机上输入下面的命令,创建集群【–cluster-replicas 1 表示为每个master创建一个slave节点】
redis-cli -a 111111 --cluster create --cluster-replicas 1 主机1ip:主机1端口号 从机1ip:从机1端口 主机2ip:主机2端口 从机2ip:从机2端口 主机3ip:主机3端口号 从机3ip:从机3端口
-
可以使用info replication 查看主从关系
-
可以使用cluster nodes查看集群关系
-
连接客户端【特别注意连接集群一定要加参数-c】
#加参数-c是为了优化路由,不然不让写 redis-cli -a 密码 -p 端口号 -c
-
使用
sluster failover
可以切换当前机器为主机
7.集群扩容
上面的三主三从不够用了,需要新增主机
-
主机和从机需要上面的配置文件
-
启动两个机器
-
主机加入集群【不会分配槽位】
redis-cli -a 密码 --cluster add-node 自己实际IP地址:端口号 集群中已有主机的ip:端口号
-
分配槽位
redis-cli -a 密码 --cluster reshard IP地址:端口号
-
会询问分配槽位的大小
-
会询问给哪个机器【输入机器的id号】
-
然后输入all即可
-
添加从机
redis-cli -a 密码 --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新主机节点ID #例如:redis-cli -a 111111 --cluster add-node 192.168.111.174:6388 192.168.111.174:6387 --cluster-slave --cluster-master-id 4feb6a7ee0ed2b39ff86474cf4189ab2a554a40f-------这个是6387的编号,按照自己实际情况
-
检查集群情况
cluster nodes
8.集群缩容
-
先清除从机
-
获得从机的节点id
redis-cli -a 密码 --cluster check 从机ip:从机端口号
-
删除从机
redis-cli -a 密码 --cluster del-node 从机ip:从机端口 从机6388节点ID
-
-
清除主机的槽
-
清空槽位
redis-cli -a 111111 --cluster reshard 被清空主机ip:端口号
-
选择全部槽点都给出去【有多少给出去多少】
-
输入接收槽点的主机节点ip
-
输入被清空主机的节点ip
-
没有下一个了就输入done
-
-
检查集群情况
redis-cli -a 111111 --cluster check 集群中主机ip:端口号
-
删除没有槽点的主机
#命令:redis-cli -a 密码 --cluster del-node 被删除主机ip:端口 被删除主机节点ID redis-cli -a 111111 --cluster del-node 192.168.111.174:6387 4feb6a7ee0ed2b39ff86474cf4189ab2a554a40f
-
然后再检查一下集群情况
9.集群使用说明
-
不在同一个slot槽位下的键值无法使用mset、mget等多键操作命令
解决方式:可以通过{ }来定义同一个组的概念,{ }中内容向通的键值会放到一个槽中
例如:
mset k1{a} v1 k2{a} v2 k3{a} v3 #k1 k2 k3都会映射到a槽位中
mget k1{a} k2{a} k3{a}
-
Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384个槽去模来决定放置到哪个槽中,集群的每个节点负责一部分hash槽
-
如果集群中的一台主机和他所有的从机都挂了是否还能继续运行?
-
查看某个槽位是否已经被占用
cluster countkeysinslot 槽位编号 #0表示没有被占用,其他的表示被占用
-
查看某个键存放到哪个槽位上
cluster keyslot 键名
-
查看集群节点信息
cluster node
十三、SpringBoot集成Redis
0.java连接Redis注意事项
- bind配置注释掉
- 保护模式设置为no
- Linux防火墙设置
- Redis服务器的IP地址和密码
- 不要忘记写redis的服务端口号和auth密码
1.jedis介绍
是最早的使用客户端,是Redis官网推荐使用的
缺点:每个线程都要拿自己创建的jedis实例去连接客户端,当有多个线程的时候,需要反复创建关闭。而且线程不安全
-
建一个SpringBoot项目
-
引入jedis依赖
<!--jedis--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>4.3.1</version> </dependency>
-
在测试类中进行测试
public static void main(String[] args) { //获得连接 Jedis jedis = new Jedis("服务器ip",端口号);//第一个参数是服务器ip,第二个是端口号 jedis.auth("redis密码");//连接的密码 System.out.println(jedis.ping());//测试是否连接成功,返回pong就是连接成功 //String类型 jedis.set("k1","yfj"); System.out.println(jedis.get("k1")); Set<String> keys = jedis.keys("*");//获取所有key System.out.println(keys); jedis.expire("k1",20);//设置过期时间 System.out.println(jedis.ttl("k1"));//查看过期时间 //list类型 jedis.lpush("list","value1","value2","value3"); System.out.println(jedis.lpop("list")); }
2.lettuce介绍
jedis和lettuce的区别:lettuce是springboot2.0之后默认的客户端
底层使用的是Netty,当又多个线程都需要连接Redis的时候,能保证只创建一个Lettuce
-
创建Springboot项目
-
引入Lettuce依赖
<!--lettuce--> <dependency> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> <version>6.2.1.RELEASE</version> </dependency>
-
编写业务
public static void main(String[] args) { //1. 使用构建器链式编程来构建RedisURI RedisURI uri = RedisURI.builder(). redis("服务器ip"). withPort(端口号). withAuthentication("default","密码"). build(); //2.创建连接客户端 RedisClient redisClient = RedisClient.create(uri); StatefulRedisConnection connect = redisClient.connect(); //3.通过conn创建操作的command RedisCommands commands = connect.sync(); //=========各种命令=========== System.out.println(commands.ping()); //=================== //4.各种关闭释放资源 connect.close(); redisClient.shutdown(); }
3.RedisTemplate介绍【推荐使用】【必须掌握】
3.1 连接单机【一台Redis主机】
-
创建SpringBoot项目
-
引入依赖
<!--SpringBoot与Redis整合依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
-
编写配置文件
#连接的库号 spring.redis.database=0 # 修改为自己真实IP spring.redis.host=192.168.111.185 spring.redis.port=6379 spring.redis.password=111111 #连接池大小 spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1ms spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0
-
测试类中,自动注入redisTemplate对象
redisTemplate类需要下面的配置类来序列化。
StringRedisTemplate是redisTemplate的子类,存储非对象类型不要序列化,也就不需要配置类,但是存储对象类型的时候需要手动转换为json类型
-
编写配置类(修改springboot对redis默认的序列化设置,如果不设置,存进去的数据会被转义)
package love.junqing.redis_study.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { /** * redis序列化的工具配置类,下面这个请一定开启配置 * 127.0.0.1:6379> keys * * 1) "ord:102" 序列化过 * 2) "\xac\xed\x00\x05t\x00\aord:102" 野生,没有序列化过 * this.redisTemplate.opsForValue(); //提供了操作string类型的所有方法 * this.redisTemplate.opsForList(); // 提供了操作list类型的所有方法 * this.redisTemplate.opsForSet(); //提供了操作set的所有方法 * this.redisTemplate.opsForHash(); //提供了操作hash表的所有方法 * this.redisTemplate.opsForZSet(); //提供了操作zset的所有方法 * @param lettuceConnectionFactory * @return */ @Bean @Bean public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){ // 准备RedisTemplate对象 RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); // 设置连接工厂 redisTemplate.setConnectionFactory(connectionFactory); // 创建JSON序列化工具 GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); // 设置key的序列化 redisTemplate.setKeySerializer(RedisSerializer.string()); redisTemplate.setHashKeySerializer(RedisSerializer.string()); // 设置value的序列化 redisTemplate.setValueSerializer(jsonRedisSerializer); redisTemplate.setHashValueSerializer(jsonRedisSerializer); // 返回 return redisTemplate; } }
-
然后调用对象获取响应操作类型的对象(opxForXXX)
-
用返回的对象调用对应的方法即可
//redisTemplate方式 @Resource//@AutoWire注入不上就可以用@Resource private RedisTemplate redisTemplate; @Test void testString(){ //opsForValue就是String类型 ValueOperations valueOperations = redisTemplate.opsForValue(); valueOperations.set("name","胡歌"); System.out.println(valueOperations.get("name")); }
//StringRedisTemplate方式 class RedisStudyApplicationTests { @Resource private StringRedisTemplate stringRedisTemplate; //转成json工具 private static final ObjectMapper mapper=new ObjectMapper(); @Test void testString() throws JsonProcessingException { //准备放进去的对象 person p1 = new person("杨", 12); //手动序列化 String s = mapper.writeValueAsString(p1); //放进去 stringRedisTemplate.opsForValue().set("person",s); //读的时候反序列化 String val = stringRedisTemplate.opsForValue().get("person"); person person = mapper.readValue(val, person.class); System.out.println(person); } }
3.2 连接集群
经典故障:集群正常的时候可以连接,但是当一台主机挂掉之后,会造成连接失败
导致原因:Redis默认的连接池采用Lettuce,当集群节点发生变化之后,Letture默认不会刷新拓扑节点
解决方法:动态刷新节点拓扑视图
-
Springboot配置文件变成下面的,其他的跟上面的连接单机版一样
# ========================redis集群===================== spring.redis.password=redis密码 # 获取失败 最大重定向次数 spring.redis.cluster.max-redirects=3 spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1ms spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.min-idle=0 #支持集群拓扑动态感应刷新,自适应拓扑刷新是否使用所有可用的更新,默认false关闭 spring.redis.lettuce.cluster.refresh.adaptive=true #定时刷新 spring.redis.lettuce.cluster.refresh.period=2000 #节点ip和端口号要根据实际填写 spring.redis.cluster.nodes=192.168.111.175:6381,192.168.111.175:6382,192.168.111.172:6383,192.168.111.172:6384,192.168.111.174:6385,192.168.111.174:6386