Set数据类型,存储的数据不能重复,类似于Java中的HashSet。
-
SMEMBERS
获取指定key中所有的成员,该命令执行会有一定的风险,如果集合中有很多个数据,返回大量的数据,
会有一定的网络和内存开销,会阻塞redis中唯一的一个线程,这个时候就需要用到SSCAN。 -
SSCAN 分批次的获取数据,每次返回当前获取到的位置,交给下一次继续扫描。
代码实现
@Test
public void test04() {
Set<Object> set = new HashSet<>();
//每次获取500个
ScanOptions scanOptions = new ScanOptions.ScanOptionsBuilder().count(500).build();
// 放在try中自动释放cursor
try (Cursor<Object> cursor = redisTemplate.opsForSet().scan("set", scanOptions)) {
while (cursor.hasNext()) {
set.add(cursor.next());
}
} catch (IOException e) {
}
System.out.println(set.size());
}
ZSET同样不能存储重复的数据,后续的会覆盖之前的数据,geo本质用的是zset,geo相关的可以用zset的命令,同样针对拉取所有数据提供了解决方案,
ZSET,原理和SSCAN类似。
ZSET使用案例:基于评论次数的控制
List<Object> objects = redisTemplate.executePipelined(new SessionCallback<List>() {
@Override
public List execute(RedisOperations operations) throws DataAccessException {
//添加评论
operations.opsForZSet().add(FREQ_CONTROLLER_PERFIX, UUID.randomUUID().toString().replaceAll("-", ""),
System.currentTimeMillis());
//保留一个小时内的评论
operations.opsForZSet().removeRangeByScore(FREQ_CONTROLLER_PERFIX, 0, -3600 * 1000);
//求一个小时内的评论
Long num = operations.opsForZSet().zCard(FREQ_CONTROLLER_PERFIX);
//设置key的过期时间
operations.expire(FREQ_CONTROLLER_PERFIX, 2 * 3600, TimeUnit.SECONDS);
return null;
}
});
System.out.println(JSONObject.toJSONString(objects));
参考:https://mp.weixin.qq.com/s/mvAkPXBayAzT_RWFdsOt5A
案例:延迟队列,主要用的是redis的ZRANGEBYSCORE,根据score来回去过期的任务。
添加任务线程
//添加任务
SCHEDULED_EXECUTOR_SERVICE.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try{
int startId=ATOMIC_INTEGER.get();
redisTemplate.executePipelined(new SessionCallback<List>() {
@Override
public List<String> execute(RedisOperations operations) throws DataAccessException {
for (int i = 0; i < 1000; i++) {
Task task = new Task();
task.setId(ATOMIC_INTEGER.incrementAndGet());
long delayTime = System.currentTimeMillis()+getRandomNum(0, 100)*1000;
task.setDelayTime(delayTime);
operations.opsForZSet().add(DELAY_TASK, task, delayTime);
}
return null;
}
});
LOGGER.info("添加任务id:{}-{}完成",startId,ATOMIC_INTEGER.get());
}catch (Exception e){
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}
},0,4, TimeUnit.SECONDS);
消费线程
//消费任务
for (int i=0;i<4;i++){
SCHEDULED_EXECUTOR_SERVICE.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
//获取需要执行的任务
String identify = redisService.acquireLockWithTimeout(DELAY_TAKS_LOCK, 10000, 1000);
Set<Task> setTask = (Set) redisTemplate.opsForZSet().
reverseRangeByScore(DELAY_TASK, System.currentTimeMillis(), Double.MAX_VALUE, 0, COUNT_NUM);
LOGGER.info("获取到需要执行的任务数:{}",setTask.size());
//移除
LOGGER.info("移除个数{}",setTask.size());
List<Object> objects = redisTemplate.executePipelined(new SessionCallback<List>() {
@Override
public List execute(RedisOperations operations) throws DataAccessException {
for (Task t : setTask) {
operations.opsForZSet().remove(DELAY_TASK, t);
}
return null;
}
});
LOGGER.info("删除完毕,删除的个数为{}",objects.stream().count());
//释放锁
redisService.releaseLock(DELAY_TAKS_LOCK,identify);
//执行任务
Thread.sleep(10000);
}catch (Exception e){
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}
},0,4, TimeUnit.SECONDS);
}
监听队列中的任务数
SCHEDULED_EXECUTOR_SERVICE.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try{
Long count = redisTemplate.opsForZSet().count(DELAY_TASK, 0, System.currentTimeMillis());
LOGGER.info("队列中总共有{}个任务",count);
}catch (Exception e){
e.printStackTrace();
LOGGER.error(e.getMessage());
}
}
},0,4, TimeUnit.SECONDS);