一、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的卸载
-
停止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.命令查询
1.key的相关操作命令
1.1 查询所有key
1.2 判断key是否存在
1.3 查看key对应的值的类型
-
操作命令:type key名
-
案例:查看key1对应的value的值的类型
type key1
1.4 删除键值【阻塞删除】
阻塞删除,如果删除一个很大的数据,用时很长事件,它删不完,其他的操作也会阻塞到这里,直到它执行结束
-
操作命令:del key名
-
案例:删除key1和他的值
del key1
1.5 删除键值【非阻塞删除】
仅仅将kyes从keyspace元数据中删除,真正的删除会在后续异步中进行
不会阻塞其他线程
1.6 查看过期时间
查看还有多少秒过期,-1表示永不过期,-2表示已过期
-
操作命令:ttl key名
-
案例:查看key1还有多少秒过期
ttl key1
1.7 设置键值生存时长
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)
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命令
通过键获取值
2.3 mset命令
批量设置键值
2.4 mget命令
通过键批量获取值
2.5 msetnx命令
当键不存在的时候批量设置值
只要有一个键存在,整个命令就都不会被执行
2.6 getrange命令
获取value的部分值(字符串截取)
2.7 setrange命令
从指定位置开始用指定的值替换原来的值
2.8 数值的增减
一定要是数字才能进行增减
2.8.1 incr命令
给指定的值进行自增
默认加1
2.8.2 decr命令
给指定的值进行自减
默认减1
2.9 strlen命令
获取值的长度
2.10 append命令
在值的末尾追加内容
2.11 分布式锁
2.11.1 setex命令
将set和expire命令合成一个命令
setex是一个原子命令,创建和设置时间是同是的
2.11.2 setnx命令
将set和nx属性合成一个命令
2.12 getset命令
先get再set
和set使用get属性一样
-
基本语法:
getset k1 v1 #先获取值,然后再设置
3.List链表类型相关命令
3.1 lpush命令
从左侧添加数据到key(如果key已经存在就添加;key不存在就创建新的链表)
3.2 rpush命令
从右侧添加数据到key(如果key已经存在就添加;key不存在就创建新的链表)
-
基本命令:
rpush 键名 值1 值2 值3 ...
3.3 lrange命令
遍历指定范围的链表数据
如果范围是 0 -1则遍历所有
没有rrange
3.4 lpop命令
从左侧开始将数据弹出【默认是1个】
3.5 rpop命令
从右侧开始将数据弹出【默认是1个】
3.6 lindex命令
从左侧开始获取指定索引的数据
3.7 llen命令
获取链表的长度
-
基本命令:
llen 键名
-
基本案例:
llen list1
3.8 lrem命令
删除n个等于指定值的数据
3.9 ltrim命令
截取指定范围的值然后再赋值给key
3.9 rpoplpush命令
将源列表的1个数据从右侧弹出,添加到目的列表的左侧
3.10 lset命令
修改链表指定索引的值为指定的值
3.10 linset命令
将指定的目的数据插入到指定的已有数据前面/后面
3.10 应用场景
微信公众号:右两个人分别发了文章,就会依次加入到你的list中
4. Hash类型相关命令
KV模式不变,V里面存的是KV
应用场景:可以用于购物车【添加商品就是hset,增加数量hincrby,商品总数hlen,全选hgetall】
4.1 hset命令
4.2 hget命令
4.3 hmset命令
和hset一样
4.4 hmget命令
允许获取一个K里面的多个属性
4.5 hgetall
获取Key里面的所有键和值
4.6 hdel命令
删除key里面的键和值
4.7 hlen
4.8 hexists命令
判断这个key里面是否存在某个key
存在返回1,不存在返回0
4.9 hkeys命令
列举出里面所有的键
4.9 hvals命令
列举出key里面所有的值
4.10 hincrby命令
数字自增【默认自增1】
4.11 hincrbyfloat命令
小数增加
4.12 hsetnx命令
不存在命令生效
5.Set类型相关命令
无序、单key,多value【value不能重复】
会自动去除重复的值
应用场景:
- QQ和抖音推荐共同好友,或者可能认识的人【集合的运算】
- 抽奖程序【spop或者,srandmember】
- 微信点赞的好友
- 判断是否点赞过
5.1 sadd命令
5.2 smembers命令
遍历集合中的所有元素
5.3 sismember命令
判断元素是否在集合中
5.4 srem命令
删除集合中的元素
5.5 scard命令
统计集合中有多少个元素
-
基本命令:
scard 键名 #统计集合中有多少个元素
-
基本案例:
scard key1 #统计集合key1的长度
5.6 srandmember命令
随机展示N个元素,但是**不会从集合中删除**
5.6 spop命令
随机弹出N个元素,弹出一个删除一个
5.7 smove命令
将指定的元素从一个集合中迁移到另一个集合中
5.8 集合运算【重要】
5.8.1 计算差集【A-B】
5.8.2 计算并集【A+B】
5.8.3 计算交集
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命令
6.2 zrange命令【重要】
遍历指定索引范围的值【按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命令
获取查询值的分数
6.6 zcard命令
获取集合的长度
-
基本命令:
zcard 键名 #获取集合的长度
-
基本案例:
zcard set1
6.7 zrem命令
删除指定的值
6.8 zincrby命令【重要】
给指定的值加分数
6.9 zcount命令
获得指定分数范围内元素的个数
6.10 zrank命令
获得指定元素的下标值【0开始往下计数】
6.11 zrevrank命令
获得下标值【逆序:从下面往上计数】
7.bitmap类型相关命令
里面只能存0或1
位图的本质是一个String数组
应用场景:用户是否登陆过、钉钉上班打卡、百词斩打卡、广告是否点击过
7.1 setbit
设置索引位置的值【从0开始】
7.2 getbit
获取指定索引位置的值
7.3 strlen
统计字节数占用了多少【8位一个字节,8位1组,占用1位也是一个字节】
7.4 bitcount
统计全部键里面有多少个1
7.5 bitop
将两个位图进行逻辑操作(and、or、not)操作,结果存到另一个位图中
8.HyperLogLog类型相关命令
可以去重复进行基数统计,计算基数所需的空间总是固定的。
只会根据输入数据来进行计算基数,而**不会存储数据本身**【例如:有100万人访问我的网站,这里面只会存数字100万,而不会存这100万个人的数据】
人话就是:一个可以根据条件自动去重的计数器
基数:去重复后的真实个数【我访问了3次淘宝,去除重复那么只能算1】
案例:
- 统计今天访问网站的人(UV),需要去重
- 用户搜索网站关键词的数量
8.1 pfadd命命令
将数据去重后添加到里面
8.2 pfcount命令
返回给定HyperLogLog的基数估
-
基本命令:
pfcount 键名 #将去数据去重后添加到里面
-
基本案例:
pfcount key1 #返回去重后的个数
8.3 pfmerge命令
将多个HyperLogLog去重后合并到一个HyperLogLog中
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命令
根据名字返回经纬度
9.3 geodist命令
两个位置之间的举例
单位:m 米 km 千米
9.4 georadius命令【重点】
查找某个位置多大半径内附近的XXX
withdist:将自己的位置也一并返回
withcoodr:将经纬度一并返回
withhash:将hash值一并返回
count:返回最大记录数
9.5 georadiusbymember命令
和georadius一样,只不过将经纬度换成了具体的名字
9.6 geohash命令
返回坐标的hash值【因为geopos返回的值小数点位数很多,所以将经纬度转化为hash值】
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
10.1.2 xrange命令
获取消息列表
start表示开始值,-表示最小值
end表示结束值,+表示最大值
count表示最多获取多少个值
10.1.2 xrevrange命令
将消息队列反转过来【+ -也需要调转】【看上面的xrange】
10.1.3 xdel命令
按照时间戳删除消息
10.1.4 xlen命令
显示消息队列中有多少条消息
10.1.5 xtrim命令
对消息的长度进行截取,如果超长了会进行截取
maxlen:允许的最大程度
minid:允许的最小id【比该id小的值会被抛弃】
10.1.6 xread命令
读取消息
count:最多允许读取多少条消息
block:是否以阻塞的方式读取消息,默认不阻塞,如果为0表示永远阻塞
0-0:表示从最小的读起
$:表示等待最新的消息【比当前最后一个还要新】
10.2 消费组相关指令
10.2.1 xgroup create
用于创建消费者组
$:表示从stream尾部开始消费
0:表示从stream头不开始消费
10.2.2 xreadgroup
表示读取消息
stream中的消息一旦被阻力的消费者读取,就不能在被该消费组内的其他消费者读取
count:每个消费者允许读几条
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 命令修复,如果报错那就倒霉了
1.6 产生RDB快照的情况
- 满足配置文件中的条件
- 手动sava/bgsave命令
- 执行flushdb/flushall命令【里面是空的】
- 执行shutdown且没有开启AOF持久化
- 主从复制时,主节点自动触发【后面会讲】
1.7 如何禁用RDB快照【两种方法】
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文件保存路径
Redis6之前只有一个AOF文件
Redis7之后会有1-3个AOF文件,分别是:
- base:【表示基础AOF】一般由子进程重写产生
- incr:【表示增量AOF】保存的命令一般写道这里面
- history:【表示历史AOF】会被redis自动删除
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 一致性哈希算法优缺点
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