Redis零基础小白篇

一、Redis概述

1.是什么

是存在内存中的数据库

是Key-Value数据库(MySQL是关系数据库)

2.能干什么

一个程序中大部分操作都是查询,少部分操作是写入

所以用MySQL作存储,Redis作查询

所有查询先查询Redis,没有再查询MySQL,Redis会把自己没有的数据存到自己里面

  • 所以Redis和MySQL不是竞争的关系而是合作的关系,挡在MySQL前面的带刀护卫
  • 能够将内存中的数据异步写导硬盘上,同时不影响性能
  • 如果只有一台Redis,它挂了之后,会产生穿透、雪崩等事件。所以可以多搞几台Redis。
  • 分布式锁
  • 队列
  • 优势:
    1. 性能极高(每秒可读11万次,可写8万次)
    2. 数据类型丰富(不仅支持k-v,还支持list,set等数据结构)
    3. 支持数据的持久化(可以将内存的数据保存在硬盘上)
    4. 支持数据的备份

3.去哪下

官网地址:https://redis.io/

4.Redis的特性了解

  1. 命名规则

    版本号第二位如果是奇数,则为非稳定版 如:2.7,2.9,3.1

    版本号第二位如果是偶数,则为稳定版本 如:2.6

二、Redis安装及启动连接

  1. 确定Linux系统是64位

    getconf LONG_BIT
    
  2. 查看是否具有gcc编译环境

    gcc --version
    
  3. 如果没有gcc环境就使用命令安装C++库

    yum -y install gcc-c++
    
  4. 将redis的安装包移动到opt目录下

  5. 解压安装包

    tar -zxvf 安装包名
    
  6. 进入解压后的目录

  7. 进行编译安装

    make && make install
    
  8. 如果显示It's a good idea to run 'make test'就说明安装成功

  9. 默认安装目录:/usr/local/bin

    image-20230518125447502

  10. 将/opt/redis的解压包下的redis.conf复制一份,放到自己定义的路径下

    redis.conf是redis的配置文件

    这样做是为了:如果我们把本来的改坏了,还有备份可以使用

    cp redis.conf /yfjconfig/redis.conf
    
  11. 修改配置文件【复制的那个】

    1. 默认的daemonize no 改为daemonize yes 【作为服务器后端启动】
    2. 默认的protected-mode yes 改为protected-mode no 【关掉后可以让别人连接】
    3. 默认的 bind 127.0.0.1 -::1 直接注释掉【允许访问的ip地址,默认只能本机访问】
    4. 将注释的requirepass foobared打开,然后改为自己的密码
    vim redis.conf #打开配置文件
    /daemonize #查找daemonize【然后修改为yes】
    /protected-mode #查找保护模式【改为no】
    /bind 127.0.0.1 -::1 #允许访问的外部ip【注释掉】
    /requirepass  #设置密码【打开注释,改为自己的】
    
  12. 启动服务【按自定义配置文件】

    redis-server /yfjconfig/redis.conf
    
  13. 查看是否正常启动

    ps -ef|grep redis|grep -v grep
    
  14. 连接redis服务

    不写 -p 端口号 那么默认使用6379

    redis-cli -a 密码 -p redis的端口号 #默认6379
    #进去后会报个警告是正常的
    
  15. 验证是否可以正常使用

    ping #如果出现PONG就是正常启动
    
  16. 添加数据

    set k1 helloworld
    
  17. 获取数据

    get k1
    
  18. 退出客户端【没有关闭服务器】

    quit
    
  19. 关闭redis服务器

    • 单实例关闭

      redis-cli -a 密码 shutdown #在redis里可以直接执行shutdown命令
      
    • 多实例管理【可以关闭多个端口的】

      redis-cli -p 端口号 shutdown
      

三、Redis的卸载

  1. 停止redis-server服务【看上面的关闭服务器】

  2. 删除/usr/local/lib目录下与redis相关的文件

    rm -rf /usr/local/bin/redis-*
    

四、Redis10大类型介绍

注意:这里说的10大类型是value的数据类型,key的数据类型只有字符串

0.一图介绍

image-20230518145344632

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已经存在则添加数据
  • 如果值全移除,对应的键也会消失

image-20230518191152391

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 主要用于存储**地理位置信息**,并对存储的信息进行操作,包括:
    1. 添加地理位置的坐标。
    2. 获取地理位置的坐标。
    3. 计算两个位置之间的距离。

根据用户给定的经纬度坐标来获取指定范围内的地理位置集合

案例:我打车,车离我有多远

