Redis(1)

113 阅读28分钟
								瑞吉外卖--Redies

一:Redis简介:

Redis 是一个基于"内存"的key-value结构的"数据库"。(内存数据库)
Redis是NoSql数据库,即"非关系型数据库";
传统的关系型数据库MySql,主要存储在磁盘上; 而Redis主要主要在内存;
    
    
Redis优点:
1.基于"内存"存储、读写性能高。
2.适合存储"热点数据" (即在短时间内会有大量用户访问的数据 eg:热点商品、咨询、新闻、计数器,排行榜)
3.企业应用广泛;

Redis应用场景:
  1.缓存(使用比较多)
  2.任务队列
  3.消息队列
  4.分布式锁



## 二:Redis数据类型

~~~java
(1)"这里的数据类型指的是value的数据类型,key的类型都为字符串"!!!
 value有10大数据类型
  
(2)String是Redis的基本数据类型,一个key对应一个value!!!
    
(3)String类型是二进制最安全的,意思是redis的String可以包含任何数据,比如jpg图片或者序列化的对象

1.Redis常用10种数据类型:

介绍:Redis存储的是key-value结构的数据,其中key是"字符串类型""value""5种常用的数据类型":!!!!
   1.字符串: String 类型,常用
   2.哈希:hash :适合存储"对象"
   3.列表:list :按照插入顺序排序,可以有重复元素
   4.无序集合:set  :无序集合,没有重复元素
   5.有序集合:sorted set :有序集合,没有重复元素

三:Redis常用命令

Redis常用命令:用来操作Redis中的数据; (类似于通过sql语句操作mySql数据库)

(1).字符串String

1.字符串String操作命令

(1)
1. set key value :设置指定key的值;  "如果key相同的话,后面的value会覆盖之前的key"
    eg: set name xiaoming
2. GET key      :获取指定key的值 
3.keys *:查看所有的key
4.exists key :检查给定key是否存在    
5.type key:查看key所存储的value的数据类型   
6.del key :删除指定的key数据    
    
7.ttl key: 查看指定key还有多少s过期(-1表示永不过期,-2表示已过期)
8. expire key +s :为指定的key设置过期时间
    
9.select dvindex :切换数据库【0-15】,默认为0 --> :eg: select 0
10. move key dbindex【0-15】:将当前数据库的key迁移到指定的数据库dbindex中 -->eg: move k1 3   

11:dbsize:查看当前数据库key的数理
12:flushdb :清空当前库("慎用")
13:flushall:通杀所有数据库("慎用")
    
    
  
    
(2)
//同时设置/获取多个键值
1.mset key1 value1...:同时设置多个键值k-v -->:eg: set k1 v1 k2 v2...
2.mget key1...:同时获取多个value -->:eg: mget k1 k2 ...

//获取/设置指定区间范围内的值
1.getrange key:返回指定区间的值(0表示第一位,-1表示最后一位) -->eg: getrange k1 0 -1
2.setrange key:设置指定区间的值("可覆盖原来的") -->eg:setrange k1 0 aa:使用"aa""覆盖"k1对应的value的第一位起
   
//数值增减("一定要是数字才能进行增减")
1.incr key:为key对应value增加1
2.incrby key +数字 :为key对应value增加指定数字
3.decr key:为key对应value减少1
2.decrby key +数字 :为key对应value减少指定数字  
    
//获取字符串长度/字符串内容追加
strlen key :获取key对应value长度
append key +内容:value内容追加
    
//分布式锁
1.setex key seconds value : 设置指定key的值,并将key的过期时间设为seconds秒; ("常用于手机验证码")
    eg: setex city 10 beijing
2.setnx key value :只有key不存在时设置key的值,如果key存在就不执行;
        
//将key的值设置为value,并返回key的旧值(old value) 
getset key value

2.string应用场景

(3)string类型value的运用场合:
    --抖音点赞/收藏(incr key)

(2).哈希hash操作命令:

1.哈希简介:

KV模式不变,但是V是一个键值对

2.哈希hash操作命令

Redis hash 是一个String类型的field 和value的映射表, hash特别适合用于存储 "对象"
         
     "哈希结构":
                  value   
               field1 value1
    key ----->
               field2 value2
 //插入数据 /获取数据   
1. hset key field value : 将哈希表的key中的字段 的field值设置为value ("可以为key追加多个field-value")
      eg: hset user:001(对象):name xiaoming :"将user:001对象的name属性/字段设置为xiaoming"
          hset user:001 age 20 address sjz  : "将user:001对象的age属性/字段设置为20,address=sjz"
 
2. hget key field : 获取存储在哈希表中指定字段(field)的值(value)
      eg: hget 001 name : "获取001对象的name属性/字段的值"
          hget 001 age : "获取001对象age属性字段的值"
              
 //获取某个key内的全部field数量: heln
   hlen key -->:heln user:001 
 
 //key内是否存在某个字段: hexists
   hexists key field ---> hexists user:001 name
              
              
