目录

  • Redis的简单使用补充说明
  • 可视化操作
  • 转码
  • 数据类型
  • String类型
  • 常用操作
  • Hash类型
  • 常用操作
  • List类型
  • 常用操作
  • Set类型
  • 常用操作
  • SortedSet类型
  • 常用操作
  • Java客户端
  • Jedis
  • 简单demo
  • JedisPool
  • SpringDataRedis
  • SpringBoot
  • SpringDataRedis的序列化方式
  • Spring配置
  • Redisson
  • 缓存穿透
  • 缓存雪崩
  • 缓存击穿
  • 全局唯一ID
  • Lua
  • setnx的问题
  • Redisson
  • 消息队列
  • 基于List消息队列
  • 基于PubSub消息队列
  • 基于Stream消息队列
  • 消费者组


Redis的简单使用补充说明

可视化操作

https://github.com/lework/RedisDesktopManager-Windows/releases

下载安装就行

转码

redis-cli --raw

可以将UTF-8编码字符转换成具体的内容

数据类型

String类型

String类型,也就是字符串类型,redis中最简单的类型。
value是字符串。但是会根据字符串的格式不同,可以将分为3类:

  • String:普通字符串
  • int:整数类型,可以自增、自减操作
  • float:浮点类型,可以自增、自减操作
常用操作
  • set:添加或修改
  • get:根据key获取
  • mset:批量添加/修改多个
  • mget:获取多个
  • incr:整型key自增1
  • incrby:整型自增指定步长->incrby age 2
  • incrbyfloat:让一个浮点类型自增并指定步长
  • setnx:当且仅当key不存在的时候添加
  • setex:添加并指定有效期(秒)

Hash类型

Hash类型,也叫散列,value是一个无序字典,类似Java的HashMap;
String结构是将对象序列化为Json字符串,当需要修改对象某个字段时不方便,Hash结构可以将每个对象中的每个字段独立存储,可以针对单个字段做CRUD;

常用操作
  • hset key field value:添加或修改key的field的值
  • hget key field:获取一个key的field的值
  • hmset:批量添加/修改key的多个field的值
  • hmget:批量获取key的多个field的值
  • hgetall:获取一个key的所有field和value
  • hkeys:获取key的所有field
  • hvals:获取key的所有value
  • hincrbu:让key的field自增并指定步长
  • hsetnx:当且仅当key的field值不存在才添加

List类型

Redis中的L类型与Java中的LinkedList类似,可以看作是一个双向链表结构。支持正向/反向检索;
特征也与LinkedLi类似:

  • 有序
  • 元素可以重复
  • 插入和删除快,查询速度一般
常用操作
  • lpush/rpush key element…:向列表左/右侧插入一个或多个元素
  • lpop/rpop key:移除并返回链表左/右侧第一个元素,没有返回nil
  • lrange key start end:返回一段范围内的所有元素
  • blpop/brpop:与lpop/rpop类似,在没有元素时等待指定时间,不会直接返回nil

Set类型

Redis的Set结构与Java中的HashSet类似,可以看作是 一个value为null的HashMap。因为也是一个hash表,因此具备与HashSet类似的特征:

  • 无序
  • 元素不可重复
  • 查找快
  • 支持交集、并集、差集等功能
常用操作
  • sadd key member…:向set中添加一个或多个元素
  • srem key member…:移除set中指定元素
  • scard key:返回set中元素的个数
  • sismember key member:判断一个元素是否在set中
  • smembers:获取set中所有元素
  • sinter key1 key2…:求key1与key2的交集
  • sdiff key1 key2…:求key1与key2的差集,前有后没有的
  • sunion key1 key2…:求key1和key2的并集

SortedSet类型

Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet类似,但底层数据结构缺差别很大,SortedSet的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加Hash表。SortedSet具备下列特性:

  • 可排序
  • 元素不重复
  • 查询速度快

因为SortedSet的可排序特性,经常用来实现排行榜这样的功能;