7. redis基数统计(HyperLogLog)

  • HyperLogLog 是用来做**基数统计(不重复)**的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定且是很小的
  • 在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比
  • 但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
  • 案例:统计今天访问天猫的人数(有人可能重复访问)

8. redis位图(bitmap)

位图:由0和1状态表现的二进制的bit数组

image-20230518151547148

作用:如果我的一个东西只有两种状态

例如:每天是否打卡。那么就可以使用这个

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

  • 操作命令

    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]
    
  • 参数说明

    1. EX seconds:设置以秒为单位设置过期时间
    2. PX milliseconds:设置以毫秒为单位设置过期时间
    3. EXAT unix-time-seconds:设置以秒为单位的unix时间戳所对应的过期时间
    4. PXAT unix-time-milliseconds:设置以毫秒为单位的unix时间戳所对应的过期时间
    5. NX:当键不存在的时候执行语句
    6. XX:当键存在的时候执行语句
    7. KEEPTTL:保存设置前指定的生存时间
    8. 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 键名 起始索引 结束索引
    
  • 基本说明

    1. 如果索引位置 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】

image-20230519114130513

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不能重复】

会自动去除重复的值

应用场景

  1. QQ和抖音推荐共同好友,或者可能认识的人【集合的运算】
  2. 抽奖程序【spop或者,srandmember】
  3. 微信点赞的好友
  4. 判断是否点赞过

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】

案例

  1. 统计今天访问网站的人(UV),需要去重
  2. 用户搜索网站关键词的数量

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形式存在

一个时间戳是一条完整的消息

要求

  1. 消息ID必须比上个ID大
  2. 默认用星号表示自动生成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文件重新写回内存中

image-20230520133422486

  • Redia7之前的保存规则

    1. 每间隔900s,如果有超过1个数据进行了变化,就重新写一份新的RDB
    2. 每隔300s,如果有超过10个数据发生了百安话,就重新写一份RDB
    3. 每隔60s,如果有超过10000个数据发生了变化,就写一份新的RDB文件
  • Redia6.2之后的保存规则

    不是必须几秒内必须修改几次,而是每多少秒检查一次,如果次数到了就会保存

    1. 每隔3600s检查一次,如果有超过1个数据进行了变化,就重新写一份新的RDB
    2. 每隔300s检查一次,如果有超过100个数据发生了百安话,就重新写一份RDB
    3. 每隔60s检查一次,如果有超过10000个数据发生了变化,就写一份新的RDB文件

1.2 RDB触发的方式【两种】

1.2.1 自动触发

默认就是按上面的规则,可以自己修改配置文件,设置自己想要的频率

  • 案例演示:每隔5s,如果有2次修改就保存

    1. 修改触发条件

      在后面添加上我们的触发条件即可

      save 时间 触发次数

      save 5 2 #每5秒钟如果修改2次就触发
      
    2. 修改配置文件中dump文件保存路径

      505行 有个dir 后面的就是我们的保存路径,修改为我们自己需要的

      :set nu #显示行号
      505 + G #直接跳转到对应行
      dir /yfjconfig/dumpfile #保存的路径【文件夹需要提前保存好】
      
    3. 指定dump文件保存的名称

      482 行有一个 dbfilename 后面的就是保存的名字,单机不修改也可以,但是后面如果有多台redis服务器,那么会重名,所以推荐加上端口端口号

      /dbfilename #查找这个字符串的位置【n是下一个】
      dbfilename dump6379.rdb #保存的文件名
      
    4. 重启服务器

      如果不放心就重启服务器

      redis-cli -a 密码 #登录客户端
      shutdown #结束进程
      quit #退出客户端
      redis-server /配置文件地址 #按配置文件启动进程
      
    5. 验证是否修改成功

      redis中支持使用config get查看配置文件的信息

      redis-cli -a 密码 #登录客户端
      config get dir #查看保存目录是否正确
      config get dbfilename #查看文件名是否正确
      
    6. 触发RDB

      1. 触发方式1:上面添加了触发规则,那么我们只需要5s内修改两次数据即可
      2. 触发方式2写入数据,超过5秒后在次写入数据也会触发【因为他检测到了2个数据变化】
      3. 触发方式3使用flushdb/flushall也会触发RDB,但是里面是空的
      4. 触发方式4当使用shutdown的时候也会触发RDB

      触发后,保存的快照在我们指定的路径下

    7. 恢复数据

      redis重启的时候就会自动读取配置文件中指定路径下的快照

      当产生dump文件的时候,一定不要将文件放在和redis同一台机器上,应该隔离开,防止物理机损坏后备份文件也挂了

1.2.2 手动触发【只允许使用bgsave】