//删除
 hdel key field : 删除存储在哈希表中的指定字段
      eg: hdel 001 age : "删除001对象的age属性/字段的值"

 //获取哈希表key中的所有field和value :hkeys / hvals
1. hkeys key : 获取哈希表key中所有字段(即所有的field)
      eg: hkeys 001 : "获取001对象的所有属性/字段field"
          
2. hvals key : 获取哈希表key中所有的值 (即所有的value)
      eg: hvals 001 : "获取001对象的所有的value"
          
3. hgetall key : 获取在哈希表中指定key的所有字段和值 (即返回所有的field 和 value)
      eg: hegetall 001 : "返回001对象所有的field 和 value"
          
          
//为哈希表中的key对应的value(整数/小数)增加对应的值: hincrby / hincrbyfloat
  1.hincrby key field + 数字 -->:hincrby user1:001 source 1
  2.hincrbyfloat key field + 数字:--> hincrbyfloat user:001 source 0.5
      
//不存在则赋值,存在则操作无效 :hsetnx
  hsetnx key filed-value
      
          

(3).列表list:

1.list介绍

list:是一个"双端链表"的结构,主要功能有"push/pop"等,一般用在栈、队列、消息队列等场景;
"单key多value"


left、right都可以插入添加
    如果键不存在,创建新的链表
    如果已存在,新增内容
    如果值全移除,对应键也就消失了
    
    
 list:   
(1)Redis 列表 是 "简单的字符串列表"(列表中的每一个元素都是"字符串类型"的) ,按照"插入顺序排序";
(2)列表中的元素可重复、有序、为字符串(!!!!)         
(3)"使用list可以做 任务队列!!!!"
    
(4)  "列表结构":
                   value
  key -----> | a | b | c | d |
                          
              "尾部"      "头部"
                       
         下标: 0   1   2   3 
          
       遍历顺序: ------------->("从尾部到头部")

2.列表list操作命令:


//插入 /查询(lpush、rpush、lrnage)
1.lpush key value [value2] :将一个或多个值插入到"列表头部" 
     eg: lpush mylist a b c : "向 列表mylist 头部 依次插入 a b c"
   
   
2.lrange key start stop :获取列表指定范围内的元素 ("遍历的适合是从尾部开始,到头部!!!!!!!!!")
     eg: lrange mylist 0 -1 : '代表获取 列表mylist 从第一个到最后一个的元素'
         "查询结果为""c"   (因为 a先插入,b其次,c最后插入) ---> mylist : | c | b | a |
					 "b"
					 "a"
    
3.rpush key value...:将一个或多个值插入到"列表尾部" 
    eg: rpush mylist a b c : "向 列表mylist 尾部 依次插入 a b c" 
    
  之后:lrange key start stop
     eg: lrange mylist 0 -1 :
         "查询结果为""a"   ---> mylist : | a | b | c |
					 "b"
					 "c"    

 //移除列表中最后一个加入的元素: (lpop、rpop)
1.lpop key
             
2..rpop key :移除并获取列表中的"最后一个元素"
    eg:  rpop mylist :"移除并返回 mylisy列表 的最后一个元素 即 :c"

//list按下标取得值(lindex)
1. lindex key +下标 

        
//获取list列表中元素个数(llen)
1.llen key : 获取列表list长度(元素的个数)

    
//lrem key 数字N 给定值v1 :从left往右删除N个等于v1的元素
  eg: lrem key 0 :表示删除key列表全部给定的值。 "0个就是全部值"  ==del 列表
    
//ltrim key 开始index 结束index: 截取指定范围的值然后再赋值给key
 
//rpoplpush:移除列表的最后一个元素,并将该元素加到另一个列表,并返回    rpoplpush list1 list2
    
//lset key index value:使用value替换list中下标为index的值  
      
//linsert key before/after value1 value2:在value1前面/后面 插入value2

   
    
5. brpop key1 [key2] timeout : 移除并返回最后一个元素("即队尾(最右边)")的值, 如果列表没有元素就会阻塞列表指导等待超时  或 发现可弹出元素为止;
     eg:  
         当列表有元素时: brpop mylist 10 : " 移除并返回最后一个元素的值"
         当列表没有元素时: brpop mylist 10 : "会一直阻塞,直到10s结束"

(3)list应用场景:

eg:关注了订阅了一些作品,只要更新作品之后,他的作品就会依次进入我的list,然后按顺序排好

4.集合set操作命令:

(1)set介绍:

Set: 单值多value、无重复

(2)无序集合 Redis set

Redis set 是 "String类型""无序"集合,集合成员是"无重复"的,这就意味着集合中不能出现重复的数据
    