常用操作
  • zadd key score member:添加一个或多个元素,如果已经存在则更新score值
  • zrem key member:删除一个指定元素
  • zscore key member:获取指定元素的score值
  • zrank key member:获取指定元素的排名
  • zcard key:获取元素个数
  • zcount key min max:统计score值在给定范围内的所有元素的个数
  • zincrby key increment member:让指定元素自增指定步长
  • zrange key min max:按照score排序后,获取指定排名范围内的元素
  • zrangebyscore key min max:按照score排序后,获取指定score范围内的元素
  • zdiff、zinter、zunion:求差集、交集、并集

排名有关的可以反序,zrevxxx

Java客户端

各个常用客户端比较:

  • Jedis:以redis命令作为方法名称,学习成本低,简单实用。但是Jedis实例是线程不安全的,多线程环境下需要基于连接池来使用;
  • Lettuce:Lettuce是基于Netty实现的,支持同步、异步和响应式编程方式,而且是线程安全的。支持Redis的哨兵模式、集群模式和管道模式;
  • Redisson:Redisson是一个基于Redis实现的分布时、可伸缩的Java数据结构集合。包含了诸如Map、Queue、Lock、Semaphore、AtomicLone等强大功能;

Jedis

简单demo

引入依赖

<dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

测试代码

package per.xgt.demo;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;

/**
 * @Author: gentao9527
 * @date: 2022/8/31 17:07
 * @Description: TODO
 * @Version: 1.0
 */
public class JedisTest {

    private Jedis jedis;

    @BeforeEach
    void set(){
        // 建立连接
        jedis = new Jedis("ip",port);
        // 密码或者可以输入账号密码
        jedis.auth("username","password");
        // 选择库
        jedis.select(0);
    }

    @Test
    public void test(){
        jedis.set("k1","v1");
        System.out.println(jedis.get("k1"));
    }

    @AfterEach
    public void afterTest(){
        if (null != jedis){
            jedis.close();
        }
    }

}

只用Jedis,方法名与Redis命令一致。
使用完后,需要释放资源。

JedisPool

Jedis本身是线程不安全的,并且频繁地创建和销毁连接会有性能损耗,因此推荐使用Jedis连接池代替Jedis的直连方式;
测试代码:

package per.xgt.demo;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @Author: gentao9527
 * @date: 2022/8/31 17:26
 * @Description: TODO
 * @Version: 1.0
 */
public class JedisPoolTest {
    private static JedisPool jedispool;
    static {
        // 配置连接处
        JedisPoolConfig config = new JedisPoolConfig();
        // 最大连接数
        config.setMaxTotal(20);
        // 最大空闲连接
        config.setMaxIdle(5);
        // 最小空闲连接
        config.setMinIdle(5);
        // 连接等待,没有连接资源时,等待时间,-1表示没有限制,单位毫秒
        config.setMaxWaitMillis(1000);

        // 创建连接处对象
        jedispool = new JedisPool(config,"ip",port,2000,"username","password");
    }
    public static Jedis getJedis(){
        return jedispool.getResource();
    }
    public static void main(String[] args) {
        Jedis jedis = getJedis();
        System.out.println(jedis.ping());
    }
}

SpringDataRedis

SpringData是Spring中数据操作的模块,包含堆各种数据库的集成,其中Redis的集成模块叫做SpringDataRedis;

  • 提供了对不同Redis客户端的整合(Lettuce和Jedis)
  • 提供了RedisTemplate统一API来操作Redis
  • 支持Redis的发布订阅模式
  • 支持Redis的哨兵和Redis集群
  • 支持基于Lettuce的响应式编程
  • 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
  • 支持基于Redis的JDKCollection实现

SpringDataRedis中提供了RedisTemplaye工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作封装到了不同的类型中:

  • redisTemplate.opsForValue():操作String
  • redisTemplate.opsForHash():操作hash
  • redisTemplate.opsForList():操作List
  • redisTemplate.opsForSet():操作Set
  • redisTemplate.opsForZSet():操作SortedSet
  • redisTemplate:通用的命令