如果有一个非常重要的数据进来的,但是没有达到触发自动保存的条件,这个时候就可以手动触发保存

Redis提供了两个命令来shengchengRDB文件,分别是savebgsave,这两个命令一样,当使用这个命令之后,会开启一个子进程,用来立即保存快照。

生产上只允许用bgsave不允许使用save

save命令在主程序执行会阻塞当前redis服务器,直到持久化工作完成,在执行save命令期间,Redis不能处理其他命令,所以线上禁止使用,用了就坐牢

bgsave命令的子进程是在后台完成,所以允许主机进行操作

可以使用lastsave命令获取最后一次执行成功的时间

lastsave #redis内获取上一次执行的时间戳
date -d @时间戳 #linux中将时间戳转化为可以看懂的时间

1.3 RDB的优点

  1. 适合大规模的数据恢复
  2. 按照业务定时备份
  3. 对数据完整性和一致性要求不高
  4. RDB文件在内存中的加载速到要比AOF快得多

1.4 RDB的缺点

  1. 如果需要在Redis停止工作时(例如断电后)将数据丢失的可能性降到最低,RDB并不好,可能会丢失最新的数据
  2. RDB需要经常fork以便使用子进程在磁盘上持久化,如果数据集很大fork可能会很耗时间,并且对cpu的性能不是很好,可能会导致停止对客户端服务

1.5 RDB修复命令

如果redis正在写入数据,可是写入一半命令的时候,服务器突然断电了,从而导致这个文件破损了

可以使用redis-check-rdb 命令修复,如果报错那就倒霉了

  • redis-check-rdb命令

    redis-check-rdb 需要修复的rdb文件 #将指定的rdb文件修复
    

1.6 产生RDB快照的情况

  1. 满足配置文件中的条件
  2. 手动sava/bgsave命令
  3. 执行flushdb/flushall命令【里面是空的】
  4. 执行shutdown且没有开启AOF持久化
  5. 主从复制时,主节点自动触发【后面会讲】

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

image-20230520161051792

2.2 AOF的持久化工作流程

image-20230520134716834

①客户端作为命令的来源,会有很多个源头源源不断的写入命令

②这些命令到redis后,不会直接写入aof文件,而是先放入aof缓存区进行保存,当这些命令达到一定量以后再写入磁盘,从而避免频繁的磁盘IO操作

③aof缓冲区会根据写回策略【后面会讲】将命令写入到磁盘的AOF上

④随着写入内容的增加为了避免AOF膨胀,会根据规则进行命令的合并【又称AOF重写】,从而达到AOF文件的压缩效果

2.3 AOF缓冲区的三种写回策略

三种写回策略:Always、everysec【每一秒】、no

image-20230520140522135

默认写回策略:everysec【配置文件中appendsync定义的】

  • always:每个写命令执行完立刻同步的将日志写入磁盘【会频繁的进行磁盘IO操作】
  • everysec:先把 日志写入到AOF的缓存区中,每隔一秒把缓存区中的数据写入到磁盘中
  • no:只是把日志写入到AOF的缓存中,由操作系统决定什么时候将缓存内容写入到磁盘【不会进行频繁的进行IO,但是丢数据的概率增大】

2.4 开启AOF功能

image-20230520160930141

  1. 开启AOF支持

    appendonly yes #默认是no关闭,设置为yes就是打开aof
    
  2. 设置写回策略

    appendfsync everysec #【默认】每秒写入一次【把另外两个注释掉】
    
  3. 设置AOF的保存路径

    redis6以前AOF的保存路径和RDB一样,都是通过dir设置

    image-20230520144300305

    redis7之后RDB和AOF区分开了,会在dir里面创建一个文件夹【appenddirname属性】单独保存aof文件

    image-20230520144542370

    • 修改aof目录的名字

      appenddirname "文件夹名字" #使用默认的即可
      #aof文件夹路径:dir属性+appenddirname属性
      
  4. 设置aof文件保存路径

    Redis6之前只有一个AOF文件

    Redis7之后会有1-3个AOF文件,分别是:

    1. base:【表示基础AOF】一般由子进程重写产生
    2. incr:【表示增量AOF】保存的命令一般写道这里面
    3. history:【表示历史AOF】会被redis自动删除

    image-20230520145928046

    • 修改aof文件名字:

      appendfilename "XXX.aof"#默认即可
      

2.5 AOF正常恢复演示

  1. 重启redis-server服务
  2. 登录客户端
  3. 添加数据,aof的目录里是否由对应文件
  4. 然后关闭redis-server服务
  5. 然后重新启动验证是否可以正常加载

2.6 AOF异常恢复演示【修复文件】