----------------------------------------基础操作-----------------------------    
//添加元素 / 遍历集合    
1.sadd key member1 [member2] :向集合中添加一个或多个成员
    eg:sadd myset a b c d :"向 集合myset 中插入 a b c d"
       sadd myset2 a b x y::"向 集合myset 中插入 a b x y"
        
2.semembers key : 返回集合中的所有成员
    eg:semebers myset: "返回 集合myset 中的所有成员(无序)"
      
3.sismember 集合 元素 :判断元素是否在集合中
    sismember myset 1
4..scard key :获取集合的成员数
    eg: scard myset 

5.srem key member1 [member2]:移除集合中一个或多个成员 //删除集合中的元素
    eg: srem myset a b c : "移除 集合myset中的 a b c"  
             

-------------------------------------------------------------------------------------------------------        
srandmember key 数字:从集合中随机展现  数字个数个元素,元素"不删除" --> srandmember myset 2:从myset中随机展现两个元素!!
  
spop key 数字:从集合中随机弹出一个元素,出一个在集合中"删除"1个 --> spop member 2:从集合member中随机弹出两个元素(之后这两个元素就不会再出现在member集合中)
    
------------------------------------------剪切操作-----------------------------------
 smove key1 key2:将key1里面已经存在的某个值 "拿出然后赋给"key2  --->smove member member1 4:将集合member集合中的4拿出然后赋值(放入)到member1集合中
     
------------------------------------------集合运算("!!!!!!!!!!!!!!!!!!!!!")---------------------------------
1.sinter key1 [key2] :返回给定所有集合的交集
    eg: sinter myset myset2 : "返回 集合myset 和 集合myset2 的交集" --> "即 "a"  "b"
        
        
   sintercard numberkeys(即几个key) key1 key2 limit +数字 :返回 将集合key1 和key2 去重+交集的个数
                                             limit:代表让它返回几个
        
        
2.sunion key1 [key2] : 返回给定所有集合的并集
    eg: siunion myset myset2 : "返回集合myset 和集合myset2 的并集"
        
3.sdiff key1 [key2] : 返回给定所有集合的差集("注意两个集合的前后顺序")
    eg: sdiff myset myset2 : "myset 和 myset2的差集 :即 myset-myset2 :--> "c" "d"
    eg: sdiff myset2 myset : "myset2 和 myset的差集 : 即myset2-myset : --> "x" "y"

(3)set应用场景:

主要应用:集合运算