SpringBoot

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--jackson依赖,如果是有mvc就不需要-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

配置文件

spring:
  redis:
    host: ip
    port: 6379
    password: password
    database: 0
    username: username
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 1
        max-wait: 1000ms

测试代码

package per.xgt;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class Redis05SpringDataTemplateApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("k1","v1");

        System.out.println(redisTemplate.opsForValue().get("k1"));
    }
}
SpringDataRedis的序列化方式

RedisTemplaye可以接受任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化;
缺点:

  • 可读性差
  • 内存占用较大

自定义序列化,自定义RedisTemplate

package per.xgt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

/**
 * @Author: gentao9527
 * @date: 2022/8/31 20:10
 * @Description: TODO
 * @Version: 1.0
 */
@Configuration
public class RedisConfig {

    @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;
    }

}

@Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
        // 创建RedisTemplate对象
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();

        // 设置连接工厂
        redisTemplate.setConnectionFactory(connectionFactory);

        // 创建JSON序列化工具
        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        // json序列化设置
        ObjectMapper mapper = new ObjectMapper();
        // 哪些方法规则可以序列化:ALL表示所有方法 ANY表示任何访问修饰符
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jsonRedisSerializer.setObjectMapper(mapper);

        // 设置key的序列化
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());

        // 设置value的序列化
        redisTemplate.setValueSerializer(jsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);

        // 返回
        return redisTemplate;
    }

问题:
在存入对象时候,会有额外开销,将类的class类型写入json结果中,存入Redis;如果去掉,又不能自动实现反序列化

127.0.0.1:6379> get user:1
{"@class":"per.xgt.entity.User","name":"大哥","age":12}

解决:
存储Java对象时,手动完成对象的序列化和反序列化;
Spring默认提供了一个StringRedisTemplate类,key和value的序列化方式默认就是String方式。省去了自定义RedisTemplate的过程;

@Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void contextLoadsss() throws JsonProcessingException {
        // json序列化工具
        ObjectMapper mapper = new ObjectMapper();
        // 实体类
        User user = new User("大哥", 12);
        // json转换
        String userString = mapper.writeValueAsString(user);
        // 存入redis
        stringRedisTemplate.opsForValue().set("user:1",userString);
        // 取出数据
        String stringUser = stringRedisTemplate.opsForValue().get("user:1");
        User resultUser = mapper.readValue(stringUser, User.class);
        System.out.println(resultUser);
    }

Spring配置

<?xml version="1.0" encoding="UTF-8"?>
<beans    xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:p="http://www.springframework.org/schema/p"
          xmlns:tx="http://www.springframework.org/schema/tx"
          xmlns:context="http://www.springframework.org/schema/context"
          xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
    <context:property-placeholder location="classpath:*.properties"/>
    <!--设置数据池-->
    <bean id="poolConfig"  class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}"></property>
        <property name="minIdle" value="${redis.minIdle}"></property>
        <property name="maxTotal" value="${redis.maxTotal}"></property>
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}"></property>
        <property name="testOnBorrow" value="${redis.testOnBorrow}"></property>
    </bean>
    <!--链接redis-->
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}"></property>
        <property name="port" value="${redis.port}"></property>
        <property name="password" value="${redis.password}"></property>
        <property name="poolConfig" ref="poolConfig"></property>
    </bean>
 
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="connectionFactory" >
        <!--以下针对各种数据进行序列化方式的选择,可以自定义一个序列化工具-->
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
    </bean>
</beans>

Redisson

缓存穿透

客户端请求的数据在缓存中和数据库中都不存在,缓存永远不会击中,所以请求都会查询数据库;

常见解决方案:

  • 缓存空对象
    优点:实现简单,维护方便;
    缺点:额外的内存消耗,可能造成短期的不一致;
  • 布隆过滤
    优点:内存占用较少,没有多余key;
    缺点:实现复杂,存在误判可能;

缓存雪崩

同一时段大量的缓存key同时失效或Redis服务宕机,导致大量请求请求数据库;

