Redis零基础小白篇

2023-11-03

一、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
    
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis零基础小白篇 的相关文章

  • 如何在多线程应用程序中使用 StackExchange.Redis IDatabase 对象?

    我从 StackExchange Redis 文档中收到有关如何使用 IDatabase 的混合消息 在里面基本使用文档 https github com StackExchange StackExchange Redis blob mas
  • 有没有办法在 Redis 和关系数据库中使用带有 @RedisHash 的实体?

    我正在使用Spring引导 为了将我的实体保存在关系数据库上 我配置了一个数据源和我的域类 例如 Entity Table schema schema name name tb name public class table name ex
  • StackExchange.Redis 和 StackExchange.Redis.StrongName 之间有什么区别?

    当我关注Azure时文档 http azure microsoft com en us documentation articles cache dotnet how to use azure redis cache 关于如何在Azure
  • 使用 sidekiq 处理两个独立的 Redis 实例?

    下午好 我有两个独立但相关的应用程序 他们都应该有自己的后台队列 阅读 单独的 Sidekiq 和 Redis 进程 然而 我希望偶尔能够将工作推给app2的队列来自app1 从简单的队列 推送的角度来看 如果app1没有现有的 Sidek
  • 如何清理redis中不活跃的玩家?

    我正在制作一个使用 redis 来存储游戏状态的游戏 它可以很好地跟踪位置和玩家 但我没有一个好的方法来清理不活跃的玩家 每当玩家移动时 这是一个半慢速移动游戏 想想每秒 1 5 帧 我就会用新位置更新哈希并删除旧位置键 跟踪活跃玩家的最佳
  • socket.io redis 和内存泄漏

    我的socket io版本是 电子邮件受保护 cdn cgi l email protection and 电子邮件受保护 cdn cgi l email protection 我在 Windows 上 在某些地方 我看到问题已得到解决 我
  • 使用brew在MacOSx上安装Redis JSON

    如何使用brew 在 macOSx 上安装 RedisJSON 如何在不编译redis的情况下启用redis上的模块 我不想使用 docker 客户端 Redis Stack 可能是最简单的方法 它不仅仅是 RedisJSON 还包括 Re
  • 如何在多个Lua State(多线程)之间传递数据?

    我在中启动Redis连接池redis lua 通过从 C 调用 我得到了redis lua state 此 Lua 状态全局启动一次 仅在其他线程中启动get从中 当有一个 HTTP 请求 工作线程 时 我需要从redis lua stat
  • 如何让客户端下载动态生成的非常大的文件

    我有一个导出功能 可以读取整个数据库并创建一个包含所有记录的 xls 文件 然后文件被发送到客户端 当然 导出完整数据库的时间需要大量时间 并且请求很快就会以超时错误结束 处理这种情况的最佳解决方案是什么 例如 我听说过使用 Redis 创
  • WSL Redis 遇到系统尚未使用 systemd 作为 init 系统(PID 1)启动。无法操作[已关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试遵循本文中讨论的 Redis 安装过程article https www digitalocean com community
  • 库存管理系统的 SQL 与 NoSQL

    我正在开发一个基于 JAVA 的网络应用程序 主要目的是拥有在多个称为渠道的网站上销售的产品的库存 我们将担任所有这些渠道的管理者 我们需要的是 用于管理每个渠道的库存更新的队列 库存表 其中包含每个通道上分配的正确快照 将会话 ID 和其
  • 如何将node.js管道传输到redis?

    我有很多数据要插入 SET INCR 到redis DB 所以我正在寻找pipeline http redis io topics pipelining 质量插入 http redis io topics mass insert通过node
  • SignalR 无法连接到 SSL 上的 Azure Redis

    我目前在 Azure 上托管我的 redis 缓存服务器 并让 signalR 依赖它作为骨干 使用以下内容 GlobalHost DependencyResolver UseRedis 服务器 端口 密码 eventKey 这可以在端口
  • Lua中按字符分割字符串

    我有像这样的字符串 ABC DEF 我需要将它们分开 字符并将两个部分分别分配给一个变量 在 Ruby 中 我会这样做 a b ABC DEF split 显然Lua没有这么简单的方法 经过一番挖掘后 我找不到一种简短的方法来实现我所追求的
  • 为什么 Redis TimeSeries 不捕获聚合中的最后一个元素?

    我试图了解 Redis 的时间序列规则创建的工作原理 但我很困惑为什么 Redis 会忽略聚合中的最后一项 并想知道这是否是预期的行为 我在中创建了示例代码redis cli为了显示 127 0 0 1 6379 gt FLUSHALL O
  • redis - 使用哈希

    我正在使用 redis 为我的 Web 应用程序实现社交流和通知系统 我是 redis 的新手 我对哈希值及其效率有一些疑问 我读过这篇很棒的文章Instagram 帖子 http instagram engineering tumblr
  • 有没有办法让特定的key在集群模式下定位到特定的redis实例上?

    我想让我的多锁位于不同的redis实例上 我发现redission可以指定一个实例来执行命令 但是如果该命令与key相关 则指定的实例会将命令传输到另一个实例 你能给我一些建议吗 你可以 但这并不是微不足道的 首先 Redis 在键中使用大
  • 如何使 Redis 缓存中数据层次结构(树)的部分内容无效

    我有一些产品数据 需要在 Redis 缓存中存储多个版本 数据由 JSON 序列化对象组成 获取普通 基本 数据的过程很昂贵 将其定制为不同版本的过程也很昂贵 因此我想缓存所有版本以尽可能进行优化 数据结构看起来像这样 BaseProduc
  • redis 2.8.7 Linux Sentinel环境配置问题,如何使其自启动,应该订阅什么?

    现在我们尝试使用 redis 2 8 7 作为缓存存储 来自使用 booksleeve 客户端的 NET Web 应用程序 目前看来这是一个非常有趣和令人兴奋的任务 redis 文档非常好 但由于缺乏真正的实践经验 我确实有几个关于如何正确
  • 想要在后台不间断地运行redis-server

    我已经下载了 redis 2 6 16 tar gz 文件并安装成功 安装后我运行 src redis server 它工作正常 但我不想每次都手动运行 src redis server 而是希望 redis server 作为后台进程持续

随机推荐

  • C++性能测试工具——gperftools的安装

    一 软件安装说明 gperftools的安装有两种方式 一种是源码方式 一种是直接安装模式 这里使用源码安装模式 原因是使用直接安装模式比较简单 安装此软件需要先安装libunwind这个软件 所以这里需要通过源码方式安装libunwind
  • 【机器学习】支持向量机【上】硬间隔

    有任何的书写错误 排版错误 概念错误等 希望大家包含指正 在阅读本篇之前建议先学习 机器学习 拉格朗日对偶性 机器学习 核函数 由于字数限制 分成两篇博客 机器学习 支持向量机 上 硬间隔 机器学习 支持向量机 下 软间隔与核函数 支持向量
  • CSS布局flex布局 对齐 等分 均分 详解

    一切都始于这样一个问题 怎样通过 CSS 简单而优雅的实现水平 垂直同时居中 记得刚开始学习 CSS 的时候 看到float属性不由得感觉眼前一亮 顺理成章的联想到 Word 文档排版中用到的的左对齐 右对齐和居中对齐 然而很快就失望的发现
  • 【leetcode】1143.最长公共子序列

    leetcode 1143 最长公共子序列 题目 思路 代码 复杂度 题目 leetcode原题链接 给定两个字符串 text1 和 text2 返回这两个字符串的最长 公共子序列 的长度 如果不存在 公共子序列 返回 0 一个字符串的 子
  • 如何快速查看并定位网页元素代码

    如何快速查看并定位网页元素代码 目的 可以迅速得找出一个网页中对应元素的html代码 1 首先我们打开一个网页 比如 百度首页 2 打开后我们会看到很多的文字链接以及按钮链接 那么我们找到我们想要查看的元素的文字或者按钮 3 我们这里以 百
  • @Cacheable注解属性介绍

    本文目录 1 value cacheNames 属性 2 key属性 3 keyGenerator 属性 4 cacheManager 属性 5 cacheResolver 属性 6 condition 属性 7 unless 属性 8 s
  • C++导出EXCEL开源库xlslib库使用心得

    使用教程 第一步 下载xlslib库 本文建立在xlslib2 5 0版本基础上 下载地址xlsLib download SourceForge net 第二步 切换到解压文件目录xlslib build msvc2008 打开项目xlsl
  • linux查询jvm运行内存使用情况,在Linux下获取正在运行的JVM的总使用内存

    您可以运行 ps aux grep java 这将显示包含在其推出的字符串java的每个应用程序的内存使用情况 这应该是大多数 如果不是所有的Java应用程序 从我的服务器的输出如下 servername servername ps aux
  • 超过飞飞系列-ZYNQ之FPGA学习2.1Verilog语法

    一 VHDL Verilog C语言区别 VHDL 硬件描述语言 美军开发 相对难 不直观 需要专业培训 欧洲发展较好 Verilog 硬件描述语言 设计群体广泛 资源成熟 中国多采用 并行处理运行 C 软件语言 经过C的单片机程序需取码
  • 简单工厂(Simple Factory)

    文章目录 1 代码示例 2 简单工厂模式的定义 实现意图 工厂模式 通过把创建对象的代码包装起来 做到创建对象的代码与具体的业务逻辑代码相隔离的目的 工厂模式可以细分为 简单工厂模式 工厂方法模式 抽象工厂模式 1 代码示例 include
  • servlet实现图片的上传

    servlet实现图片的上传 我们通常说的上传图片 是将图片上传到服务器上面 本篇以tomcat为例 实现简单的本地图片上传服务器 一 图片的上传需要引入两个jar包 commons fileupload 1 4 jar 下载地址 http
  • 深度详解 View.post() 为何能够获取到 View 的宽高值?

    文章目录 1 简介 1 1 问题描述 1 2 结果展示 2 源码分析 2 1 View post 方法添加任务 2 2 HandlerActionQueue post 方法添加任务 2 3 探究 AttachInfo 的由来 2 3 1 A
  • 爬取在线论坛帖子:使用 Python 获取帖子及评论

    在这篇博客中 我们将学习如何使用 Python 编写一个网络爬虫 从一个在线论坛 例如 Reddit 中获取帖子及其评论 我们将使用 requests 和 BeautifulSoup 库来实现这个功能 文章将包括以下内容 目录 1 爬虫的基
  • 重写、覆盖、重载、隐藏、多态几个概念的区别分析

    override gt 重写 覆盖 overload gt 重载 polymorphism gt 多态 override是重写 覆盖 了一个方法 以实现不同的功能 一般是用于子类在继承父类时 重写 重新实现 父类中的方法 成员函数的重载 o
  • 论文阅读:CLIP2Video: Mastering Video-Text Retrieval via Image CLIP

    动机 之前的大多都是试图从大规模的视频文本数据集中提取视频的时空特征以及视频和语言之间的多模式交互 作者将在图像语言中预训练的模型迁移到视频文本检索任务中 而之前这种使用这种方式的工作大多都是基于证明这种迁移学习是有效的 以验证CLIP模型
  • [BABEL] Note: The code generator has deoptimised the styling of "unknown" as it exceeds the max of "

    BABEL Note The code generator has deoptimised the styling of unknown as it exceeds the max of 500KB babelrc文件添加 compact
  • 构建Python pandas基于SSH远程MySQL和PostgreSQL的数据分析

    如果您无法从外部环境直接访问数据库 则可能需要SSH隧道来查询它 在这篇文章中 我将向您展示如何通过SSH连接并查询MySQL数据库到Pandas数据框 可以将相同的代码应用于连接到其他数据库 例如PostgreSQL 假设您的数据库托管在
  • Spring 基础教程之一:Spring简介

    明天就要讲传说中的spring了 不知道它是否像老师说的那样简单且神奇 spring的英文翻译是春天 泉水 弹簧 活跃的意思 不知道像我们这样的距找工作还有50天左右的人来说 我们的春天是否到了 在这个春天我们是否能够喝上甘甜的泉水 然后像
  • aps是什么意思_全画幅大还是中画幅大? 为什么说底大一级压死人

    经典摄影教程 总第十期 书接上文 是什么造成了画面的 空间感 其中我们谈到了当我们使用不同焦距的时候 我们的拍摄距离往往也会改变 但是这个焦距说的就是等效焦距 在什么是等效焦距 一文中 也说了等效焦距是因为传感器大小不同产生的讨厌的东西 那
  • Redis零基础小白篇

    一 Redis概述 1 是什么 是存在内存中的数据库 是Key Value数据库 MySQL是关系数据库 2 能干什么 一个程序中大部分操作都是查询 少部分操作是写入 所以用MySQL作存储 Redis作查询 所有查询先查询Redis 没有