使用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() |