Redisson在日常开发过程中的使用
使用BitSet实现日期连续签到
  @GetMapping("/user/sign/{id}")
  public Result<String> userSign(@PathVariable("id") Long id,
                                @RequestParam(value = "date", required = false) 
                                String completeDated) throws IOException {
   
    LocalDateTime now = LocalDateTime.now();
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = "M2B:USER:SIGN" + ":" + id + ":" + keySuffix;
    RBitSet bitSet = redissonClient.getBitSet(key);
    bitSet.expire(50, TimeUnit.MINUTES);
    if (id == 1) {
      // 签到,获取今天是本月的第几天,bitset下标从0开始
      int dayOfMonth = now.getDayOfMonth();
      if (bitSet.get(dayOfMonth - 1)) {
        return Result.accept("您已签到,请勿重复签到");
      } else {
        bitSet.set(dayOfMonth - 1, true);
        return Result.accept("签到成功");
      }
    } else if (id == 2) {
      // 补签
      Date date = DateUtil.parseDate(completeDate);
      Instant instant = date.toInstant();
      ZoneId zoneId = ZoneId.systemDefault();
      LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();
      //4.获取今天是本月的第几天
      int dayOfMonth = localDateTime.getDayOfMonth();
      //5.写入redis SETBIT key offset 1
      bitSet.set(dayOfMonth - 1, true);
    } else if (id == 3) {
      // 签到天数(标识为1的)
      long cardinality = bitSet.cardinality();
      return Result.accept("总共签到了:" + cardinality + "天");
    } else if (id == 4) {
      // 连续签到,以及签到的天数
      int dayOfMonth = now.getDayOfMonth();
      BitSet bitSet1 = bitSet.asBitSet();
      List<Integer> signList = Lists.newArrayList();
      bitSet1.stream().forEach(signList::add);
      signList.removeIf(b -> b > dayOfMonth);
      boolean continuousSign = signList.stream().anyMatch(c -> c.equals(dayOfMonth));
      if (continuousSign) {
        System.out.println("连续签到:" + getLongDay(signList) + "天");
      }
      List<DateTime> dateTimes = DateUtil.rangeToList(DateUtil.beginOfMonth(new Date()),
          DateUtil.endOfMonth(new Date()), DateField.DAY_OF_MONTH);
      System.out.println("连续签到的日期");
      for (int signIndex : signList) {
        DateTime dateTime = dateTimes.get(signIndex);
        System.out.println(dateTime.toDateStr());
      }
      System.out.println(1);
    }
    return Result.accept(null);
  }
/**
   * 获取连续签到的天数
   *
   * @param list 传入日期的集合 此处以 List<Integer>举例
   * @return
   */
  private static int getLongDay(List<Integer> list) {
    List<Integer> list2 = new ArrayList<Integer>();// 存放中断元素的位置
    List<Integer> list3 = new ArrayList<Integer>();
    //先获取连续中断的位置,放在list2中
    for (int i = 0; i < list.size(); i++) {
      if (i == list.size() - 1) {
        break;
      } else if (list.get(i + 1) - list.get(i) != 1) {
        list2.add(i);
      }
    }
    //通过判断获取连续的个数,在list3中取最大值即可。
    if (0 == list2.size()) {
      // 没有中断 返回原集合长度
      return list.size();
    } else {
      for (int i = 0; i < list2.size(); i++) {
        if (1 == list2.size()) {
          list3.add(list2.get(0) + 1);// 中断前的天数
          list3.add(list.size() - 1 - list2.get(i));// 剩余的天数
        } else {
          if (i == 0) {
            list3.add(list2.get(0) + 1);
            list3.add(list2.get(i + 1) - list2.get(i));
          } else if (i == list2.size() - 1) {
            list3.add(list.size() - 1 - list2.get(i));
          } else {
            list3.add(list2.get(i + 1) - list2.get(i));
          }
        }
      }
      return Collections.max(list3);
    }
  }
