zset是一个排序集合,我主要用来给用户进行排名,以及对一个指定区间的数据进行统计,可以用来替代mysql中between and语句 ,列举几个场景如何利用zset解决需求
业务场景:用户每天都有刷牙数据产生,刷牙数据包括刷牙时长,刷牙时间,刷牙分数
需求1 : 根据每天每个用户的最高分数进行排名
需求2:运营活动根据每天每个用户的最高分数进行排名,如果分数一样则根据坚持天数进行排名
需求3:统计近7天单个用户是刷牙数据,并且显示给用户刷牙平均分,刷牙时长平均分,有多少天没有坚持每天刷牙两次。
需求1:根据每天每个用户的最高分数进行排名
每天新建一个zset,当有用户刷牙数据过来,则根据用户的id在zset中通过zscore命令查找当前用户是否存在记录,如果不存在 当前用户记录,则直接用zadd命令添加新记录,value为用户id,score为刷牙分数 ,如果存在记录,则比较当前记录的分数与zset中的记录哪个更大,把大的score插入到当前用户id所对应的zset中即可。
当用户查看刷牙记录排行时,直接通过zRevRangeWithScores命令即可根据分数从大到小的显示用户的排行情况。如果希望知道某个用户的排名,则可根据用户id,查找出用户分数,通过zset.count(zsetkey, userScore + 0.01, topScore) + 1来算出用户的排名,这个逻辑是给用户的分数加一个0.01,然后计算出最大值与当前值+0.01之间有多少个用户,得到的人数+1即为用户名次。 此条语句为java中的写法,redis中应该使用zcount
需求2:运营活动根据每天每个用户的最高分数进行排名,如果分数一样则根据坚持天数进行排名
兼顾分数与天数
对于计算坚持天数,我们需要利用redis的set来完成,当用户添加一条刷牙记录时,使用一个set来记录用户的坚持日期,比如20161027 这样,把这个记录加入到以用户id为组合key的set中,然后通过set的scard命令(springdata中是size)来计算用户的坚持天数。这样做的好处:1.set可以过滤重复的日期 ,每条记录过来只要无脑插入即可 2:如果某运营活动需要统计某个时间段内的用户坚持天数,可新建一个set存储运营活动的所有日期,然后使用set的交集运算intersect来算出交集,并统计元素个数,得出的数据即为运营活动总用户的坚持天数。
得到坚持天数insistday后,对应需求1中用户的刷牙分数,我们做一个扩大化,比如*1000,前提是用户的坚持天数不会超过999,每次插入用户数据到zset的时候,把用户的分数score*1000+insistday。这样通过zRevRangeWithScores命令既可得到根据用户分数排名,再根据坚持天数排名的排行榜
需求3:统计近7天单个用户是刷牙数据,并且显示给用户刷牙平均分,刷牙时长平均分,有多少天没有坚持每天刷牙两次。
思路同需求2,在zset的score上做文章。把score做成一个同时记录日期,分数,时长的double型数据,分成16位 :前10位把时间转化成yyMMddHHmm的字符型,中间三位记录刷牙分数 000,注意务必占满3位,不够的话用0替代,最后三位记录坚持时长,三位数字转化成字符串表示,最后将这10+3+3个字符串拼装起来,转化为double型,记录到每个用户的zset中即可。
这样当统计7天的数据时,只有算出第一天和第七天的日期,将其转化为begindate:yyMMdd0000000000 endDate:yyMMdd2359999999,利用zset的rangeByScoreWithScores命令
zset.zRangeByScoreWithScores(zsetkey,begindate, mendDateax)即可得到中间的所有数据
private Set<TypedTuple<Object>> getUserRecordBetweenDate(Integer userId,
Date beginDate, Date endDate) {
String beginStr = DateUtil.dateToStr(beginDate, "yyMMdd");
String endStr = DateUtil.dateToStr(endDate, "yyMMdd");
StringBuffer sbbegin = new StringBuffer();
sbbegin.append(beginStr);
sbbegin.append("0000");// 时分
sbbegin.append("000");// 分数
sbbegin.append("000");// 刷牙时间
Double min = Double.valueOf(sbbegin.toString());
StringBuffer sbend = new StringBuffer();
sbend.append(endStr);
sbend.append("2359");
sbend.append("100");
sbend.append("999");
Double max = Double.valueOf(sbend.toString());
String zsetkey = Constant.RedisZsetEnum.USERALLRECORD.getKey() + userId;
ZSetOperations<String, Object> zset = redisService
.getZsetRedisTemplate();
Set<TypedTuple<Object>> userZset = zset.rangeByScoreWithScores(zsetkey,
min, max);
return userZset;
然后对score转化成string型,利用substring(10,13),substring(13,16)得到刷牙分数和刷牙时长,对其求和,再除以条数即位平均数。
对应没有坚持两次的天数统计:循环遍历开始日期到结束日期组成的列表,利用rangeByScoreWithScores命令把begindate与endDate中的yyMMdd换成那一天的日期即可,如果得到的结果个数<2则将统计的天数+1
需要注意的问题:
1.在zset中声明的类型与调用的类型一定要统一。比如声明的zset中value的值为String,那么如果调用的时候哪怕你知道用户id为Integer型12345,也要将其转化为String才能调用,否则是得不到数据的
2.在需求三中实际上有个漏洞,如果数据是当天23:59之后的时间,其实最后的秒数是被忽略了。一个是业务的考虑,不需要太顾及,另外一个就是在zset的score中,对于double型的数据,如果太长,超过了17位,则其采用科学计数法记录的数据会有误差,后面的数据就记不住了,对于我们的需求中,需要一个数据同时完成时间,得分,时长的记录,所以就对score的形式进行了缩减,把年的前两位减掉,把日期的秒数减掉,以保证16位的长度。另外在获取score的时候,得到的是科学计数法的数据,我通过这段代码将其转化为string
public static String getStringFromDouble(Double d){
DecimalFormat format = new DecimalFormat("#");
String a = format.format(d);
return a;
}[size=large][/size]
分享到:
相关推荐
Redis开发环境配置 Redis是一个完全开源免费的key-value数据库,遵守BSD协议,具有高性能。Redis与其他key-value缓存...通过本文的讲解,希望读者能够快速搭建Redis开发环境,并掌握Redis的基本使用方法和配置技巧。
3. **使用压缩数据结构**:如上所述,Redis提供了多种压缩数据结构选项,合理配置可以有效减少内存使用。 #### 四、持久化策略与优化 1. **RDB与AOF的选择**:根据实际需求选择适合的持久化方式。RDB适合快速重启...
### Redis命令实践与技巧解析 #### 一、Redis概述及应用场景 Redis是一种开源的、高性能的键值存储系统,以其高速读写能力、丰富的数据结构和广泛的用途而著称。它支持多种数据结构,如字符串(strings)、哈希...
### Redis进阶实践之十四:Redis-cli命令行工具使用详解(第一部分) #### 工具概述 ##### Redis-cli 简介 Redis-cli 是 Redis 官方提供的一...随着实践经验的积累,开发者将会发现 Redis-cli 更多隐藏的功能和技巧。
源码分析对于深入理解其工作原理和优化技巧至关重要。本文件提供的"redis源码"是基于IntelliJ IDEA开发环境的,包含了完整的Redis源代码,涵盖了切片、非切片以及数据源等关键组件。 首先,Redis的核心设计是基于单...
在面试中,对Redis的考察往往涉及对基础知识的掌握、实际应用能力以及问题解决技巧。这些面试题可能是对候选人的技术深度和广度的测试,也可能是对其实际工作经验的一个侧面反映。 由于文件内容没有具体展开,我们...
在这个“redis-5.0.4”源码包中,我们可以深入理解Redis的工作原理,优化技巧以及扩展功能的实现。 首先,Redis的核心架构基于单线程模型。在5.0.4版本中,它依然保持了这一特性。通过非阻塞I/O多路复用技术(如...
【Redis教程从基础到高级,图形和代码结合,方便理解】 Redis是一款高性能的键值对数据库,...同时,结合图形和代码的解释,可以帮助学习者更直观地掌握Redis的使用技巧,从而在实际项目中更好地运用这个强大的工具。
内容概要:本文主要介绍了Redis大Key的概念及其产生的原因,并深入探讨了检测大Key的方法以及不同类型大Key的安全删除技巧。具体而言,针对String、Hash、List、Set和ZSet等不同数据结构给出了相应的处理策略,同时...
在开发过程中,还可以参考ThinkPHP的官方文档和其他相关教程,如《ThinkPHP入门教程》、《thinkPHP模板操作技巧总结》、《ThinkPHP常用方法总结》等,这些资源可以帮助你更好地掌握ThinkPHP框架和PHP编程。...
同时,利用RabbitMQ来处理用户搜索行为,将高频词汇存储到Redis的有序集合(ZSet)中,便于后台管理系统审核并更新词库。增量数据的同步则通过自定义的Canal Starter来实现。 此外,支付功能的设计也是项目的关键...
接着讲解了积分排行榜的实现,利用Redis的ZSet结构提高查询效率并保持实时性。同时强调了系统安全性的多个方面,如防止XSS攻击、敏感词过滤以及权限控制机制。此外,还分享了一些实用技巧,如文件下载时避免内存溢出...
8. **数据结构**:Redis中的ZSET实现、链表操作、Map底层原理,理解并能灵活运用各种数据结构。 9. **Java虚拟机(JVM)**:GC(垃圾收集)机制、CMS收集器及其参数、JVM监控工具(jstat、jstack、jmap)的使用,...