1.微信抽奖小程序:  
a:有多少人想要参加: sadd key 用户id ("用户点击立即参与按钮--后台执行")
b:显示有多少人参加了: scard key("统计key里面有多少")
c:抽奖(从set中任意选取N个中奖人):--srandmember key 2("随机抽奖两个人,元素不删除)
b:spop key [数字] --有人抽到一个序号就去掉一个;--spop key 3: "随机抽奖3个人,元素删除
   
2.微信朋友圈点赞
                          
a: 新增点赞:sadd pub:myID(key) 点赞用户ID1 点赞用户ID2
b:取消点赞: srem pub:myID 点赞用户ID
c:展现所有点赞过的数据:smembers pub:msID
d:点赞人数:scard key
e:判断某个朋友是否对我点赞: sismember pub:myID 用户ID  
                                         
                                         
3.可能认识的人:
 b: "差集" sdiff key1 key2

5有序集合 Redis sorted set

(1)Zset介绍:

在set基础上,每个value值前面加了一个"score"分数值,作为排序的参考值

eg: 之前--> key     v1      v2...
                    |       |
  "Zset"--> key  score v1 score2 v2...

(2)zset命令

Redis sorted set "有序集合""String类型元素"的集合,且"不允许重复"的成员。
每一个元素都会关联一个"double类型"的分数(score)。
Redis正是"通过分数""为集合中的成员进行从小到大排序"。
有序集合的"成员是唯一"的,但是"分数可以重复";



1. zadd key score1 member1 [score2 member2] :向 有序集合 添加一个或多个成员, 或者更新已经存在的成员的分数
   eg: zadd myset3 10.0 a 9.0 b : "向 有序集合myset3 中 添加 a(10.0) b(9.0)"
       
-------------------------------------"取值"------------------------------------------------       
2.(1)zrange key start stop [withscores] : 通过索引区间 返回有序集合中指定区间内的成员
   eg: zrange myset3 0 -1 : "查看 有序集合myset3 内的成员(按照分数从小到大排序!!!)---> 即 b a" 
       zrange myset3 0 -1 withscores :"返回元素的同时也返回对应的分数"
           
  (2)zrevrange .......:反转:"从大到小排列" 
           
  (3)zrangebyscore key  minscore  maxscore withscores :查找zset中分数在 [minscore,maxscore]之间的元素
   
  (4)zrangebyscore key  (a b withscores :查找zset中分数在 (a,b]中的元素
                                                     
  (5).................limit    -->:limit:作用是返回限制
  
  (6)zscore key value :获取zset中对应value的分数   
                                                     
-------------------------------------"增加"               
3.zincrby key increment member :有序集合中对指定成员/元素的分数加上增量 increment
    eg: zincrby myset3 20 b : "给 有序集合myset3 中的 b元素的分数 +20"
    
4. zrem key member [member...] :移除有序集合中的一个或多个成员
    eg: zrem myset3 b : "移除 集合myset3 的 b元素"
                                                     
5.zcard key:返回zset中的value数量
                                                     
6.zcount key score1 score2:返回在score1-score2之间的元素个数
                                                     
7.("???")zmpop keynumber(几个key:eg:1/2/3) key  min count 1 :
  ---zmpop 1 zset min count 1:从zset中弹出                                                  
                                                 
--------------------------------"返回下标"-----------
 zrank key value:返回下标
 zrevrank ... :逆序返回下标                                                    

(3)Zset应用场景:

热销的商品榜单
打赏排行榜

eg: 思路:
 a:定义商品的排行榜: 使用"Zset" ,key为goods:sellsort, score为商品销售数量
 
 b:eg: 商品编号1001卖了10个,1002卖了11个 : zsadd goods:sellsort 10 1001 11 1002
    
 c:有客户又去买了2件商品1001,则商品1001销量+2 :zincrby goods:sellsort 2 1001
     
 d:求销量前10名: zrange goods:sellsort 0 9 withsocres

6.位图(bitmap):

(1)位图介绍

1.位图(bitmap):由01状态表现的二进制位的"bit数组" (0表示No,1表示Yes)
    
2.需求:
用于状态统计---Y/N 
3.位图本质是:"String类型的数组" (也可以用String的部分命令:get...)

(2)基本命令:

1.setbit
  setbit key offset("偏移量从0开始") value(只能设置为0/1)
     eg: serbit k1 1 1:将bitmap的第一位设置为1  :--eg:"可以表示某个人在第一天进行了打卡"
         serbit k1 2 0:将bitmap的第一位设置为0  :--eg:"可以表示某个人在第一天每有进行了打卡"
         setbit bit1 0 1
         setbit sign:u1:2024910(key) 3 1
         
 2.getbit key offset:获取bitmap对应位的值
 
 3.strlen key: 
   注:"8位是一个字节(即下标0-7为一个字节)"
       
 4. bitcount: 统计key对应的位图1的数量
     eg: bitcount uid:1ogin123
    
     也可以统计比如某段连续时间内都签到的人数:
      eg: setbit 2024910 0 1
          setbit 2024910 1 0
          
          setbit 2024911 0 1
          setbit 2024911 1 1
          
5. bitop and key 2024910 2024911 :返回这连续两天都登录的人数
 

7.HyperLogLog

(1)HyperLogLog介绍:

1.需求: 
    a:统计某个网站的UV、统计某个文章的UV -->什么是UV:独立访客,一般理解为客户端IP ("需要去重处理")
    b:用户搜素网站关键词的数量
    c:统计用户每天搜索不同词条的个数
        
        
 2.HyperLogLog就是-->"去重复统计功能的基数估计算法"
        基数:是一种数据集,去重复之后的真实个数
        "基数统计"
         eg: (全集)I={2,4,6,8,77,9,4,8,10} --"去掉重复的内容"--> 基数={2468773910}="7"
         eg: 每天这么多人访问网站,但是8号访问了多次,所以统计今天一共多少人访问:--去重复之后 一共73.HyperLogLog是"String类型"
             
"注意":它"不会"存储存入的元素本身所以并不会像集合那样返回具体的记录/元素,只是会返回"去重后的基数统计值";

(2)基本命令:

1. pfadd key eleHment [element...]:添加指定元素(element)到HyperLogLog中("一般是ip地址")
    -->: pfadd hyperlog1 1 3 4 7 9 5
    
2. pfcount key [key...] :返回给定HyperLogLog的基数估算值("去重之后的基数值")
        
    
3. pfmerge destkey sourcekey [sourcekey...]: 将多个HyperLogLog合并为一个HyperLogLog
    -->: pfmerge hyperlog3 hyperlog1 hyperlog2 -->hyperlog3即为合并之后的HyperLogLog

8.地理空间GEO

(1)GEO简介:

1.GEO简介:地理位置处理

原理:
    将三维的地球变为二维的坐标
    再将二维的坐标转换为一维的点块
    最后将一维的点块转换转换为二进制再通过base32编码

2.需求:
  eg:移动互联网时代的:交友软件的app、外卖软件附近的店铺、滴滴打车的位置信息;

(2)命令:

1.geoadd key 经度  纬度 位置名称 [经度  纬度 位置名称...]: "存储指定的地理空间位置"
  eg: geoadd  city 116.403963 39.915119 "天安门"
   
 "注意":GEO是Zset类型 "经度、维度就相当于分数!!!!!!!!!!!!!!!!!"     
     
2.geopos key "地址":返回GEO里面地址的经纬度
  eg: geops city "天安门"
      
3.!!!geohash key "地址...":"用geohash(哈希编码)表示坐标(经纬度)" :geohash算法生成的base32编码值、3维变2维变1维:
  eg: geohash city 天安门 故宫
      "将经纬度做了映射" -->eg: 116.403963 39.915119 --wx4g0f6f2v0
      
4.geodist key "地址1" "地址2" 单位(eg:m/km...) :两个地址直接的距离
  eg:geodist city 天安门 长城 m

5.!!!georadius: 以"给定的经纬度为中心",查找附近的xxx 
    
    参数: 
      1:withdist: 在返回位置的同时,将位置元素与中心元素之间的"距离也一并返回"。距离的单位和用户给定的范围单位保持一致;
    
      2.withcoord:将位置的经纬度也一并返回
          
      3.withhash:以52位有符号整数形式返回位置元素
         
      4.count +数字:限定返回的记录数
          
    eg: georadius city  116.6665 39.4523 10 km withdish withcoord withhash count 10 
        --->查找以 "经纬度:116.6665 39.4523" 为中心 返回它 10km内的位置的元素+经纬度+52位hash编码形式+返回10条数据
        
        
 6.georandiusbymember: 以"给定的位置元素为中心",查找指定范围内的元素
    eg: georandiusbymember city 天安门 10 km withdish withcoord withhash count 10

9.Redis流(Stream)

(1).Redis流(Strem流)简介:

1. Redis - Strem :"就是Redis版的MQ:消息中间键 + 阻塞队列"
    
    stream的类型是"stream"

2.Redis消息队列的两种方案:(了解即可)
   (1)List实现消息队列(下图) :list实现方式其实就是点对点的模式--即"一进一出" "一对一"2)pub/sub
       
3.Redis - Strem能干嘛: 
   实现消息队列,它支持消息的持久化,支持自动生成全局唯一ID、支持ack确认消息的模式、支持消费组模式等,让消息队列更加稳定可靠
       

       "注意":Stream不能100%等价于Kafka、RabbitMQ来使用,生产案例少,慎用

4.底层结构: Redis - Strem是一个消息链表,会将所有加入的消息串起来,每个消息都有一个唯一的ID和对应的内容


(2)基本命令

1.基本命令理论简介:
1.队列相关指令 :"消息和消息内容的生产者,一生产就放到队列里面"
    
2.消费组相关指令:将多个消费者放到一个消费组里面,消费组用来读取这些消息
    
3.特殊符号:("类似于MySql中的between、and")
2.基本命令
a:. 队列相关命令/更多的是生产者相关命令:
1. 队列相关命令/生产者相关命令: -->"即如何向Stream中添加消息"
    
 (1):xadd:向Stream队列中添加消息,如果指定的Stream流不存在,则该命令执行时会新构建一个Stream队列(添加消息到队列末尾) ("先      进先出")
    
    a: 消息ID必须比上一个ID大
    b: 默认用星号表示自动生成消息ID ,后面顺序跟着 一堆 业务key/value: " * 用于xadd命令,让系统自动生成id"
        
    eg: xadd mystream * id 11 name zs -->:"1726057454100-0"(这个就是生成的消息ID:格式:时间戳-自增ID)
        xadd mystream * k1 v1 k2 v2 k3 v3


 (2)xrange: 用于获取消息列表(可以指定范围),忽略删除的消息
           
            start:表示开始值, -代表最小值
            end:表示结束值, +代表最大值
            count:表示最多获取多少个值
                
    eg: xrange mystream - +  :表示返回mystream 中的所有消息
        
     1) "1726057454100-0" ------> key
     2) 1) "id"
        2) "11"  
        3) "name"  ------------>value:这些都是value:value是什么格式的都行!!!!
        4) "zs"

  
       
        xrange mystream - + count 1: 表示返回mystream 中的1条消息 ("先进先出!!!")
            
        
            
        
 (3)xrevrange: 反转输出stream中的消息
    eg: xrevrange mystream + -       
        
 (4)xdel stream id(主键eg:1726057454100-0):删除指定id的消息
        
 (5)xlen:用于获取Stream流队列的长度
        
        
 (6)xtrim:用于对Stream的长度进行截取,如超长会进行截取
         maxlen:"允许最大长度",对流进行剪切限制长度
         minid:"允许最小id",从某个id值开始 比该id小的都会被抛弃
             
  eg1: xtrim mystream "maxlen" 2: 允许最大长度是2即--在stream中剩下2条信息,其余的都截去("且被截取的是时间戳小的--先进先          出")
  eg2: xtrim mystream "minid" 1726057473646-0: 允许最小id是1726057473646-0,比这个id小的都会被从stream中去掉;


 (7)xread:"用于获取消息"(阻塞/非阻塞),只会返回大于指定ID的消息
          count:最多读取多少条消息
          block:是否以阻塞的方式读取消息,默认为不阻塞,如果milliseconds设置为0,表示永远阻塞;

    --->xread [count num:返回几条数据] [block milliseconds] streams key [key...] ID [ID...]
        
      1.:读取"非阻塞"消息:  
     eg: xread count 10 streams mystream 0-0 :表示从mystream中读取10条数据(0-0:表示"从最小ID开始获取stream中的消息,从头          读到尾部,当不指定count时,将会返回stream中的所有消息",也可以使用0(00/000...))
         
         
      2.:获取"阻塞"消息:(类似于java中的阻塞队列)
     eg:    xread count 1 block 0 stream mystream $:表示阻塞队列
            
           之后外我们新开一个客户端:执行xadd mystream * k8 v8之后,之前的阻塞队列的客户端会立即弹出新的ID即k8 v8的ID!!!
 