避坑
bitset占用的内存是用最大的offset来决定的,根本不会管你实际要存多少有效数据,计算公式为
● 占用内存bit = 最大offset              
● 占用内存B = 最大offset / 8     
● 占用内存KB = 最大offset / 8 / 1024
● 占用内存MB = 最大offset / 8 / 1024 / 1024
- 
比如现在我们要存用户对一篇文章的点赞数, 以及判断用户对文章有没有点赞。 
- 
如果使用bitset, 那么确实一篇文章只要一个key就行了,也没有hash或set的结构复杂,比如文章id为1, 简单点key的名字就为article⭐1。 
- 
那么点赞确实很简单,如果用户id=100,看下占用内存忽略不计 
- 
但如果这个时候又来了一个用户点赞,用户id=2560000呢,使用方式没有变化,也完成了点赞的统计。但是占用内存呢? 
 这是什么概念?记录一篇小小的文章点赞数,即使只有两个人点赞, 就占用了312.5KB,来个几百篇文章或者动态之类的,再加上其他功能也这么使用用,这要浪费吃掉多少内存。因此用bitset的时候除了考虑系统人数规模(主要是决定bitset offset值的条件),还要考虑实际会有多少人用到这个功能。即使人数达到了很大的量级,但某个功能是个很偏僻的功能,还是要少用。
总结如下
- 使用之前永远要作为第一要务考虑的就是offset的基数大小问题
- 如果uv很小,而且offset基数又很大,不要使用,offset基数很小,可以用
- 如果uv非常高,offset基数即使大一点,也可以使用,但这个要综合考虑去计算offset基数到底多大,uv又有没有高到离谱的程度。否则仍然还是set或者hash之类的更适用。
HyperLogLog
场景
在移动互联网的业务场景中,数据量很大,我们需要保存这样的信息:一个 key 关联了一个数据集合,同时对这个数据集合做统计。
- 
统计一个 APP 的日活、月活数; 
- 
统计一个页面的每天被多少个不同账户访问量(Unique Visitor,UV)); 
- 
统计用户每天搜索不同词条的个数; 
- 
统计注册 IP 数。 
通常情况下,我们面临的用户数量以及访问量都是巨大的,比如百万、千万级别的用户数量,或者千万级别、甚至亿级别的访问信息。
你可以通过 set 集合、bitmap 这类常用工具,但有个最大的缺点是,如果数据量巨大,比如 1 亿,甚至 10 亿将耗费巨大内存消耗。
原理
Redis HyperLogLog基于一种称为HyperLogLog算法的概率性算法来估计基数。 HyperLogLog使用一个长度为m的位数组和一些hash函数来估计集合中的唯一元素数。
在 HyperLogLog 算法中,对每个元素进行哈希处理,把哈希值转换为二进制后,根据二进制串前缀中 1 的个数来给每个元素打分。例如,一个元素的哈希值为01110100011,那么前缀中1的个数是3,因此在 HyperLogLog 算法中,这个元素的分数为3。
当所有元素的分数统计完之后,取每一个分数的倒数(1 / 2^n),然后将这些倒数相加后取倒数,就得到一个基数估计值,这个值就是HyperLogLog算法的估计结果。
HyperLogLog算法通过对位数组的长度m的大小进行取舍,折衷数据结构占用的内存与估计值的精准度(即估计误差),得到了在数据占用空间与错误较小程度之间完美的平衡。
简而言之,HyperLogLog算法的核心思想是基于哈希函数和位运算,通过将哈希值转换成比特流并统计前导0的个数,从而快速估算大型数据集中唯一值的数量。通过 hyperloglog 算法我们可以在非常大的数据集中进行极速的网页浏览器去重。
算法简介
HyperLogLog 算法的基本思想来自伯努利过程。
伯努利过程就是一个抛硬币实验的过程。抛一枚正常硬币,落地可能是正面,也可能是反面,二者的概率都是 1/2 。伯努利过程就是一直抛硬币,直到落地时出现正面位置,并记录下抛掷次数k。比如说,抛一次硬币就出现正面了,此时 k 为 1; 第一次抛硬币是反面,则继续抛,直到第三次才出现正面,此时 k 为 3。
那么如何通过伯努利过程来估算抛了多少次硬币呢?还是假设 1 代表抛出正面,0 代表反面。连续出现两次 0 的序列应该为“001”,那么它出现的概率应该是三个二分之一相乘,即八分之一。那么可以估计大概抛了 8 次硬币。
HyperLogLog 原理思路是通过给定 n 个的元素集合,记录集合中数字的比特串第一个1出现位置的最大值k,也可以理解为统计二进制低位连续为零(前导零)的最大个数。通过k值可以估算集合中不重复元素的数量m,m近似等于 2^k。
延迟队列
主要处理非立即生效的业务场景,比如
- 
订单下单30分钟内,超时未支付; 
- 
优惠券、活动等需要在指定时间内生效的, 
- 
会议开始前10分钟消息通知 
源码实现
首先创建DelayedQueueManager 用于延迟队列的创建、获取、销毁
@Slf4j
@RequiredArgsConstructor
public class DelayedQueueManager {
   private final RedissonClient redissonClient;
   private final ThreadPoolTaskExecutor threadPoolExecutor;
   private final ConcurrentHashMap<String, DelayedQueue<?>> DELAYED_QUEUE_MAP = new ConcurrentHashMap<>();
    /**
    * 创建延时对象实例
    *
    * @param queueId       队列标识
    * @param <T>           泛型类型
    * @return              延时队列实例
    */
    public <T> DelayedQueue<T> create(String queueId) {
        // 先尝试从容器中获取延迟队列
        DelayedQueue<T> delayedQueue = (DelayedQueue<T>) DELAYED_QUEUE_MAP.get(queueId);
        // 如果未获取到,则实例化一个延迟队列对象
        if (Objects.isNull(delayedQueue)) {
            delayedQueue = new DelayedQueue<>(redissonClient, queueId, threadPoolExecutor);
            DELAYED_QUEUE_MAP.putIfAbsent(queueId, delayedQueue);
        }
        return delayedQueue;
    }
        /**
        * 获取延迟队列实例
        *
        * @param queueId               队列标识id
        * @param createIfNotExists     对象不存在时是否直接创建
        * @param <T>                   元素泛型类型
        * @return                      延时队列实例
        */
        public <T> DelayedQueue<T> get(String queueId, boolean createIfNotExists) {
            // 先尝试从容器中获取延迟队列
            final DelayedQueue<T> delayedQueue = (DelayedQueue<T>) DELAYED_QUEUE_MAP.get(queueId);
            if (Objects.isNull(delayedQueue)) {
                if (createIfNotExists) {
                    return create(queueId);
                }
                throw new BizzException("找不到延时队列【{}】的实例对象!", queueId);
            }
            else {
                return delayedQueue;
            }
        }
        /**
        * 销毁指定延时队列实例
        *
        * @param queueId       延时队列id
        */
        public void destroy(String queueId) {
            // 获取延时队列实例并调用销毁方法
            Optional.ofNullable(DELAYED_QUEUE_MAP.get(queueId)).ifPresent(DelayedQueue::destroy);
        }
    }
