1. 延时队列

zset 会按 score 进行排序,如果 score 代表想要执行时间的时间戳。在某个时间将它插入 zset 集合中,它变会按照时间戳大小进行排序,也就是对执行时间前后进行排序。

起一个死循环线程不断地进行取第一个 key 值,如果当前时间戳大于等于该 key 值的 score 就将它取出来进行消费删除,可以达到延时执行的目的。

发送消息

代码如下:

 
 public void sendMessage(long messageId, String message) {System.out.println("发送消息");Jedis client = jedisPool.getResource();Pipeline pipeline = client.pipelined();// score 设置成当前时间戳 + 延迟时间pipeline.zadd(DELAY_QUEUE, System.currentTimeMillis() + DELAY_TIME * 1000,String.format(MEMBER_PREFIX, messageId));Map<String, String> map = new HashMap<>();map.put(String.format(MEMBER_PREFIX, messageId), message);pipeline.hset(DELAY_MESSAGE, map);pipeline.syncAndReturnAll();pipeline.close();client.close();System.out.println("发送消息 over");}

采用 pipeline 的方式,同时写入 zset 和 hash 中

消费消息

代码如下:

public void consumer() {System.out.println("消费消息开始");Jedis client = jedisPool.getResource();Set<Tuple> tupleSet = client.zrangeByScoreWithScores(DELAY_QUEUE, 0, System.currentTimeMillis());for (Tuple t : tupleSet) {long messageId = Long.valueOf(t.getElement().replaceAll("[^0-9]", ""));messageHandler(messageId);}client.close();System.out.println("消费消息 over");
}public void messageHandler(long messageId) {System.out.println("===");pool.execute(() -> {  // 放到线程池处理Jedis client = jedisPool.getResource();String message = client.hget(DELAY_MESSAGE, String.format(MEMBER_PREFIX, messageId));System.out.println("处理消息体" + message);System.out.println("处理消息体成功");Pipeline pipeline = client.pipelined();pipeline.multi();pipeline.hdel(DELAY_MESSAGE, String.format(MEMBER_PREFIX, messageId));pipeline.zrank(DELAY_QUEUE, String.format(MEMBER_PREFIX, messageId));pipeline.exec();pipeline.close();client.close();});
}

问题

  1. 没有 ack 机制,当消费失败的情况下队列如何处理?

  2. 这是 topic 模式,广播模式如何搞

示例代码是 demo,简单应用,投入生产中还需要考虑各种细节问题

2. 排行榜

经常浏览技术社区的话,应该对 “1小时最热门” 这类榜单不陌生。如何实现呢?如果记录在数据库中,不太容易对实时统计数据做区分。我们以当前小时的时间戳作为 zset 的 key,把贴子ID 作为 member ,点击数评论数等作为 score,当 score 发生变化时更新 score。利用 ZREVRANGE 或者 ZRANGE 查到对应数量的记录。

记录回复数

代码如下:

/*** 模拟每次针对贴子的回复数加 1** @param id
*/
public void post(long id) {Jedis client = jedisPool.getResource();client.zincrby(POSTLIST, 1, String.format(MEMBER_PREFIX, id));client.close();
}

获取列表

代码如下:

/*** 获取 Top 的贴子列表 ID** @param size* @return
*/
public List<Integer> getTopList(int size) {List<Integer> result = new ArrayList<>();if (size <= 0 || size > 100) {return result;}Jedis client = jedisPool.getResource();Set<Tuple> tupleSet = client.zrevrangeWithScores(POSTLIST, 0, size - 1);client.close();for (Tuple tuple : tupleSet) {String t = tuple.getElement().replaceAll("[^0-9]", "");result.add(Integer.valueOf(t));}return result;
}

模拟用户发帖的行为

代码如下:

public void test() throws InterruptedException {int threadSize = 200;long[] ids = {100, 102, 103, 104, 105, 106, 101, 108, 107, 200, 109, 201, 202};CountDownLatch countDownLatch = new CountDownLatch(threadSize);for (int i = 0; i < threadSize; i++) {pool.execute(() -> {for (int j = 0; j < 3; j++) {Random r = new Random();int index = (int) (r.nextDouble() * ids.length);post(ids[index]);}countDownLatch.countDown();});}countDownLatch.await();
}

问题

  1. 数量过大时会占用大量内存,需要清理很多冷数据

  2. 适合处理点击数、访问量之类,处理发帖回复这种还需要考虑,帖子审核不通过的情况

3. 限流

滑动窗口是限流常见的一种策略。如果我们把一个用户的 ID 作为 key 来定义一个 zset ,member 或者 score 可以都为访问时的时间戳。我们只需统计某个 key 下在指定时间戳区间内的个数,就能得到这个用户滑动窗口内访问频次,与最大通过次数比较,来决定是否允许通过。

滑动窗口

代码如下:

/**** @param userId* @param period 窗口大小* @param maxCount 最大频次限制* @return
*/
public boolean isActionAllowed(String userId, int period, int maxCount) {String key = String.format(KEY, userId);long nowTs = System.currentTimeMillis();Jedis client = jedisPool.getResource();Pipeline pipe = client.pipelined();pipe.multi();pipe.zadd(key, nowTs, String.format(MEMBER, userId, nowTs));pipe.zremrangeByScore(key, 0, nowTs - period * 1000);Response<Long> count = pipe.zcard(key);pipe.expire(key, period + 1);pipe.exec();pipe.close();client.close();return count.get() <= maxCount;
}

思路是每一个请求到来时,将时间窗口外的记录全部清理掉,只保留窗口内的记录。zset 中只有 score 值非常重要,value 值没有特别的意义,只需要保证它是唯一的就可以了

问题

  1. 需要清理额外的数据

  2. 限制的请求量过大时,会占用大量内存

Redis ZSet 的几种使用场景相关推荐

  1. Redis源码-ZSet:Redis ZSet存储原理、Redis ZSet命令、 Redis ZSet两种存储底层编码ziplist/dict+skiplist、Redis ZSet应用场景

    Redis源码-ZSet:Redis ZSet存储原理.Redis ZSet命令. Redis ZSet两种存储底层编码ziplist/dict+skiplist.Redis ZSet应用场景 Red ...

  2. redis zset怎么排序_redis(set、zset)类型使用和使用场景

    Redis的数据类型 Redis的数据类型共有五种:string,list,hash,set,zset: String 字符串相对来说做平常,key-value,类似是hashmap的用法: List ...

  3. Redis的20种使用场景

    本文介绍Redis除了缓存以外的使用场景. 测试源码:https://github.com/vehang/ehang-spring-boot/tree/main/spring-boot-011-red ...

  4. Redis 几种应用场景

    Redis 几种应用场景 字符串缓存 <?php$redis = new Redis();$redis->connect('127.0.0.1',6379);$strKey = 'test ...

  5. 数据类型_分享redis中除5种基础数据类型以外的高级数据类型

    众所周知,在redis中的数据类型有String(字符串).hash(哈希).list(列表).set(集合).zset(有序集合)五种.但在这5种之外还有高级数据类型. 今天和大家介绍下常用的高级数 ...

  6. Redis专题-底层数据结构与使用场景

    Redis介绍 Redis是一种基于键值对的NoSQL数据库,是一个基于内存中的数据结构存储系统,可以用作数据库.缓存和消息中间件.它支持以string(字符串),hash(哈希),list(列表), ...

  7. Redis支持的数据类型以及使用场景,持久化,哨兵机制,缓存击穿,缓存穿透

    Redis支持的数据类型以及使用场景,持久化,哨兵机制,缓存击穿,缓存穿透 简单介绍一个redis? redis是内存中的数据结构存储系统,一个key-value类型的非关系型数据库,可持久化的数据库 ...

  8. php redis新增数据类型,Redis有哪几种数据类型

    Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合).(推荐学习:Redis视频教程) String(字符 ...

  9. 深入理解 Redis Template及4种序列化方式__spring boot整合redis实现RedisTemplate三分钟快速入门

    概述 使用Spring 提供的 Spring Data Redis 操作redis 必然要使用Spring提供的模板类 RedisTemplate, 今天我们好好的看看这个模板类 . RedisTem ...

最新文章

  1. 计算机与控制学院优秀学生,三好学生主要事迹:计算机与控制工程学院王天琪事迹.doc...
  2. worker进程和task进程区别_celery 每个 worker 在执行任务时,如何配置一定数量的 task?...
  3. c语言程序设计第四版十二五,C语言程序设计/普通高等教育十二五规划教材
  4. busybox arm-linux-gcc 4.4.4库的路径,BUSYBOX编译错误及解决方法总结
  5. Spring5参考指南:组件扫描
  6. MybatisPlus学习(四)条件构造器Wrapper方法详解
  7. 使用Vysper,TomEE和PrimeFaces将XMPP服务器嵌入JSF Web应用程序内部
  8. MapReduce:处理数据密集型文本处理–局部聚合第二部分
  9. vue2 父子组件传参 回调函数使用
  10. Windows下DNS ID欺骗的原理与实现
  11. java排序算法 sort_Java排序算法之SleepSort排序示例
  12. 适合MySQL master/slave模式的JDBC driver: lbpool
  13. 请求并操作指定url处的xml文件
  14. iOS平台下闪退原因汇总(一):Ran out of trampolines of type 0/1/2 运行时间错误
  15. 可观测性平台下的低代码技术实践
  16. 一款好用的国产软件源代码缺陷分析平台 — CodeSense
  17. 直流电机驱动模块介绍
  18. 镶锆石、侧边指纹、双屏翻盖机,三星的这款2万块手机,只有土豪能懂
  19. STAR法则修改简历
  20. 基于matlab山脊线,基于Matlab的标记分水岭分割算法

热门文章

  1. 去年下半年blog文章导入完毕
  2. docker安装以及部署springboot项目
  3. Linux学习笔记(四)账号管理之管理用户账号
  4. Linux学习笔记(三):系统运行级与运行级的切换
  5. git常用命令+git规范(附merge合并及冲突解决)
  6. vue —— UI组件库
  7. java hung_java – Hung JVM消耗100%的CPU
  8. js基础--变量、数据类型、循环、判断、函数定义
  9. BZOJ3238:[AHOI2013]差异——题解
  10. 《中国人工智能学会通讯》——11.64 基于成对约束的属性特征选择