"$:代表特殊ID,表示监听来获取最大的ID,如果已经是最大ID了的话,就返回nil;"

b.消费组相关指令
消费者/组:消费队列中的消息,需要将消费者分组创建消费组

1.---->用于创建消费组: xgroup create 队列名称 分组名称 0/$   

 "注意": (1"$":表示从Stream尾部开始消费
        (2"0":表示从Stream头部开始消费
         "创建消费者组的时候必须指定ID,ID为0表示从头开始消费,ID为$表示只消费新的消息---即队尾新来的消息"

        eg:  xgroup create mystream groupX $ (从尾部开始读)
             xgroup create mystream groupA 0 (从头读到尾)
            
2.------>用于消费组读取stream队列中的消息: xreadgroup group :
            
            一:一个消费者全读:
           
           (1)">":表示从第一条尚未被消费的消息开始读取 (即:游标,读取一条消息后,游标就会往前走)
              
           (2)"!!!!!!!!"
                       A:stream中的消息一旦被消费组的1个消费者读取之后,这个消费组的其它消费掌者就不能再读取了
                          --->即同一个消费组的消费者不能消费同一条消息
                       B:但是 "不同消费组"的消费者可以消费同一条消息
                           
                           
                       --------------------------->"即按组进行读取消息"
                           
                           
                    以xgroup create mystream groupA 0为例
            1.eg :  xreadgroup group groupA consumer1 stream mystream > 
                 "表示:groupA消费组中的consume1消费者读取mystrem队列中的消息,然后就会返回mystream中的消息,之后再用这个消费组的其它消费者读取这个队列中的消息eg: xreadgroup group groupA consumer2 stream mystream > 就会返回空值nil,因为consume1消费者以已经将游标从头刷到尾了"
                
            2.eg: xreadgroup group groupB consumer1 stream mystream >
                "groupB组的消费者仍然可以读取,但是也只能1个消费者进行读取"
                
                
                
                
            二: 让组内的每一个消费者都读取一部分消息,实现负载均衡
                eg:
                xreadgroup group groupC consumer1 count 1 streams  mystream >
                xreadgroup group groupC consumer2 count 2 streams  mystream >
                "令groupC消费组的consumer1读取mystrems的1条消息,consumer2读取2条消息"
                    
                    
                    
                    
                    
 3."!!!!"已读已签收(ack) 和 已读未签收     
                    
     (1)xpedning stream流名称 消费组---> xpending:查询每个消费组内的所有消费者 ["已读取、但尚未确定" 的消息]
                    
        eg1: 
            xpending mystream groupC:
                1) (integer) 2     --->代表mystream流中共有两条消息
                2) "1726057454100-0" -->代表:所有消费者读取的最小消息的ID
                3) "1726138115816-0" -->代表: 所有消费者读取的最大消息的ID
                4) 1) 1) "consumer1" -->代表:consumer1读取了1条数据
                      2) "1"
                   2) 1) "consumer2" -->代表:consumer1读取了1条数据
                      2) "1"
                       
      eg2:xpending mystream groupC - + 10 consumer2 :返回10条groupC消费组的consumer2读取mystream但未确认的消息
                    
     (2)Xack:向消息队列发送确认消息   
            
         eg1: xack mystream groupC "1726057454100-0" :表示向消息队列发送确认信息,确认"1726057454100-0"对应的消息"已读取          已确认"; --->之后xpending 就不会出现这条消息了
             
             
             