AOF写到一般突然挂了,怎么修复

如果模拟AOF文件异常,重启redis之后会进行AOF文件的载入,发现启动都启动不了

可以使用:

redis-check-aof --fix 需要修复的aof.incr文件 

2.7 AOF优缺点

  • 优点

    可以更好的保护数据不丢失,性能更高,可以紧急恢复

  • 缺点

    1. AOF文件通常比相同数据集的等效RDB文件大,恢复速度慢于rdb
    2. aof运行效率慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同

2.8 AOF重写机制

只保留可以恢复数据的最小指令集

base文件会进行重写,incr会开一个新的文件

重写机制不会对旧文件进行整理,而是直接读取服务器现有的键值对,然后用一条命令代替之前记录的多条键值对的命令,然后生成新的文件替换原来的AOF文件

2.8.1 默认重写机制

配置文件中已经配置进行**默认重写的条件**:

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb #达到多少M
  1. 根据上次重写后的aof大小,判断当前aof大小是不是增长了1倍
  2. 文件大小是否满足条件

只有同时满足,才会触发默认的重写机制

2.8.2 手动触发重写机制

客户端向服务器发送bgrewriteaof命令

3.RDB+AOF混合持久化【推荐使用】

如果AOF和RDB同时开启,会优先加载AOF

3.1 数据恢复顺序和加载流程

同时开启RDB和AOF持久化的时候,重启只会加载AOF,不会加载RDB

image-20230520161954699

3.2 打开混合模式的方式

  1. 打开AOF

  2. 设置配置文件中aof-use-rdb-preamble的值为yes【yes表示开启,no表示禁用】

3.3 结论

使用RDB做全量持久化,AOF做增量持久化

image-20230520162634284

image-20230520162653213

4.纯缓存模式

只让redis做缓存,不做备份

同时关闭RDB和AOF

  1. 关闭RDB【仍然可以使用bgsave生成RDB文件】

    save "" #修改配置文件中的save
    
  2. 关闭AOF【仍然可以私用bgrewriteaof生成aof文件】

    appendonly no #禁用aof
    

七、Redis事务

1.Redis事务介绍

MySQL事务:一次会话中执行的的多条命令要么一起成功,要么一起失败【案例:银行转账】

Redis事务一次操作可以按顺序执行多个命令,而不会被其他命令插入、加塞

2.Redis事务和传统数据库事务对比

  1. 单独的隔离操作:Redis的事实**仅仅保证事务里的操作会被连续独占的执行【Redis的命令执行是单线程的,在执行完事务执行李倩不可能再去执行其他客户端的请求】**
  2. 没有隔离级别的概念:因为事务提交前任何指令都不会被实际执行
  3. 不保证原子性:不保证所有的指令同时成功或同时失败【只有决定是否执行的能力,没有执行到一般回滚的能力】
  4. 排他性: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命令

image-20230521112614112

3.2 放弃执行

只使用multi和discard命令【写着写着,这个事务不想要了】【那么这个事务的所有操作都不会被执行】

image-20230521113201510

3.3 全体连坐

编译的时候已经发现错误

有一条命令出错,整个事务全部取消【有错误命令会影响这个事务】

image-20230521113904656

3.4 冤头债主

编译的时候没有发现错误,但是执行的时候发现了错误

事务中对的命令正常执行,错的也不管他【有错误命令不影响事务的执行】

image-20230521114909314

3.5 watch监控

乐观锁:每次去拿数据的时候都会认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有更新这个数据【提交的版本必须大于当前版本才能更新】

悲观锁:每次拿数据的时候都会认为会被别人修改,所以在每次拿数据的时候都会加锁,阻塞别人访问这个数据,知道它访问结束

Redis支持乐观锁,如果发现访问的数据在提交的时候已经被改动了,那么事务就会执行失败

watch是用来监控key的

3.5.1 没有人动的情况

需要先监控key,再开启事务

image-20230521120227615

3.5.2 有人动的情况

先监控key,当我进入事务后,在事务提交之前监控key被改变了,那么事务就会执行失败

image-20230521120553317

八、Redis管道

1.管道理论简介

问题:如果同时需要执行大量的命令,那么就需要等待上一条命令应答后在执行【例如:执行三次set,每一次执行都需要等待上一次的结果】。这样就对进程性能有了很大影响

解决思路:可以一次性发送多条命令给服务器【例如:将上面三条set命令,整合成一条mset】,服务器一次处理完毕后,一次性将结果返回,从而降低了通信次数

管道定义:为了解决往返时长,仅仅**将命令进行了打包一次性发送**,对这个Redis的执行不造成其他任何影响