常见解决方案:

  • 给不同的key的TTL添加随机值
  • 利用Redis集群提高服务的可用性
  • 给缓存业务添加降级限流策略
  • 给业务添加多级缓存

缓存击穿

热点key问题,一个被高并发访问并且缓存重建业务比较复杂的key突然失效,无数的请求访问会在瞬间给数据库带来巨大冲击;

常见解决方案:

  • 互斥锁
    优点:没有额外的没存消耗,保证一致性,实现简单;
    缺点:线程需要等待,性能受影响,可能有死锁的风险;
  • 逻辑过期
    优点:线程无需等待,性能较好;
    缺点:不保证一致性,有额外内存消耗,实现复杂;

全局唯一ID

为了增加ID的安全性,可以不直接使用redis自增的数值,而是拼接其他信息:
例:
ID = 符号位1+时间戳21+序列号32
符号位:1bit,永远为0;
时间戳:31bit,以秒为单位,可以使用很长时间;
序列号:32bit:秒内的计数器,支持每秒产生2^32个不同ID;

package per.xgt.jedis;

import redis.clients.jedis.Jedis;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

/**
 * @Author: gentao9527
 * @date: 2022/9/2 13:41
 * @Description: TODO
 * @Version: 1.0
 */
public class IDUtil {

    // 初始时间戳
    private static final Long BEGIN_TIMESTAMP;
    // Jedis
    private static final Jedis JEDIS;

    static {
        LocalDateTime time = LocalDateTime.of(2022, 1, 1, 0, 0, 0);
        // 时区
        long timeSecond = time.toEpochSecond(ZoneOffset.UTC);
        BEGIN_TIMESTAMP = timeSecond;

        Jedis jedis = new Jedis("ip",6379);
        jedis.auth("password");
        JEDIS = jedis;
    }

    public static long nextId(String keyPrefix){
        // 生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowTime = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowTime - BEGIN_TIMESTAMP;
        // 获取当前日期,精确到天
        String date = now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        // 生成序列号
        long count = JEDIS.incr("incr:" + keyPrefix + ":" + date);

        // 拼接ID

        return timestamp << 32 | count;
    }

    public static void main(String[] args) {
        long sssss = nextId("sssss");
        System.out.println(sssss);
    }

}

Lua

执行redis命令
redis.call(‘命令名称’,‘key’,‘其他参数’,…);

如果要执行脚本:

EVAL "return redis.call('set','haha','hehe')" 0

0:代表需要的key类型的参数个数

如果脚本中的key、value不想写死,可以作为参数传递,key类型参数会放入keys数组,其他参数会放在argv数组中。
注意: 在lua中数组下表从1开始

EVAL "return redis.call('set',KEYS[1],ARGV[1])" 1 haha hehe

redis中解决比对+删除原子性操作Lua脚本,例:

# 获取比对数据
local id = redis.call('get',KEYS[1])
# 比较数据是否一致
if(id == ARGV[1]) then
# 删除
    return redis.call('del',KEYS[1])
end
return 0

使用RedisTemplate调用Lua脚本

public class TestLua {

    private static final DefaultRedisScript<Long> TEST;

    static {
        TEST = new DefaultRedisScript<>();
        TEST.setLocation(new ClassPathResource("test.lua"));
        TEST.setResultType(Long.class);
    }

    public void eqAndDel(RedisTemplate redisTemplate,String key,String value){
        redisTemplate.execute(TEST, Collections.singletonList(key), value);
    }

}

setnx的问题

基于setnx实现的分布式锁存在以下问题:

  • 不可重入:同一个线程无法多次获取同一把锁;
  • 不可重试:获取锁只能尝试一次就返回false,没有重试机制;
  • 超时释放:锁超时释放虽然可以避免死锁,但如果业务执行耗时较长,也会导致锁释放,存在安全隐患;
  • 主从一致性:如果Redis提供了主从集群,主从同步存在延迟,当主宕机时,并没有将锁同步进从,从而让其他线程趁虚而入;

Redisson

依赖