4. xinfo:用于打印stream/消费者/分组 的详细信息
    -->xinfo stream mystream
             
        

10:位域:bitfield(了解即可)

11.通用命令:

通用命令: "针对key" 进行操作,即针对不同的类型都可以使用这些命令
 
    
1.keys pattern :查找所有符合给定模式的(pattern) 的"key"
    eg: keys * ("!!!!") : "查找所有的key"
         "001"
         "name"
         "myset3"
         "age"
         "myset"
         "key1"
         "myset2"

2.exists key :检查给定key是否存在
    eg: exists name "检查name是否存在"

3.type key :返回key所存储的值value的类型
    eg:  type name --> "String"
   
4. tll key :返回给定key的剩余生存时间(TLL,time to live),以秒为单位
 
5. del key :该命令 用于在key存在时删除key-value

四:在Java中操作Redis(!!!)

1.使用"Jedis"操作Redis

1.使用"Jedis"操作Redis中的数据
2."注意":  方法名和命令是一样的 !!!!!!!!!
3.案例:

public void testRedis(){

  
        
    //1.获取连接,连接到Redis
        Jedis jedis=new Jedis("localhost",6379); //指定连接的Redis服务(localhost本地)

    //2.执行具体操作
    
    
       "(1)下面以String类型数据进行操作!!!"
           //eg:我们要操作一个字符串类型的数据 比如向Redis中存入一个string类型的值 --key-value
        jedis.set("username","xiaoming");

          //get查询
        String value= jedis.get("username");
        System.out.println(value);

         //del删除
        jedis.del("username");
    
     "(2)下面以哈希类型数据进行操作!!!"
           //操作哈希类型的数据: eg:向Redis中存入(hset)一个哈希类的值 key-value
        jedis.hset("myhash","addr","beijing");

        //获取哈希的值:hget , 指定key,指定value
        String hValue = jedis.hget("myhash", "addr");
        System.out.println("hValue="+hValue);

    
    "(3)java中Redis的通用命令!!!"
        Set<String> keys = jedis.keys("*");  (=keys *)

    
     //3.关闭连接
        jedis.close();
    
    
    }