一句话总结:是批处理命令的变种优化措施,类似Redis的原生批命令【mget和mset】【但是mget和mset只能批处理string类型,其他的不可以】

2.管道实操

  1. 将需要操作的命令写到一个文件中

  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 管道与原生批命令的对比

  1. 原生批命令具有原子性(例如:mset,mget),管道pipe是非原子命令
  2. 原生批命令一次只能执行一种命令,pipe支持批量执行不同命令
  3. 原生批命令是服务端实现,而pipe需要服务端与客户端共同完成

3.2 管道与事务的对比

  1. 事务具有原子性,管道不具有原子性
  2. 管道一次性将多条命令发送到服务器,事务时一条一条的发送,事务之后又在介绍到exec命令之后才会执行,管道不会
  3. 执行事务时会阻塞其他命令的执行,而执行管道中的命令不会阻塞

3.3 使用管道的注意事项

  1. 管道会依次执行所有命令,但是不保证原子性,如果执行中有指令发生异常,将会继续执行后续命令
  2. 使用管道组装的命令个数不能太多,不然数据量过大客户端阻塞时间可能过久,同时服务端此时也会被迫回复一个队列答复,占用很多内存

九、Redis发布订阅

了解即可

是一种消息通信模式:发送者(publish)发送消息,订阅者(subscribe)接收消息,可以实现进程间的消息传递

Redis可以实现消息中间件MQ的功能,通过发布订阅实现消息的引导和分流【但是不推荐是使用,专业的事情交给专业的中间件处理】

缺点:

  1. 发布的消息在redis中不能持久化,必须先订阅然后在执行订阅【所以会丢失消息】
  2. 消息只管发送,不管用户是否接收到【因此实际生产中没没有用武之地】

十、Redis复制(replica)

1.主从复制理论简介

master(主机)以写为主,slave(从机)以读为主

当主机数据变化的时候,自动将新的数据异步同步到从机

2.能干什么

  1. 读写分离

    学了主从复制之后:写操作去找主机,读操作去找从机,从而减轻主机的负担

  2. 容灾恢复

    当主机挂了之后,从机上还有对应的数据

  3. 数据备份

  4. 水平扩容支撑高并发

    一台主机可以支持多台从机,从而面对高并发的数据

3.怎么使用

  1. 配从库,不配主库(小弟拜大哥)

    如果主机配置了==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.案例演示

架构说明:一主,两从

image-20230718093518115

要求:三台主机能够互相ping通(注意防火墙配置)

5.1 配置文件

所有的配置文件都切换为最原始的,所以需要重新修改配置文件(修改主机的,然后从机复制过来文件之后,修改对应的信息即可)

  1. 开启后台运行

    daemonize yes
    
  2. 注销绑定的ip

    #bind 127.0.0.1 -::1
    
  3. 关闭保护模式

    protected- mode no
    
  4. 修改端口(从机需要修改为自己的)

    port 6379 #这是主机的端口,从机需要修改为自己的
    
  5. 设置保存路径

    dir /myredis
    
  6. 进程的id【可以使用默认的】

    pidfile /var/run/redis_6379.pid #默认即可
    
  7. 日志文件路径【从机设置为对应的】

    logfile "/myredis/6379.log"
    
  8. 登录密码

    requirepass 密码
    
  9. RDB文件名【从机改为自己的】

    dbfilename dump6379.rdb #后面加上端口号即可
    
  10. 是否开启aof【看上面的】

    appendonly yes
    
  11. 从机配置主机的地址、密码【只给从机配置】

    replicaof 主机ip 主机端口号 #从机才需要这一步,主机不需要
    masterauth 主机密码 #从机连接主机的密码
    

5.2 一主二仆

  • 注意事项
  1. 从机只可以读,不能写
  2. 从机会复制主机的所有数据(自己上线前的数据也会复制下来)
  3. 主机挂了之后,从机还是从机,只不过连接状态由up改成了down
5.2.1 正常的启动
  1. 先启动主机,然后启动两台从机

    redis-server 配置文件地址
    redis-cli -a 密码
    
  2. 然后可以查看主从信息

    info replication
    
  3. 主机写东西,会同步到从机

5.2.2 改换门庭

