热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

读懂才会用:RedisZSet的几种使用场景

  本文展示具体的使用方法和问题,供大家参考。代码示例,仅仅是Demo,在生产使用需要考虑更多细节问题。1.延时队列zset会按score进行排序,如果score代表想要执行时间的

 

 

本文展示具体的使用方法和问题,供大家参考。代码示例,仅仅是Demo,在生产使用需要考虑更多细节问题。


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 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 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();
});
}

问题

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

这是 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 getTopList(int size) {
List result = new ArrayList<>();
if (size <= 0 || size > 100) {
return result;
}
Jedis client = jedisPool.getResource();
Set 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 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();
}

问题

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

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


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 count = pipe.zcard(key);
pipe.expire(key, period + 1);
pipe.exec();
pipe.close();
client.close();
return count.get() <= maxCount;
}

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

 



推荐阅读
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 怎么在PHP项目中实现一个HTTP断点续传功能发布时间:2021-01-1916:26:06来源:亿速云阅读:96作者:Le ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • Givenasinglylinkedlist,returnarandomnode'svaluefromthelinkedlist.Eachnodemusthavethe s ... [详细]
  • 本文介绍了Oracle存储过程的基本语法和写法示例,同时还介绍了已命名的系统异常的产生原因。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 本文介绍了贝叶斯垃圾邮件分类的机器学习代码,代码来源于https://www.cnblogs.com/huangyc/p/10327209.html,并对代码进行了简介。朴素贝叶斯分类器训练函数包括求p(Ci)和基于词汇表的p(w|Ci)。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 本文介绍了Foundation框架中一些常用的结构体和类,包括表示范围作用的NSRange结构体的创建方式,处理几何图形的数据类型NSPoint和NSSize,以及由点和大小复合而成的矩形数据类型NSRect。同时还介绍了创建这些数据类型的方法,以及字符串类NSString的使用方法。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
author-avatar
康博洋2602899791
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有