2.使用Spring Data Redis 操作Redis(!!!!!!!!!!)

1."在Spring Boot项目中,使用Spring Data Redis来简化Redis操作!!!!!!!!!"
  //操作Redis:通过redisTemplate模板类 调用相应的方法可以获取相应的对象,然后通过相应的对象来操作相应的数据!!!
  Spring Data Redis 底层是对jedis作的封装
2.导入maven坐标:spring-boot-starter-data-redis
    
    

Spring Data Redis

Spring Data Redis 提供了一个高度封装的类:" RedisTemplate ",针对jedis客户端中大量的api进行来归类封装,将同一类型操作封装为operation接口,具体分类如下:
  
  ValueOperations:简单K-V操作 (eg:String类型的数据) 
  SetOperations:set类型数据操作 
  ZSetOperations:zset类型数据操作(eg:有序集合类型的数据)
  HashOperations:针对map类型的数据操作
  ListOperations:针对list类型的数据进行操作
  
  注意:"5个接口,来操作5种对应的数据类型"
  

(1)Spring Data Redis 操作String数据

public class SpringDataRedisTest {

    @Autowired
    private RedisTemplate redisTemplate; //只要application.yml中写了配置,就可以注入进来

 //操作Redis:通过redisTemplate模板类 调用相应的方法可以获取相应的对象,然后通过相应的对象来操作相应的数据!!!

    /**
     * (1)操作String类型数据,即简单的K-V
     * -->通过redisTemplate调用opsForValue()方法获得ValueOperations对象,然后使用这个对象来通过set等方法对String数据进行操作
     */
    @Test
    public void testString(){
       /*ValueOperations valueOperations = redisTemplate.opsForValue(); //获取对象
       valueOperations.set("city","beijing"); */

        //a:通过set方法为String类型数据赋值
        redisTemplate.opsForValue().set("city123","beijing"); //连着写
        //b:通过get方法获取
        String value=(String) redisTemplate.opsForValue().get("city123");
        System.out.println("Value="+value);
        //c:设置过期时间
        redisTemplate.opsForValue().set("key0","value0",10l, TimeUnit.SECONDS); //key-value 10 单位:秒
        //d:只有key不存在时设置key的值,如果key存在就不执行 (set key value)
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent("city123", "nanjing");
        System.out.println("aBoolean="+aBoolean); //false:代表"city123"已经存在,不会执行这条语句
    }
}

(2)Spring Data Redis 操作Hash数据

