Redis基础知识
1. redis操作指令不区分大小写,但是key和value区分大小写
2. list、set、hash、zset,当不存在这个key时,add会自动创建列表。当删除最后一个元素的时候会自动删除key,回收内存
3. 当一个String 字符串设置了过期时间,再次set的时候,会把过期时间去掉
// 查看一个key是什么数据结构
127.0.0.1:6379> object encoding setkey1
"hashtable"
// 判断key是否存在
127.0.0.1:6379> exists stringkey
(integer) 1
// 删除字符串
127.0.0.1:6379> del stringkey
(integer) 1
// 设置5秒后过期
127.0.0.1:6379> expire stringkey1 5
(integer) 1
// 查询剩余时常,秒
127.0.0.1:6379> ttl key1
(integer) 2
Redis的数据类型
1. string 字符串
2. list 有序列表
3. set 无序集合
4. zset 有序集合
5. hash 哈希字典
6. GeoHash 地图哈希字典
7. HyperLogLog 统计概率结构
8. PubSub 老版消息多播消息队列(已弃用)
9. Stream 新版消息队列
10. BitMap 位图,底层是string类型。其实就是bit数组
String 字符串
数据结构
1. 本身是一个字节数组,每个字节由8个bit组成,可以看成是由bit组成的数组。类似于JAVA的ArrayList,每次分配的内存会比实际的多一些,来避免内存的频繁分配
知识点
1. 当字符串长度小于1M时,扩容是直接X2。当超过1M时,每次扩容1M。最大程度是512M,因为长度是int类型最大是2的32次方
操作命令:
// 设置字符串
127.0.0.1:6379> set stringkey v1
OK
// 获取字符串
127.0.0.1:6379> get stringkey
"v1"
// 批量设置字符串
127.0.0.1:6379> mset stringkey1 v1 stringkey2 v2 stringkey3 v3
OK
// 批量获取字符串
127.0.0.1:6379> mget stringkey1 stringkey2 stringkey3
1) "v1"
2) "v2"
3) "v3"
// set并且设置过期为5秒
127.0.0.1:6379> setex stringkey1 5 v1
OK
// 如果stringkey1不存在则设置,存在则不执行
127.0.0.1:6379> setnx stringkey1 v1
(integer) 1
// 自增加1,value只能是整数型的才可以,边界值是signed long的最大最小值
127.0.0.1:6379> setnx stringkey4 1
(integer) 1
127.0.0.1:6379> setnx stringkey4 1
(integer) 1
// 指定自增加几
127.0.0.1:6379> setnx stringkey4 1
(integer) 1
List 列表
数据结构
1. 列表元素比较少的时候,使用ziplist - 压缩列表,他是由一大块连续的内存存储。
2. 当元素比较多的时候,会使用quicklist - 快速列表存储,他是由链表和ziplist - 压缩列表组成。每个链表里有向前prev和向后next指针。这样好处是可以快速插入也不会有太多空间冗余
知识点
1. 类似于JAVA的LinkedList链表,他插入和删除操作会很快,但是查询时定位索引会很慢。
2. 当列表弹出最后一个元素后,这个key会自动删除,内存回收
使用场景
1. 异步消息队列。但是存在各种问题
1. 文章列表,或者分页数据
操作命令:
// 从后面添加列表元素
127.0.0.1:6379> rpush listkey1 v1 v2 v3
(integer) 3
// 从前面添加列表元素
127.0.0.1:6379> rpush listkey1 v1 v2 v3
(integer) 3
// 获取列表长度
127.0.0.1:6379> llen listkey1
(integer) 4
// 从前面弹出一个元素
127.0.0.1:6379> lpop listkey1
"v0"
// 从右面弹出一个元素
127.0.0.1:6379> rpop listkey1
"v3"
// 获取listkey1坐标0位的值
127.0.0.1:6379> lindex listkey1 0
"v1"
// -1:获取listkey1的所有的值
127.0.0.1:6379> lrange listkey1 0 -1
1) "v1"
2) "v2"
// 保留坐标1 以后的值,其他的删掉
127.0.0.1:6379> ltrim listkey1 1 -1
OK
127.0.0.1:6379> lrange listkey1 0 -1
1) "v2"
// 删除整个listkey1
127.0.0.1:6379> ltrim listkey1 1 0
OK
Hash 哈希
数据结构
1. 类似于JAVA的HashMap,属于无序字典。由数组 + 链表组成。但是redis里hash的key只能是字符串
知识点
1. 哈希扩容时,为了防止rehash时间太长,避免堵塞,采用渐进式rehash。他会在扩容的时候同时保留俩个hash结构,这个过程中查询的时候会同时查询俩个hash结构,然后新增的值会直接到新的hash结构中,然后会逐渐把老的hash内容迁移到新的hash结构中
2. 最大可存储2的32次方-1 个,大约为40亿
使用场景
1. 商品购物车
2. 使用String 存json还是使用Hash?当对象的属性频繁修改的时候,就用hash存储,可以只改其中的一个属性。
操作命令:
// 添加Map子元素
127.0.0.1:6379> hset hashkey1 key1 "v1 and v1.1"
(integer) 1
// 获取这个key下所有 键值对
127.0.0.1:6379> hgetall hashkey1
1) "key1"
2) "v1 and v1.1"
3) "key2"
4) "v2"
5) "key3"
6) "v3"
// 查询元素个数
127.0.0.1:6379> hlen hashkey1
(integer) 3
// 获取指定的key下的value
127.0.0.1:6379> hget hashkey1 key1
"v1 and v1.1"
// 已经存在的键key1,会进行更新操作。返回的是0
127.0.0.1:6379> hset hashkey1 key1 v1
(integer) 0
127.0.0.1:6379> hget hashkey1 key1
"v1"
// 批量设置键值对
127.0.0.1:6379> hmset hashkey1 key4 "v4" key5 v5 key6 v6
OK
// 批量获取键值对
127.0.0.1:6379> hmget hashkey1 key4 key5
1) "v4"
2) "v5"
// 如果键值对里的值存的是整数,可以进行自增操作
127.0.0.1:6379> hset hashkey1 v7 3
(integer) 1
127.0.0.1:6379> hincrby hashkey1 v7 4
(integer) 7
Set 集合
数据结构
1. 当他元素个数比较少且都是整数时,会使用intset - 紧凑整数。如果都是 16位的无符号整型的话,intset就是单个长度为16位的数组;如果都是 32位的无符号整型的话,intset就是单个长度为32位的数组;如果都是 64位的无符号整型的话,intset就是单个长度为64位的数组。当value出现字符串,或者元素个数超过512个会变成
知识点
1. 类似于JAVA的HashSet, 键值对是无序的,但是去重唯一(不存在的话返回1,已经存在的话会返回0)。字典里的value都是null
2. 当集合中的最后一个元素被移除,会自动删除key, 内存自动回收
使用场景
1. 共同好友,推荐相同好友
2. 唯一性数据,比如访问博客的IP地址列表
操作命令:
// 添加元素
127.0.0.1:6379> sadd setkey1 v1 v2 v3
(integer) 3
// 查询所有元素
127.0.0.1:6379> smembers setkey1
1) "v3"
2) "v2"
3) "v1"
// 查询这个元素是否在set中存在
127.0.0.1:6379> sismember setkey1 v1
(integer) 1
// 查询元素个数
127.0.0.1:6379> scard setkey1
(integer) 3
// 随机弹出俩个元素
127.0.0.1:6379> spop setkey1 2
1) "v1"
2) "v2"
// 随机删除一个元素
127.0.0.1:6379> spop setkey1
"v3"
// 删除v2
127.0.0.1:6379> srem setkey1 v2
(integer) 1
// 俩个set相同数据
127.0.0.1:6379> sadd setkey1 v1 v2 v3
(integer) 3
127.0.0.1:6379> sadd setkey2 v2
(integer) 1
127.0.0.1:6379> sinter setkey1 setkey2
1) "v2"
Zset 有序集合
数据结构
1. zset 的内部实现是一个 hash 字典加一个skiplist - 跳跃列表。也就是数组 + 链表 + 跳跃列表.一个元素他可以同时存在于多层,最多有64 层,最 多可以容纳 2^64 次方个元素。如图的同时存在L0,L1,L2层。然后可以快速在不同层次之间进行跳跃
跳跃列表采取一个随机策略来决定新元素可以兼职到第几层。 首先 L0 层肯定是 100% 了, L1 层只有 50% 的概率, L2 层只有 25% 的概率, L3 层只有 12.5% 的概率,一直随机到最顶层。列表中元素越多,越能深入层次
2. zset 的跳跃列表查找指定元素的时候,会循环一层层的寻找,先从最高层依次往下寻找
知识点
1. 相当于JAVA的SortSet和HashMap结合体,他保证了Set内部的value唯一性,同时给每个value设置了一个score,当做排序权重
2. 当集合中的最后一个元素被移除,会自动删除key, 内存自动回收
3. 当使用zadd更新已存在的value时,会先把原来value删掉,再重新把value和新的score插入
4. 根据score排序时,当score值相同时。会按照value进行字符串排序
5. zrank怎么查出元素的排名?
redis在skiplist - 跳表的每个forward指针添加了span属性,用于记录跨度。在插入和删除操作的时候都会更新span值的大小。zrank执行时会把经过的节点的span值叠加算出最终排行
使用场景
1. 简单限流,score存的是时间戳,每次按score删除当前时间 到 (当前时间-60秒)外的数据,再统计总个数,就能得到当前60秒存储的量
2. 粉丝关注的列表,score存的是时间戳,可以按时间顺序列出粉丝
3. 排行榜相关的列表,微信步数排行等
操作命令:
// 添加元素
127.0.0.1:6379> zadd zsetkey1 1.1 v1
(integer) 1
// 按score正序查询所有元素
127.0.0.1:6379> zrange zsetkey1 0 -1
1) "v1"
2) "v2"
3) "v3"
// 按score逆序查询所有元素
127.0.0.1:6379> zrevrange zsetkey1 0 -1
1) "v3"
2) "v2"
3) "v1"
// 查询元素个数
127.0.0.1:6379> zcard zsetkey1
(integer) 3
// 根据元素查询他的score权重,内部 score 使用 double 类型进行存储,所以存在小数点精度问题
127.0.0.1:6379> zscore zsetkey1 v1
"1.1000000000000001"
// 查询元素排在第几个序号
127.0.0.1:6379> zrank zsetkey1 v3
(integer) 2
// 根据区间筛选1.0 到 1.2之间的元素
127.0.0.1:6379> zrangebyscore zsetkey1 1.0 1.2
1) "v1"
2) "v2"
// -inf代表无穷,withscores代表返回权重分数
127.0.0.1:6379> zrangebyscore zsetkey1 -inf 1.1 withscores
1) "v1"
2) "1.1000000000000001"
// 删除元素v2
127.0.0.1:6379> zrem zsetkey1 v2
(integer) 1
BitMap 位图
数据结构
1. 位图其实就是字符串,就是byte数组,一个byte等于8bit
知识点
1. 位数组是自动扩展,如果设置了某个偏移位置超出了现有的内容范围,就会自 动将位数组进行零扩充。零扩充 就是 将目标最高位设置成零
2. 每一个byte,每8位bit存的就是ASCII码的二进制值
应用场景
1. 可以按照用户维度记录用户签到,key是"年度:用户ID"。可以实现布隆过滤器
操作命令:
// 设置bitMap第1位为1
127.0.0.1:6379> setbit bitmapkey1 1 1
(integer) 0
// 获取bitMap第一位的值
127.0.0.1:6379> getbit bitmapkey1 1
(integer) 1
// 按byte位获取,相当于获取ASCII转字符串,当ASCII码不可打印时,输出该字符的16进制形式
127.0.0.1:6379> get bitmapkey1
"`"
// 之间用字符串存储,会将字符串转成ASCII码进行存储
127.0.0.1:6379> set bitmapkey1 hh
OK
// 查询所有的1个数
127.0.0.1:6379> bitcount bitmapkey1
(integer) 6
// 查询坐标0到坐标2之间1的个数
127.0.0.1:6379> bitcount bitmapkey1 0 2
(integer) 6
// 查询第一个 1 时的坐标
127.0.0.1:6379> bitpos bitmapkey1 1
(integer) 1
// 从第0位开始取 4 个位,结果是无符号数 (u)
127.0.0.1:6379> bitfield bitmapkey1 get u4 0
1) (integer) 6
// 所谓有符号数是指获取的位数组中第一个位是符号位,剩下的才是值。如果第一位是
1,那就是负数。无符号数表示非负数,没有符号位,获取的位数组全部都是值。有符号数最
多可以获取 64 位,无符号数只能获取 63 位
// 从第0位开始取 4 个位,结果是有符号数 (i)
127.0.0.1:6379> bitfield bitmapkey1 get i4 0
1) (integer) 6
127.0.0.1:6379> bitfield bitmapkey1 get u4 0 get u3 2 get i4 0
1) (integer) 6
2) (integer) 5
3) (integer) 6
// 从第 8 个位开始,将接下来的 8 个位用无符号数 97 替换
127.0.0.1:6379> bitfield bitmapkey1 set u8 8 97
1) (integer) 104
// 从第2位开始,对接下来的 4 位无符号数 +1
127.0.0.1:6379> bitfield bitmapkey1 incrby u4 2 1
1) (integer) 11
// 溢出的时候指定饱和截断(sat),超出范围就一直是最大值。 默认的模式是折现,还可以选择fail报错
127.0.0.1:6379> bitfield bitmapkey1 overflow sat incrby u4 2 1
1) (integer) 12
HyperLogLog 基数统计
数据结构
知识点
1. 用于统计总数,标准误差是 0.81%
2. 只能统计总数,不能获取里面的详细元素
3. 他需要占用12k的内存空间,所以不适合统计数量比较少的场景
4. 为什么占用的是12k?
因为HyperLogLog再使用的适合使用了2的14次方既16384个桶,每个桶需要6bit存储,于是 总占用内存为 2的14次方 * 6/8=12k字节
应用场景
1. 统计网站PV, UV等不需要特别精准的数据总数
操作命令:
// 添加元素
127.0.0.1:6379> pfadd hyperloglogkey1 user1
(integer) 1
// 获取元素总数
127.0.0.1:6379> pfcount hyperloglogkey1
(integer) 3
// 把hyperloglogkey2 合并到hyperloglogkey1上
127.0.0.1:6379> pfmerge hyperloglogkey1 hyperloglogkey2
OK
BitMap 位图
数据结构
1. 位图其实就是字符串,就是byte数组,一个byte等于8bit
知识点
1. 位数组是自动扩展,如果设置了某个偏移位置超出了现有的内容范围,就会自 动将位数组进行零扩充。零扩充 就是 将目标最高位设置成零
2. 每一个byte,每8位bit存的就是ASCII码的二进制值
应用场景
1. 可以按照用户维度记录用户签到,key是"年度:用户ID"。可以实现布隆过滤器
操作命令:
// 设置bitMap第1位为1
127.0.0.1:6379> setbit bitmapkey1 1 1
(integer) 0
// 获取bitMap第一位的值
127.0.0.1:6379> getbit bitmapkey1 1
(integer) 1
// 按byte位获取,相当于获取ASCII转字符串,当ASCII码不可打印时,输出该字符的16进制形式
127.0.0.1:6379> get bitmapkey1
"`"
// 之间用字符串存储,会将字符串转成ASCII码进行存储
127.0.0.1:6379> set bitmapkey1 hh
OK
// 查询所有的1个数
127.0.0.1:6379> bitcount bitmapkey1
(integer) 6
// 查询坐标0到坐标2之间1的个数
127.0.0.1:6379> bitcount bitmapkey1 0 2
(integer) 6
// 查询第一个 1 时的坐标
127.0.0.1:6379> bitpos bitmapkey1 1
(integer) 1
// 从第0位开始取 4 个位,结果是无符号数 (u)
127.0.0.1:6379> bitfield bitmapkey1 get u4 0
1) (integer) 6
// 所谓有符号数是指获取的位数组中第一个位是符号位,剩下的才是值。如果第一位是
1,那就是负数。无符号数表示非负数,没有符号位,获取的位数组全部都是值。有符号数最
多可以获取 64 位,无符号数只能获取 63 位
// 从第0位开始取 4 个位,结果是有符号数 (i)
127.0.0.1:6379> bitfield bitmapkey1 get i4 0
1) (integer) 6
127.0.0.1:6379> bitfield bitmapkey1 get u4 0 get u3 2 get i4 0
1) (integer) 6
2) (integer) 5
3) (integer) 6
// 从第 8 个位开始,将接下来的 8 个位用无符号数 97 替换
127.0.0.1:6379> bitfield bitmapkey1 set u8 8 97
1) (integer) 104
// 从第2位开始,对接下来的 4 位无符号数 +1
127.0.0.1:6379> bitfield bitmapkey1 incrby u4 2 1
1) (integer) 11
// 溢出的时候指定饱和截断(sat),超出范围就一直是最大值。 默认的模式是折现,还可以选择fail报错
127.0.0.1:6379> bitfield bitmapkey1 overflow sat incrby u4 2 1
1) (integer) 12
GeoHash 地理存储
数据结构
1. 地图上,横着的线是精度,竖着的线是维度,GeoHash算法把经纬度二位数组转换成了一维整数,相当于每个地点是在一条线上,距离近的俩个地点 越相邻
2. 底层使用zset存储,既使用skiplist - 跳表 存储,value是元素的key,socre是经纬度经过GeoHash算法转换的52位整数
知识点
1. GeoHash 算法会继续对这个整数做一次 base32 编码 (0-9,a-z 去掉 a,i,l,o 四个字母) 变成一个字符串。经纬度使用 52 位的整数进行编码
2. Redis 没有提供 geo 删除指令
如果要删除元素,需要使用zset的删除方法例如:
进行删除geo 。RedisTemplate.opsForZset.remove(b,String.valueof(ccc)),其中c 就是memberCoordinateMap的key。 需要遍历删除。即可把获赠个有序集合删除
3. 经纬度转换成一维整数,再重新转换成二维经纬度的时候会有轻微精度损失
4. 因为单个key数据会很大,在集群环境的时候迁移过程中会产生卡顿的原因(单个key超过1M就会出现迁移卡顿),Geo建议使用单独的单节点redis服务器部署,也可以按照国家,省份进行拆分成多个geo key
应用场景
1. 查询附件的人,查询附近几百米范围的饭店等
操作命令:
// 添加元素,116.48105 39.996794是经纬度
127.0.0.1:6379> geoadd geohashkey1 116.48105 39.996794 juejin
(integer) 1
// 查询俩个地点之间距离,距离单位可以是 m、 km、 ml、 ft,
//分别代表米、千米、英里和尺
127.0.0.1:6379> geodist geohashkey1 juejin ireader km
"10.5501"
// 获取元素的经纬度,这时候会有轻微精度损失
127.0.0.1:6379> geopos geohashkey1 juejin
1) 1) "116.4810499548912"
2) "39.996793488582597"
// geohash 经过base32编码的code,可以去http://geohash.org/wx4g52e1ce0 查询地址
127.0.0.1:6379> geohash geohashkey1 ireader
1) "wx4g52e1ce0"
// 以ireader周边20km为范围,搜索3个正序,withdist 显示距离,withcoord显示经纬度
// 会包含自己需要去掉自己
127.0.0.1:6379> georadiusbymember geohashkey1 ireader 20 km withcoord withdist withhash count 3 asc
1) 1) "ireader"
2) "0.0000"
3) (integer) 4069886008361398
4) 1) "116.51420205831528"
2) "39.905409186624944"
2) 1) "juejin"
2) "10.5501"
3) (integer) 4069887154388167
4) 1) "116.4810499548912"
2) "39.996793488582597"
3) 1) "meituan"
2) "11.5748"
3) (integer) 4069887179083478
4) 1) "116.48903220891953"
2) "40.00766997707732"
// 根据经纬度查询周边20km的3个
127.0.0.1:6379> georadius geohashkey1 116.514202 39.905409 20 km withdist count 3 asc
1) 1) "ireader"
2) "0.0000"
2) 1) "juejin"
2) "10.5501"
3) 1) "meituan"
2) "11.5748"
PubSub 老版消息队列
数据结构
1. 底层是链表结构,消息链表。
知识点
1. 为了支持消息多播产生的。消息多播是指允许生产者生产一次消息,中间件负责将消息复制到多个消息队列,每个消息队列由相应的消费组进行消费。
2. Redis PubSub的生产者和消费者是不同的连接,也就是上面这个例子实际上使用了两个Redis 的连接。因为 Redis 不允许连接在 subscribe 等待消息时还要进行其它 的操作。
3. 使用 blpop 来代替休眠来提高消息处理的及时性
4. 消息结构:
{'pattern': None, 'type': 'subscribe', 'channel': 'codehole', 'data': 1L}
{'pattern': None, 'type': 'message', 'channel': 'codehole', 'data': 'python comes'}
{'pattern': None, 'type': 'message', 'channel': 'codehole', 'data': 'java comes'}
{'pattern': None, 'type': 'message', 'channel': 'codehole', 'data': 'golang comes'}
data 这个毫无疑问就是消息的内容,一个字符串。
channel 这个也很明显,它表示当前订阅的主题名称。
type 它表示消息的类型,如果是一个普通的消息,那么类型就是 message,如果是控制 消息,比如订阅指令的反馈,它的类型就是 subscribe,如果是模式订阅的反馈,它的类型就 是 psubscribe,还有取消订阅指令的反馈 unsubscribe 和 punsubscribe。
pattern 它表示当前消息是使用哪种模式订阅到的,如果是通过 subscribe 指令订阅的, 那么这个字段就是空。
应用场景
1. 已被废弃,因为不能持久化,重启后有可能会到账消息丢失,后面被Stream类型取代
操作命令:
// 订阅消息
127.0.0.1:6379> subscribe chanel1 chanle2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "chanel1"
3) (integer) 1
1) "subscribe"
2) "chanle2"
3) (integer) 2
1) "subscribe"
2) "chanel*"
3) (integer) 3
// 发布消息
127.0.0.1:6379> publish channel1 "abcderfg"
(integer) 0
// 模糊匹配订阅多个主题
127.0.0.1:6379> psubscribe channel*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "channel*"
3) (integer) 1
Stream 新版消息队列
数据结构
1. 底层是链表结构,消息链表。
知识点
1. 支持多播的可持久化的消息队列。把加入的消息按顺序串联起来,每个消息都有唯一的ID对应。
2. 消息是持久化的,redis重启后,消息还在
3. 每个 Stream 都可以挂多个消费组,每个消费组会有个游标 last_delivered_id 在 Stream数组之上往前移动,表示当前消费组已经消费到哪条消息了。
4. 每个消费组 (Consumer Group)的状态都是独立的,相互不受影响。也就是说同一份Stream内部的消息会被每个消费组都消费到。
同一个消费组 (Consumer Group) 可以挂接多个消费者 (Consumer),这些消费者之间是 竞争关系,任意一个消费者读取了消息都会使游标last_delivered_id往前移动。每个消费者有一个组内唯一名称
消费者 (Consumer) 内部会有个(PEL)状态变量pending_ids,它记录了当前已经被客户端读取的消息,但是还没有 ack。一旦某个消息被ack,它就开始减少。它用来确保客户端至少消费了消息一次,而不会在网络传输的中途丢失了没处理
5. 他的消息ID是时间戳 - 序号,例如 1527846880572-5。后面的消息ID肯定要比前面的大
6. 客户端如果想要使用 xread进行顺序消费,一定要记住当前消费到哪里了,也就是返回的消息 ID。下次继续调用 xread 时,将上次返回的最后一个消息 ID 作为参数传递进去,就可以继续消费后续的消息。
7. 当前没有新消息的时候。可以使用block 参数进行阻塞等待
应用场景
1. redis消息队列,但是不建议使用
操作命令:
// * 号表示服务器自动生成 ID,后面顺序跟着一堆 key/value
127.0.0.1:6379> xadd codehole * name laoqian age 30
1527849609889-0
// 获取消息长度
127.0.0.1:6379> xlen codehole
(integer) 3
// 获取消息
127.0.0.1:6379> xrange codehole - +
1) 1) 1527849609889-0
2) 1) "name"
2) "laoqian"
3) "age"
4) "30"
2) 1) 1527849629172-0
2) 1) "name"
2) "xiaoyu"
3) "age"
4) "29"
3) 1) 1527849637634-0
2) 1) "name"
2) "xiaoqian"
3) "age"
4) "1"
// 指定最大消息 ID为1527849629172-0 的列表
127.0.0.1:6379> xrange codehole - 1527849629172-0
1) 1) 1527849609889-0
2) 1) "name"
2) "laoqian"
3) "age"
4) "30"
2) 1) 1527849629172-0
2) 1) "name"
2) "xiaoyu"
3) "age"
4) "29"
// 根据ID删除消息
127.0.0.1:6379> xdel codehole 1527849609889-0
(integer) 1
// 按顺序读取俩条消息
127.0.0.1:6379> xread count 2 streams codehole 0-0
1) 1) "codehole"
2) 1) 1) 1527851486781-0
2) 1) "name"
2) "laoqian"
3) "age"
4) "30"
2) 1) 1527851493405-0
2) 1) "name"
2) "yurui"
3) "age"
4) "29"
// 从尾部阻塞等待新消息到来,下面的指令会堵住,直到新消息到来,$代表尾部
127.0.0.1:6379> xread block 0 count 1 streams codehole $
// 表示从头开始消费
127.0.0.1:6379> xgroup create codehole cg1 0-0
OK
// $ 表示从尾部开始消费,只接受新消息,当前 Stream 消息会全部忽略
127.0.0.1:6379> xgroup create codehole cg2 $
OK
// 获取消息的相关信息
127.0.0.1:6379> xinfo stream codehole
1) length
2) (integer) 3 # 共 3 个消息
3) radix-tree-keys
4) (integer) 1
5) radix-tree-nodes
6) (integer) 2
7) groups
8) (integer) 2 # 两个消费组
9) first-entry # 第一个消息
10) 1) 1527851486781-0
2) 1) "name"
2) "laoqian"
3) "age"
4) "30"
11) last-entry # 最后一个消息
12) 1) 1527851498956-0
2) 1) "name"
2) "xiaoqian"
3) "age"
4) "1"
//获取 Stream 的消费组信息
127.0.0.1:6379> xinfo groups codehole
1) 1) name
2) "cg1"
3) consumers
4) (integer) 0 # 该消费组还没有消费者
5) pending
6) (integer) 0 # 该消费组没有正在处理的消息
2) 1) name
2) "cg2"
3) consumers # 该消费组还没有消费者
4) (integer) 0
5) pending
6) (integer) 0 # 该消费组没有正在处理的消息
// ack 一条消息
// 客户端处理完毕后使用 xack指令通知服务器,本条消息已经处理完毕,该消息 ID 就会从 PEL 中移除
127.0.0.1:6379> xack codehole cg1 1527851486781-0
(integer) 1
127.0.0.1:6379> xinfo consumers codehole cg1
1) 1) name
2) "c1"
3) pending
4) (integer) 4 # 变成了 5 条
5) idle
6) (integer) 668504