还是主从复制,只不过是用命令行确定主从关系

  1. 从机的配置文件中注释掉主从关系(密码不要注释

    #replicaof 主机ip 主机端口号 
    
  2. 机器全部启动

  3. 从机的命令行中连接(等一会就会连接上)

    slaveof 主机地址 端口号
    
5.2.3 自立为王

使用slaveof命令确定的主从关系,在从机重启之后就会删除

5.3 薪火相传

某台从机是另外机器的主机(上一个slave可以是下一个slave的master(还是不能自己写),从而减轻master的写压力)

中途变更转向:会清除之前的数据,重新建立拷贝新的数据

使用slaveof命令

  1. 在上面一主二仆的基础上,使用slaveof改变一台从机的主机【为另一台从机】

    slaveof 另一台从机ip 另一台从机的端口号
    

5.4 反客为主

原本是从机,使用slaveof no one命令,从而变成自由个体,自立为王(主机的数据也都会从这里消失)

6.主从复制工作流程总结

  1. slave首次全新连接master之后,发送一个同步请求,全量复制数据会自动执行,slave自带的数据会被自动清空
  2. master节点收到同步命令后会在后台保存快照(RDB),同时收集所有用于修改数据集的命令并缓存起来,执行完RDB后,master会将RDB快照和缓存的命令发送到slave,以完成第一期全量同步。而slave服务在接收到数据库文件数据后,会将其加载到内存中,从而完成第一次数据同步
  3. 每10s钟发送一次心跳包,保证通信
  4. master继续将新的收集到的修改命令自动依次传给slave,完成同步
  5. 从机下线,然后又上线后,master会检查backlog里面的offset,master和slave都会保存一个复制的offset还有一个masterid,offset是保存在backlog中的,Master只会把已经复制的offset后面的数据复制给slave,类似端点续传

7.主从复制的缺点

  1. 复制延时,信号衰减

    image-20230718140309824

  2. 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 哨兵配置文件说明

  1. 拷贝一份redis安装路径下的sentinel.conf文件(防止后续改错了没法没法恢复)

  2. 重点参数说明:

    image-20230718151348381

    • sentinel monitor 主机名 主机地址 端口号 最少投票数:设置要监听的master服务器【只有达到最少投票数才能认定为master下线】
    • sentinel auth-pass 主机名 密码:master设置了密码,需要master密码
  3. 其他参数:

    image-20230718152021975

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 实操开始

  1. 给master主机的配置文件设置masterauth【master主机密码】然后在启动

    因为当它挂掉之后,哨兵会选一个新的主机,当他上线之后,他就是从机了

  2. 从机使用之前主从复制的配置文件启动

  3. 启动哨兵,没有报错就是运行正常

    redis-sentinel sentinel26379.conf --sentinel
    
  4. 自己关闭master主机【模拟master挂了】

  5. 从机会有一个上位【数据不会丢失】

  6. 重新启动之前挂掉的机器,这个机器会变为从机

3.5 相关说明

  1. 配置主从机的配置文件内容会被哨兵修改,这也是为什么一开始的主机下线之后能够作为从机连接到新的主机
  2. 主从角色切换之后,master_redis.conf、slave_redis.conf、sentinel.conf的内容都会发生变化
  3. 一开始的master_redis.conf中会多一行slaveof的配置
  4. sentinel.conf的监控目标会替换

4.哨兵运行流程和选举原理

4.1 运行流程

image-20230718235348330

4.2 运行流程

  • SDown主观下线:单个哨兵发送ping心跳后,在一定时间内没有收到合法的回复,就达到了主观下线的条件

  • ODown客观下线:多个哨兵达成一致意见才能认为一个master客观上已经下线

  • 选举领导者哨兵(由谁进行指定master操作):当主节点被判断客观下线之后,各个哨兵会相互协商,选出一个领导者哨兵,该领导者会进行故障迁移【下面的几个步骤都是由leader自己完成】

    选举领导者哨兵的算法**Raft算法**:

    基本思路是先到先得,如果A跟B说我想当兵王,如果B没有投给其他人,那么B就会投给A

  • 由兵王推动故障迁移,并选出一个新的master

    1. 某个slaver被选为master【规则:先判断谁的权限高,如果一样再判断谁的复制偏移量大,如果还一样就判断谁的id小】
    2. leader会对新选举出来的master执行slaveof no one命令,然后将其提升为master,然后leader向其他slave发送命令,让他们称为新的master的slave
    3. 当老master重新上线会被设置为新master的从节点

5.哨兵使用建议

  1. 哨兵节点应该为多个,哨兵本身应该为集群,保证高可用
  2. 各个哨兵的配置应该一致
  3. 哨兵节点的数量应该为奇数
  4. 如果哨兵节点部署在Docker等容器里面,尤其要注意端口的正确映射
  5. 哨兵集群+主从复制并不能保证数据的零丢失【发现、迁移需要时间】

十二、Redis集群(cluster)

1.集群是什么

上面的复制和哨兵是一个主机多个从机,只有一台主机负责写,当主机挂掉之后会有写的真空期,所以可以绑定多个主机进行写操作,每个主机只进行一部分写操作,从而形成一个集群

一句话:Redis集群是一个提供在多个Redis节点间共享数据的程序集(Redis集群支持多个Master)

个人理解:用户不关心从哪一个master写入,只要能写入,这个集群全部共享数据,当一台Master挂掉之后,不会影响整个系统的运行

image-20230719093356571

2.集群能做什么

  1. Redis集群支持多个Master,每个Master又可以挂在多个slave

    从而实现:读写分离、数据的高可用、海量数据的读写存储操作

  2. 集群自带故障转移机制,所以无需再去使用哨兵功能

  3. 客户端与Redis连接时,不需要再连接集群中的所有节点,只需要任意连接集群中的一个可用节点即可

  4. 槽位slot负责分配到各个物理服务器节点,由对应的集群来负责维护节点、插槽和数据之间的关系

3.槽位slot

先记两个重要结论:

  1. 槽位最多16384个
  2. Reids集群建议小于等于1000个

每个key写入的时候都会根据一个算法去计算自己的槽位,槽位被所有节点平分,然后这个key会被存储到对应的槽位中。

例如当前的集群有三个节点:

image-20230719095649929

4.集群分片

4.1 分片是什么

使用Redis集群的时候我们会将存储的数据分散到多台Redis机器上,这称为分片。

简而言之:集群中的每个Redis实例(主机)都被认为是整个数据集的一个分片

例如:上面槽位的案例,16384个槽位被分到了3片中,每个key存的时候去对应的片找对应的槽位

4.2 分片优势

添加或移除设备不会造成集群不可用,不会造成服务停止

image-20230719100817625

5.三种分片算法

5.1 哈希取余分区

直接根据**key的hash值%Redis主机数**,算出哈希值,从而决定映射到哪一个节点上

  • 优点:简单粗暴,直接有效,只需要预估好数据、规划好节点,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分数据落到同一台服务器上,这样每台服务器固定处理一部分请求,起到负载均衡的作用
  • 缺点:公式的机器数是写死的,所以发生扩容和停机后,会导致数据全部洗牌

5.2 一致性哈希算法分区

目的:当服务器个数发生变动时,尽量较少影响客户端到服务器的映射关系

5.2.1 三大步骤
  1. 构建一致性哈希环

    原本模的是机器台数,现在模的是2^32-1

  2. redis服务器节点ip映射

    将集群中各个ip节点映射到环上的某个位置

  3. key落到服务器的落键规则

    计算出key的hash值, 从这个值顺时针行走,第一台遇到的服务器就是该定位到的服务器

    image-20230719113647377

5.2.2 一致性哈希算法优缺点
  • 优点:

    1. 容错性,如果有一台机器挂了,只会影响那一段数据的位置
    2. 扩展性,机器数量增减少只会导致hash区域全部数据重新洗牌
  • 缺点:在服务节点太少是,容易节点分布不均匀从而造成**数据倾斜**(缓存的数据大部分都存在同一台服务器上)

    image-20230719114105796

5.3 哈希槽分区【最推荐】

哈希槽实际上是一个数组[0,2^14-1]形成的hash slot空间

在数据和节点之间又加入了一层哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据

image-20230719114506252

槽解决的是粒度问题,粒度变大了,方便数据移动,哈希解决的是映射问题

一个集群有16384个槽

为什么槽位为16384个?

16484发送的心跳包为2kb,65536发送的心跳包为8kb,浪费宽带

槽位越小,节点少的情况下,压缩比高,容易传输

6.集群环境搭建

集群环境3主3从,因为电脑配置原因,主机和从机在一台电脑上

  1. 创建一个文件夹,用来存放集群配置文件

    mkdir /yfjconfig/cluster #创建一个文件夹存放集群的配置文件
    vim redisCluster6381.conf #创建这台机器的集群配置文件
    
  2. 将配置信息复制进去改为自己需要的【主机的】

    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
    
  3. 复制一份,改一下里面的端口号【从机的】

  4. 启动六台机器

    redis-server 集群配置文件名
    
  5. 在一台主机上输入下面的命令,创建集群【–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端口
    
  6. 可以使用info replication 查看主从关系

  7. 可以使用cluster nodes查看集群关系

  8. 连接客户端【特别注意连接集群一定要加参数-c】

    #加参数-c是为了优化路由,不然不让写
    redis-cli -a 密码 -p 端口号 -c
    
  9. 使用sluster failover可以切换当前机器为主机

7.集群扩容

上面的三主三从不够用了,需要新增主机

  1. 主机和从机需要上面的配置文件

  2. 启动两个机器

  3. 主机加入集群【不会分配槽位】

    redis-cli -a 密码 --cluster add-node 自己实际IP地址:端口号 集群中已有主机的ip:端口号
    
  4. 分配槽位

    redis-cli -a 密码 --cluster reshard IP地址:端口号
    
  5. 会询问分配槽位的大小

  6. 会询问给哪个机器【输入机器的id号】

    image-20230719183309533

  7. 然后输入all即可

  8. 添加从机

    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的编号,按照自己实际情况
    
  9. 检查集群情况

    cluster nodes
    

8.集群缩容

  1. 先清除从机

    1. 获得从机的节点id

      redis-cli -a 密码 --cluster check 从机ip:从机端口号
      

      image-20230719230542179

    2. 删除从机

      redis-cli -a 密码 --cluster del-node 从机ip:从机端口 从机6388节点ID
      
  2. 清除主机的槽

    1. 清空槽位

      redis-cli -a 111111 --cluster reshard 被清空主机ip:端口号
      
    2. 选择全部槽点都给出去【有多少给出去多少】

      image-20230719231220977

    3. 输入接收槽点的主机节点ip

      image-20230719231236249

    4. 输入被清空主机的节点ip

      image-20230719231349392

    5. 没有下一个了就输入done

      image-20230719231411784

  3. 检查集群情况

    redis-cli -a 111111 --cluster check 集群中主机ip:端口号
    
  4. 删除没有槽点的主机

    #命令:redis-cli -a 密码 --cluster del-node 被删除主机ip:端口 被删除主机节点ID 
    redis-cli -a 111111 --cluster del-node 192.168.111.174:6387 4feb6a7ee0ed2b39ff86474cf4189ab2a554a40f
    
  5. 然后再检查一下集群情况

9.集群使用说明

  1. 不在同一个slot槽位下的键值无法使用mset、mget等多键操作命令

    解决方式:可以通过{ }来定义同一个组的概念,{ }中内容向通的键值会放到一个槽中

    例如:

    mset k1{a} v1 k2{a} v2 k3{a} v3 #k1 k2 k3都会映射到a槽位中
    
    mget k1{a} k2{a} k3{a}
    
  2. Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384个槽去模来决定放置到哪个槽中,集群的每个节点负责一部分hash槽

  3. 如果集群中的一台主机和他所有的从机都挂了是否还能继续运行?

    image-20230719233247452

  4. 查看某个槽位是否已经被占用

    cluster countkeysinslot 槽位编号 #0表示没有被占用,其他的表示被占用
    
  5. 查看某个键存放到哪个槽位上

    cluster keyslot 键名
    
  6. 查看集群节点信息

    cluster node
    

十三、SpringBoot集成Redis

0.java连接Redis注意事项

  1. bind配置注释掉
  2. 保护模式设置为no
  3. Linux防火墙设置
  4. Redis服务器的IP地址和密码
  5. 不要忘记写redis的服务端口号和auth密码

1.jedis介绍

是最早的使用客户端,是Redis官网推荐使用的

缺点:每个线程都要拿自己创建的jedis实例去连接客户端,当有多个线程的时候,需要反复创建关闭。而且线程不安全

  1. 建一个SpringBoot项目

  2. 引入jedis依赖

    <!--jedis-->
    <dependency>
       <groupId>redis.clients</groupId>
       <artifactId>jedis</artifactId>
       <version>4.3.1</version>
    </dependency>
    
  3. 在测试类中进行测试

    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

  1. 创建Springboot项目

  2. 引入Lettuce依赖

    <!--lettuce-->
            <dependency>
                <groupId>io.lettuce</groupId>
                <artifactId>lettuce-core</artifactId>
                <version>6.2.1.RELEASE</version>
            </dependency>
    
  3. 编写业务

    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主机】

  1. 创建SpringBoot项目

  2. 引入依赖

    <!--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>
    
  3. 编写配置文件

    #连接的库号
    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
    
  4. 测试类中,自动注入redisTemplate对象

    redisTemplate类需要下面的配置类来序列化。

    StringRedisTemplate是redisTemplate的子类,存储非对象类型不要序列化,也就不需要配置类,但是存储对象类型的时候需要手动转换为json类型

  5. 编写配置类(修改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;
        }
    }
    
  6. 然后调用对象获取响应操作类型的对象(opxForXXX)

    image-20230720152340327

  7. 用返回的对象调用对应的方法即可

    //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默认不会刷新拓扑节点

解决方法:动态刷新节点拓扑视图

  1. 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
    
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值