增加延迟队列,从redisson的BlockingQueue获取DelayedQueue,并通过守护线程,获取延迟队列中的数据,执行响应的监听器
public class DelayedQueue<T> {
    /**
     * 队列id标识
     */
    private final String queueId;
    /**
     * 是否取消的标识
     */
    private final AtomicBoolean isCancelled = new AtomicBoolean(false);
    /**
     * 守护线程
     */
    private final Future<Void> daemonThread;
    // 阻塞队列
    private final RBlockingQueue<T> blockingQueue;
    // 延迟队列
    private final RDelayedQueue<T> delayedQueue;
    // 监听器列表
    private final ConcurrentHashSet<DelayedQueueListener<T>> delayedQueueListeners = new ConcurrentHashSet<>();
    /**
     * 构造器函数
     *
     * @param redissonClient            redisson客户端实例
     * @param queueId                   队列标识
     * @param threadPoolTaskExecutor    监听器通知线程池
     */
    public DelayedQueue(RedissonClient redissonClient, String queueId, ThreadPoolTaskExecutor threadPoolTaskExecutor) {
        this.blockingQueue = redissonClient.getBlockingQueue(queueId);
        this.delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
        this.queueId = queueId;
        // 添加守护线程监听阻塞队列变化,通知到每一个监听器
        this.daemonThread = CompletableFuture.runAsync(
                () -> {
                    while (!isCancelled.get()) {
                        try {
                            // 获取延时队列中的元素(会阻塞)
                            final T value = blockingQueue.take();
                            // 向每个注册监听器发送通知
                            delayedQueueListeners.forEach(
                                    // 异步通知防止阻塞
                                    listener -> CompletableFuture.runAsync(() ->  listener.invoke(value), threadPoolTaskExecutor)
                            );
                        } catch (InterruptedException e) {
                            log.error("获取阻塞队列元素异常!队列名称:{}", this.queueId, e);
                            ThreadUtil.safeSleep(GlobalConstants.Number.THOUSAND);
                        }
                    }
                }
        );
    }
    /**
     * 添加监听器
     *
     * @param delayedQueueListener      延迟队列事件监听器
     */
    public boolean addListener(DelayedQueueListener<T> delayedQueueListener) {
        // 注册监听器
        return delayedQueueListeners.add(delayedQueueListener);
    }
    /**
     * 移除监听器
     *
     * @param delayedQueueListener      延迟队列事件监听器
     */
    public boolean removeListener(DelayedQueueListener<T> delayedQueueListener) {
        return delayedQueueListeners.remove(delayedQueueListener);
    }
    /**
     * 添加元素到延时队列
     *
     * @param element       队列元素
     * @param delay         延迟时间
     * @param timeUnit      时间单位
     */
    public void offer(T element, long delay, TimeUnit timeUnit) {
        this.delayedQueue.offer(element, delay, timeUnit);
    }
    /**
     * 销毁当前队列
     */
    public void destroy() {
        // 1. 取消守护线程任务
        if (isCancelled.compareAndSet(false, true)) {
            // 2. 取消当前正在运行的任务
            daemonThread.cancel(true);
            // 3. 清空阻塞队列和延迟队列中的元素
            this.delayedQueue.destroy();
            this.blockingQueue.clear();
        }
    }
}
延迟队列触发监听器
/**
 * 延迟队列触发监听器
 *
 */