/**
     * (2)操作Hash类型数据
     * -->通过redisTemplate调用opsForHash()方法获得HashOperations对象,然后使用这个对象来通过put等方法对Hash数据进行操作
     */
    @Test
    public void testHash(){

        //获取hashOperations对象
        HashOperations hashOperations = redisTemplate.opsForHash();

        //a:存值:使用Hash"存储"对象的数据 (eg:为对象002存储数据)
        hashOperations.put("002","name","ikun");
        hashOperations.put("002","aeg","20");
        hashOperations.put("002","addr","beijing");

        //b:取值
        String age = (String)hashOperations.get("002", "age");
        System.out.println("age="+age);

        //c:获取hash结构中所有的字段(field)
        Set keys = hashOperations.keys("002");
        for (Object key : keys) {
            System.out.println(key);
        }

        //d:获取hash结构中所有的值value
        List values = hashOperations.values("002");
        for (Object value : values) {
            System.out.println(value);
        }

(3)Spring Data Redis 操作List列表数据


    /**
     * (3)操作List类型数据
     *  -->通过redisTemplate调用opsForList()方法获得listOperations对象,然后使用这个对象来通过leftPush等方法对List数据进行操作
     */

    @Test
    public void testList(){

        //获得listOperations对象
        ListOperations listOperations = redisTemplate.opsForList();

        //a:存数据(leftPus):根据插入的顺序进行排序,且可以有重复元素
        listOperations.leftPush("mylist1","a"); //存一个
        listOperations.leftPushAll("mylist1","b","c","d");//存多个

        //b:取值:range()
        List<String> mylist = listOperations.range("mylist1", 0, -1);
        for (String value : mylist) {
            System.out.println(value);
        }

        //c:获取列表长度:size()
        Long size = listOperations.size("mylist1");
        System.out.println("size="+size);

        //d:rightPop() key :移除并获取列表中的"最后一个元素"
        Object o = listOperations.rightPop("mylist1");
        System.out.println(o);
    }

(4)Spring Data Redis 操作Set无序集合数据

 /**
     * (4)操作Set类型数据:无序且不可重复
     * -->通过redisTemplate调用opsForSet()方法获得setOperations对象,然后使用这个对象来通过add()等方法对Set数据进行操作
     */

    @Test
    public void testSet(){
        //获取setOperations对象
        SetOperations setOperations = redisTemplate.opsForSet();

        //a:存值:add()
        setOperations.add("myset","a","b","c","a");

        //b:取值:members()
        Set<String>myset = setOperations.members("myset");
        for (String o : myset) {
            System.out.println(o);
        }

        //c:删除成员:remove()
        setOperations.remove("myset","a","b");

        System.out.println("111");
        //取值:members()
        myset = setOperations.members("myset");
        for (String o : myset) {
            System.out.println(o);
        }

(5)Spring Data Redis 操作ZSet有序集合数据


    /**
     * (5)操作ZSet有序集合:有序(是根据分数从小到大进行排序)且不可重复
     * -->通过redisTemplate调用opsForZSet()方法获得zSetOperations对象,然后使用这个对象来通过add()等方法对ZSet数据进行操作
     */

    @Test
    public void testZSet(){
        //获取zSetOperations对象
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();

        //a:存值:add() key-value-source(double类型)
        zSetOperations.add("myZset","a",10.0);
        zSetOperations.add("myZset","b",11.0);
        zSetOperations.add("myZset","c",12.0);
        zSetOperations.add("myZset","a",13.0); //会覆盖原来a的值,所以a=13

        //b:取值:range()
        Set<String> myZset = zSetOperations.range("myZset", 0, -1);
        for (String s : myZset) {
            System.out.println("s="+s);
        }
        
        ------------------->: b、c、a

        //c:修改分数:incrementScore():为myZset成员的b元素,增加20分
        zSetOperations.incrementScore("myZset","b",20.0);

        //取值:range()
        myZset = zSetOperations.range("myZset", 0, -1);
        for (String s : myZset) {
            System.out.println("修改完分数之后的排序s1="+s);
        }
        
         ------------------->: c、a、b


        //d:删除成员:remove()
        zSetOperations.remove("myZset","a","b");

        //取值:range()
        myZset = zSetOperations.range("myZset", 0, -1);
        for (String s : myZset) {
            System.out.println("删除a,b元素之后的排序s2="+s);
        }
        
         ------------------->: c

    }

(6)通用操作:针对不同的数据类型都可以操作

 /**
     * (6)通用操作
     *
     */
    @Test
    public void testCommon(){

        //a:获取Redis中所有的key -->keys("*")
        Set keys = redisTemplate.keys("*");
        for (Object key : keys) {
            System.out.println("key="+key);
        }

        //b:判断某个key是否存在 -->hasKey("key名称")
        Boolean b = redisTemplate.hasKey("itcast");
        System.out.println(b);

        //c:删除指定key -->delete("key名称")
        redisTemplate.delete("myZset");

        //d:获取指定key对应的value的数据类型 --type("key名称")
        DataType dataType = redisTemplate.type("mySet");
        System.out.println("Myset的数据类型是:"+dataType);
    }