<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.13.6</version>
</dependency>

配置类

@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redissonClient(){
        // 配置
        Config config = new Config();
        config.useSingleServer().setUsername("default").setPassword("XGTflh1314").setAddress("119.3.230.11");

        return Redisson.create(config);
    }

}

消息队列

基于List消息队列

list数据结构是一个双向链表,很容易模拟队列效果;
队列是入口和出口不在一边,因此可以利用:LPUSH和RPOP、或者RPUSH和LPOP来实现;
注意:当队列中没有消息时,RPOP或LPOP操作会返回null,并不会项JVM的阻塞队列那样会阻塞并等待消息,因此应该使用BRPOP或者BLPOP来实现阻塞效果;

优点:

  • 基于Redis的持久化机制,数据安全性有保证
  • 可以满足消息有序性

缺点:

  • 无法避免消息丢失
  • 只支持单消费者

基于PubSub消息队列

Redis2.0引入的消息传递模型。消费者可以订阅一个或多个channel,生产者向对应channel发送消息后,所有订阅者都能收到相关消息;

  • SUBSCRIBE CHANNEL:订阅一个或多个频道
  • PUBLISH CHANNEL MSG:向一个频道发送消息
  • PSUBSCRIBE PATTERN:订阅与pattern格式匹配的所有频道

优点:

  • 采用发布订阅模型,支持多生产、多消费

缺点:

  • 不支持数据持久化
  • 无法避免消息丢失
  • 消息堆积有上限,超出时数据丢失

基于Stream消息队列

Stream是Redis5.0引入的一种新数据类型,可以实现一个功能非常完善的消息队列;

  • xadd key [nomkstream] [maxlen|minid [=|~] threshold] [limit count]] * | id field value [field value…]:发送消息。
    nomkstream:如果队列不存在,是否自动创建队列,默认是自动创建;
    [maxlen|minid [=|~] threshold] [limit count]]:设置消息队列的最大消息数量;
    *| id:消息的唯一id,*代表由redis自动生成,格式是“时间戳-递增数字”;
    field value:发送到消息队列中的消息;
  • xread [count count] [block] streams key [key…] id [id…]:读取消息;
    [count count]:每次读取消息的最大数量;
    [block]:当没有消息时,是否阻塞,阻塞时长;
    streams key:要从哪个队列读取消息,key就是队列名;
    id [id…]:起始id,只返回大于该ID的消息,0表示从第一个消息开始,$表示从最新的消息开始;

read的问题:当我们指定起始为id为$时,有可能漏读消息;

消费者组

Consumer Group:将多个消费者分到一个组中,坚挺同一个队列;

  • 消息分流:队列中的消息会分流给组内的不同消费者,而不是重复消费,从而加快消息处理的速度;
  • 消息标示:消费者组会维护一个标示,记录最后一个被处理的消息,哪怕消费者宕机重启,还会从标示之后读取消息。确保每一个消息都会被消费;
  • 消息缺人:消费者获取消息后,消息出于pending的状态,并存入一个pending-list。当处理完成后需要通过xack来确定消息,标记消息为已处理,才会从pending-list移除;

操作:

  • xgroup create key groupname id [mkstream]:创建消费者组;
    id:起始id标示,$代表队列中队友一个,0代表队列中第一个;
    mkstream:队列不存在时自动创建队列;
  • xgroup destory key groupname:删除指定消费者组
  • xgroup create connsumer key groupname consumername:给指定的消费者组添加消费者
  • xgroup delconsumer key groupname consumername:删除消费者组中的指定消费者
  • xreadgroup GROUP group consumer[Count count] [Block] [Noack] streams key… id…:从消费者组读取消息;
    group:组名;
    consumer:消费者名,如果不存在,会自动创建一个消费者;
    Count:查询的最大数量;
    Block:当没有消息时,最长等待时间;
    Noack:无需手动ACK,获取到消息后自动确认;
    key:指定队列名称
    id:获取消息的起始id:
    >:从下一个未消费的消息开始;
    其他:根据其他id开始;