@FunctionalInterface
public interface DelayedQueueListener<T> {
    /**
     * 触发延迟队列事件
     */
    void invoke(T t);
}
延迟队列Springboot配置,配置延迟队列线程池
@Configuration
@AutoConfigureAfter(RedissonAutoConfiguration.class)
public class DelayedQueueBeanConfiguration {
    /**
     * 延时队列异步线程池
     */
    @Bean
    @ConditionalOnMissingBean(name = "delayedQueueListenerAsyncPool")
    public ThreadPoolTaskExecutor delayedQueueListenerAsyncPool() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        //核心线程数=容器cpu核心的2倍+1
        int coreNum = TWO * Runtime.getRuntime().availableProcessors() + ONE;
        threadPoolTaskExecutor.setCorePoolSize(coreNum);
        //最大线程数是核心线程数的两倍
        threadPoolTaskExecutor.setMaxPoolSize(TWO * coreNum);
        //队列长度100
        threadPoolTaskExecutor.setQueueCapacity(HUNDRED);
        //拒绝策略是直接抛异常
        threadPoolTaskExecutor.setRejectedExecutionHandler(
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
        threadPoolTaskExecutor.setBeanName("delayedQueueListenerAsyncPool");
        threadPoolTaskExecutor.setThreadNamePrefix("delay-queue-notify-");
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
    /**
     * 延时队列管理器注入
     *
     * @param redissonClient            redisson客户端实例
     * @param threadPoolTaskExecutor    线程池实例
     */
    @Bean
    @ConditionalOnBean(RedissonClient.class)
    public DelayedQueueManager delayedQueueManager(RedissonClient redissonClient, 
                  @Qualifier("delayedQueueListenerAsyncPool")  ThreadPoolTaskExecutor threadPoolTaskExecutor) {
        return new DelayedQueueManager(redissonClient, threadPoolTaskExecutor);
    }
}
方法调用,此处案例为:优化券创建后,根据配置生效的时间进行生效
String delayedQueueId = RedisKeyUtil.getCouponEffectDelayedQueueKey();
DelayedQueue<CouponVo> couponEffectDelayedQueue
    = delayedQueueManager.get(delayedQueueId, true);
couponEffectDelayedQueue.addListener(couponEffectDelayedQueueListener);
// couponVo 优惠券, between 优惠券开始的时间(延迟的时间)
couponEffectDelayedQueue.offer( couponVo , between, TimeUnit.SECONDS);
监听延迟队列生效,并执行生效方法
@Slf4j
@Component
@RequiredArgsConstructor
public class CouponEffectDelayedQueueListener implements DelayedQueueListener<CouponVo> {
    private final CouponHandler couponHandler;
    @Override
    public void invoke(CouponVo couponVo) {
        String effectTime = DateUtil.format(couponVo.getSendStartTime(), DatePattern.NORM_DATETIME_FORMAT);
        log.info("监听到优惠券编号{}生效延迟队列, 优惠券生效时间:{}",  couponVo.getCouponNo(), effectTime);
        try {
            couponHandler.changeCouponStatus(CouponDto.builder()
                .ids(Lists.newArrayList(couponVo.getId()))
                .status(SendCouponStatusEnum.IN_USING)
                .modifierId(-1L)
                .modifyTime(new Date()).build()
            );
            log.info("优惠券:{} 状态生效成功", couponVo.getCouponNo());
        } catch (Exception e) {
            log.warn("优惠券:{} 状态生效失败", couponVo.getCouponNo(), e);
        }
    }
}
Reids命令对应Redissson方法
| Redis命令 | Redisson对象方法 | 
|---|---|
| AUTH | Config.setPassword() | 
| BITCOUNT | RBitSet.cardinality() RBitSet.cardinalityAsync() RBitSetReactive.cardinality() | 
| BITOP | RBitSet.or() RBitSet.orAsync() RBitSetReactive.or() RBitSet.and() RBitSet.andAsync() RBitSetReactive.and() RBitSet.not() RBitSet.xor() RBitSet.xorAsync() RBitSetReactive.xor() | 
| BITPOS | RBitSet.length() RBitSet.lengthAsync() RBitSetReactive.length() | 
| BLPOP | RBlockingQueue.take() RBlockingQueue.takeAsync() RBlockingQueueReactive.take() RBlockingQueue.poll() RBlockingQueue.pollAsync() RBlockingQueueReactive.poll() RBlockingQueue.pollFromAny() RBlockingQueue.pollFromAnyAsync() RBlockingQueueReactive.pollFromAny() | 
| BRPOP | RBlockingDeque.takeLast() RBlockingDeque.takeLastAsync() RBlockingDequeReactive.takeLast() | 
| BRPOPLPUSH | RBlockingQueue.pollLastAndOfferFirstTo() RBlockingQueue.pollLastAndOfferFirstToAsync() RBlockingQueueReactive.pollLastAndOfferFirstTo() | 
| CLIENT SETNAME | Config.setClientName() | 
| CLUSTER INFO | ClusterNode.info() | 
| CLUSTER KEYSLOT | RKeys.getSlot() RKeys.getSlotAsync() RKeysReactive.getSlot() | 
| CLUSTER NODES | 在ClusterConnectionManager里使用 | 
| DBSIZE | RKeys.count() RKeys.countAsync() RKeysReactive.count() | 
| DECR | RAtomicLong.decrementAndGet() RAtomicLong.decrementAndGetAsync() RAtomicLongReactive.decrementAndGetAsync() | 
| DEL | RObject.delete() RObject.deleteAsync() RObjectReactive.delete() RKeys.delete() RKeys.deleteAsync() | 
| STRLEN | RBucket.size() RBucket.sizeAsync() RBucketReactive.size() | 
| EVAL | RScript.eval() RScript.evalAsync() RScriptReactive.eval() | 
| CLIENT REPLY | RBatch.executeSkipResult() | 
| EVALSHA | RScript.evalSha() RScript.evalShaAsync() RScriptReactive.evalSha() | 
| EXISTS | RObject.isExists() RObject.isExistsAsync() RObjectReactive.isExists() | 
| FLUSHALL | RKeys.flushall() RKeys.flushallAsync() RKeysReactive.flushall() | 
| FLUSHDB | RKeys.flushdb() RKeys.flushdbAsync() RKeysReactive.flushdb() | 
| GEOADD | RGeo.add() RGeo.addAsync() RGeoReactive.add() | 
| GEODIST | RGeo.dist() RGeo.distAsync() RGeoReactive.dist() | 
| GEOHASH | RGeo.hash() RGeo.hashAsync() RGeoReactive.hash() | 
| GEOPOS | RGeo.pos() RGeo.posAsync() RGeoReactive.pos() | 
| GEORADIUS | RGeo.radius() RGeo.radiusAsync() RGeoReactive.radius() RGeo.radiusWithDistance() RGeo.radiusWithDistanceAsync() RGeoReactive.radiusWithDistance() RGeo.radiusWithPosition() RGeo.radiusWithPositionAsync() RGeoReactive.radiusWithPosition() | 
| GEORADIUSBYMEMBER | RGeo.radius() RGeo.radiusAsync() RGeoReactive.radius() RGeo.radiusWithDistance() RGeo.radiusWithDistanceAsync() RGeoReactive.radiusWithDistance() RGeo.radiusWithPosition() RGeo.radiusWithPositionAsync() RGeoReactive.radiusWithPosition() | 
| GET | RBucket.get() RBucket.getAsync() RBucketReactive.get() | 
| GETBIT | RBitSet.get() RBitSet.getAsync() RBitSetReactive.get() | 
| GETSET | RBucket.getAndSet() RBucket.getAndSetAsync() RBucketReactive.getAndSet() RAtomicLong.getAndSet() RAtomicLong.getAndSetAsync() RAtomicLongReactive.getAndSet() RAtomicDouble.getAndSet() RAtomicDouble.getAndSetAsync() RAtomicDoubleReactive.getAndSet() | 
| HDEL | RMap.fastRemove() RMap.fastRemoveAsync() RMapReactive.fastRemove() | 
| HEXISTS | RMap.containsKey() RMap.containsKeyAsync() RMapReactive.containsKey() | 
| HGET | RMap.get() RMap.getAsync() RMapReactive.get() | 
| HSTRLEN | RMap.valueSize() RMap.valueSizeAsync() RMapReactive.valueSize() | 
| HGETALL | RMap.readAllEntrySet() RMap.readAllEntrySetAsync() RMapReactive.readAllEntrySet() | 
| HINCRBY | RMap.addAndGet() RMap.addAndGetAsync() RMapReactive.addAndGet() | 
| HINCRBYFLOAT | RMap.addAndGet() RMap.addAndGetAsync() RMapReactive.addAndGet() | 
| HKEYS | RMap.readAllKeySet() RMap.readAllKeySetAsync() RMapReactive.readAllKeySet() | 
| HLEN | RMap.size() RMap.sizeAsync() RMapReactive.size() | 
| HMGET | RMap.getAll() RMap.getAllAsync() RMapReactive.getAll() | 
| HMSET | RMap.putAll() RMap.putAllAsync() RMapReactive.putAll() | 
| HSET | RMap.put() RMap.putAsync() RMapReactive.put() | 
| HSETNX | RMap.fastPutIfAbsent() RMap.fastPutIfAbsentAsyncRMapReactive.fastPutIfAbsent() | 
| HVALS | RMap.readAllValues() RMap.readAllValuesAsync() RMapReactive.readAllValues() | 
| INCR | RAtomicLong.incrementAndGet() RAtomicLong.incrementAndGetAsync() RAtomicLongReactive.incrementAndGet() | 
| INCRBY | RAtomicLong.addAndGet() RAtomicLong.addAndGetAsync() RAtomicLongReactive.addAndGet() | 
| KEYS | RKeys.findKeysByPattern() RKeys.findKeysByPatternAsync() RKeysReactive.findKeysByPattern() RedissonClient.findBuckets() | 
| LINDEX | RList.get() RList.getAsync() RListReactive.get() | 
| LLEN | RList.size() RList.sizeAsync() RListReactive.Size() | 
| LPOP | RQueue.poll() RQueue.pollAsync() RQueueReactive.poll() | 
| LPUSH | RDeque.addFirst() RDeque.addFirstAsync() RDequeReactive.addFirst() RDeque.offerFirst() RDeque.offerFirstAsync() RDequeReactive.offerFirst() | 
| LRANGE | RList.readAll() RList.readAllAsync() RListReactive.readAll() | 
| LREM | RList.fastRemove() RList.fastRemoveAsync() RList.remove() RList.removeAsync() RListReactive.remove() RDeque.removeFirstOccurrence() RDeque.removeFirstOccurrenceAsync() RDequeReactive.removeFirstOccurrence() RDeque.removeLastOccurrence() RDeque.removeLastOccurrenceAsync() RDequeReactive.removeLastOccurrence() | 
| LSET | RList.fastSet() RList.fastSetAsync() RListReactive.fastSet() | 
| LTRIM | RList.trim() RList.trimAsync() RListReactive.trim() | 
| LINSERT | RList.addBefore() RList.addBeforeAsync() RList.addAfter() RList.addAfterAsync() RListReactive.addBefore() RListReactive.addAfter() | 
| MGET | RedissonClient.loadBucketValues() | 
| MIGRATE | RObject.migrate() RObject.migrateAsync() | 
| MOVE | RObject.move() RObject.moveAsync() | 
| MSET | RedissonClient.saveBuckets() | 
| PERSIST | RExpirable.clearExpire() RExpirable.clearExpireAsync() RExpirableReactive.clearExpire() | 
| PEXPIRE | RExpirable.expire() RExpirable.expireAsync() RExpirableReactive.expire() | 
| PEXPIREAT | RExpirable.expireAt() RExpirable.expireAtAsync() RExpirableReactive.expireAt() | 
| PFADD | RHyperLogLog.add() RHyperLogLog.addAsync() RHyperLogLogReactive.add() RHyperLogLog.addAll() RHyperLogLog.addAllAsync() RHyperLogLogReactive.addAll() | 
| PFCOUNT | RHyperLogLog.count() RHyperLogLog.countAsync() RHyperLogLogReactive.count() RHyperLogLog.countWith() RHyperLogLog.countWithAsync() RHyperLogLogReactive.countWith() | 
| PFMERGE | RHyperLogLog.mergeWith() RHyperLogLog.mergeWithAsync() RHyperLogLogReactive.mergeWith() | 
| PING | Node.ping() NodesGroup.pingAll() | 
| PSUBSCRIBE | RPatternTopic.addListener() | 
| PTTL | RExpirable.remainTimeToLive() RExpirable.remainTimeToLiveAsync() RExpirableReactive.remainTimeToLive() | 
| PUBLISH | RTopic.publish | 
| PUNSUBSCRIBE | RPatternTopic.removeListener() | 
| RANDOMKEY | RKeys.randomKey() RKeys.randomKeyAsync() RKeysReactive.randomKey() | 
| RENAME | RObject.rename() RObject.renameAsync() RObjectReactive.rename() | 
| RENAMENX | RObject.renamenx() RObject.renamenxAsync() RObjectReactive.renamenx() | 
| RPOP | RDeque.pollLast() RDeque.pollLastAsync() RDequeReactive.pollLast() RDeque.removeLast() RDeque.removeLastAsync() RDequeReactive.removeLast() | 
| RPOPLPUSH | RDeque.pollLastAndOfferFirstTo() RDeque.pollLastAndOfferFirstToAsync() | 
| RPUSH | RList.add() RList.addAsync() RListReactive.add() | 
| SADD | RSet.add() RSet.addAsync() RSetReactive.add() | 
| SCARD | RSet.size() RSet.sizeAsync() RSetReactive.size() | 
| SCRIPT EXISTS | RScript.scriptExists() RScript.scriptExistsAsync() RScriptReactive.scriptExists() | 
| SCRIPT FLUSH | RScript.scriptFlush() RScript.scriptFlushAsync() RScriptReactive.scriptFlush() | 
| SCRIPT KILL | RScript.scriptKill() RScript.scriptKillAsync() RScriptReactive.scriptKill() | 
| SCRIPT LOAD | RScript.scriptLoad() RScript.scriptLoadAsync() RScriptReactive.scriptLoad() | 
| SDIFFSTORE | RSet.diff() RSet.diffAsync() RSetReactive.diff() | 
| SELECT | Config.setDatabase() | 
| SET | RBucket.set() RBucket.setAsync() RBucketReactive.set() | 
| SETBIT | RBitSet.set() RBitSet.setAsync() RBitSet.clear() RBitSet.clearAsync() | 
| SETEX | RBucket.set() RBucket.setAsync() RBucketReactive.set() | 
| SETNX | RBucket.trySet() RBucket.trySetAsync() RBucketReactive.trySet() | 
| SISMEMBER | RSet.contains() RSet.containsAsync() RSetReactive.contains() | 
| SINTERSTORE | RSet.intersection() RSet.intersectionAsync() RSetReactive.intersection() | 
| SINTER | RSet.readIntersection() RSet.readIntersectionAsync() RSetReactive.readIntersection() | 
| SMEMBERS | RSet.readAll() RSet.readAllAsync() RSetReactive.readAll() | 
| SMOVE | RSet.move() RSet.moveAsync() RSetReactive.move() | 
| SPOP | RSet.removeRandom() RSet.removeRandomAsync() RSetReactive.removeRandom() | 
| SREM | RSet.remove() RSet.removeAsync() RSetReactive.remove() | 
| SUBSCRIBE | RTopic.addListener() RTopicReactive.addListener() | 
| SUNION | RSet.readUnion() RSet.readUnionAsync() RSetReactive.readUnion() | 
| SUNIONSTORE | RSet.union() RSet.unionAsync() RSetReactive.union() | 
| TTL | RExpirable.remainTimeToLive() RExpirable.remainTimeToLiveAsync() RExpirableReactive.remainTimeToLive() | 
| UNSUBSCRIBE | RTopic.removeListener() RTopicReactive.removeListener() | 
| WAIT | RBatch.syncSlavesRBatchReactive.syncSlaves() | 
| ZADD | RScoredSortedSet.add() RScoredSortedSet.addAsync() RScoredSortedSetReactive.add() | 
| ZCARD | RScoredSortedSet.size() RScoredSortedSet.sizeAsync() RScoredSortedSetReactive.size() | 
| ZCOUNT | RScoredSortedSet.count() RScoredSortedSet.countAsync() | 
| ZINCRBY | RScoredSortedSet.addScore() RScoredSortedSet.addScoreAsync() RScoredSortedSetReactive.addScore() | 
| ZLEXCOUNT | RLexSortedSet.lexCount() RLexSortedSet.lexCountAsync() RLexSortedSetReactive.lexCount() RLexSortedSet.lexCountHead() RLexSortedSet.lexCountHeadAsync() RLexSortedSetReactive.lexCountHead() RLexSortedSet.lexCountTail() RLexSortedSet.lexCountTailAsync() RLexSortedSetReactive.lexCountTail() | 
| ZRANGE | RScoredSortedSet.valueRange() RScoredSortedSet.valueRangeAsync() RScoredSortedSetReactive.valueRange() | 
| ZREVRANGE | RScoredSortedSet.valueRangeReversed() RScoredSortedSet.valueRangeReversedAsync() RScoredSortedSetReactive.valueRangeReversed() | 
| ZUNIONSTORE | RScoredSortedSet.union() RScoredSortedSet.unionAsync() RScoredSortedSetReactive.union() | 
| ZINTERSTORE | RScoredSortedSet.intersection() RScoredSortedSet.intersectionAsync() RScoredSortedSetReactive.intersection() | 
| ZRANGEBYLEX | RLexSortedSet.lexRange() RLexSortedSet.lexRangeAsync() RLexSortedSetReactive.lexRange() RLexSortedSet.lexRangeHead() RLexSortedSet.lexRangeHeadAsync() RLexSortedSetReactive.lexRangeHead() RLexSortedSet.lexRangeTail() RLexSortedSet.lexRangeTailAsync() RLexSortedSetReactive.lexRangeTail() | 
| ZRANGEBYSCORE | RScoredSortedSet.valueRange() RScoredSortedSet.valueRangeAsync() RScoredSortedSetReactive.valueRange() RScoredSortedSet.entryRange() RScoredSortedSet.entryRangeAsync() RScoredSortedSetReactive.entryRange() | 
| TIME | RedissonClient.getNodesGroup() .getNode() .time() RedissonClient.getClusterNodesGroup() .getNode() .time() | 
| ZRANK | RScoredSortedSet.rank() RScoredSortedSet.rankAsync() RScoredSortedSetReactive.rank() | 
| ZREM | RScoredSortedSet.remove() RScoredSortedSet.removeAsync() RScoredSortedSetReactive.remove() RScoredSortedSet.removeAll() RScoredSortedSet.removeAllAsync() RScoredSortedSetReactive.removeAll() | 
| ZREMRANGEBYLEX | RLexSortedSet.removeRangeByLex() RLexSortedSet.removeRangeByLexAsync() RLexSortedSetReactive.removeRangeByLex() RLexSortedSet.removeRangeHeadByLex() RLexSortedSet.removeRangeHeadByLexAsync() RLexSortedSetReactive.removeRangeHeadByLex() RLexSortedSet.removeRangeTailByLex() RLexSortedSet.removeRangeTailByLexAsync() RLexSortedSetReactive.removeRangeTailByLex() | 
| ZREMRANGEBYLEX | RScoredSortedSet.removeRangeByRank() RScoredSortedSet.removeRangeByRankAsync() RScoredSortedSetReactive.removeRangeByRank() | 
| ZREMRANGEBYSCORE | RScoredSortedSet.removeRangeByScore() RScoredSortedSet.removeRangeByScoreAsync() RScoredSortedSetReactive.removeRangeByScore() | 
| ZREVRANGEBYSCORE | RScoredSortedSet.entryRangeReversed() RScoredSortedSet.entryRangeReversedAsync() RScoredSortedSetReactive.entryRangeReversed() RScoredSortedSet.valueRangeReversed() RScoredSortedSet.valueRangeReversedAsync() RScoredSortedSetReactive.valueRangeReversed() | 
| ZREVRANK | RScoredSortedSet.revRank() RScoredSortedSet.revRankAsync() RScoredSortedSetReactive.revRank() | 
| ZSCORE | RScoredSortedSet.getScore() RScoredSortedSet.getScoreAsync() RScoredSortedSetReactive.getScore() | 
| SCAN | RKeys.getKeys() RKeysReactive.getKeys() | 
| SSCAN | RSet.iterator() RSetReactive.iterator() | 
| HSCAN | RMap.keySet() .iterator() RMap.values() .iterator() RMap.entrySet() .iterator() RMapReactive.keyIterator() RMapReactive.valueIterator() RMapReactive.entryIterator() | 
| ZSCAN | RScoredSortedSet.iterator() RScoredSortedSetReactive.iterator() | 

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号