Redis zset实现多条件排序思路

该博客探讨了如何使用Redis的ZSet数据结构来实现多维度排序,包括用户积分、会员身份和最后登录时间。通过二进制拆分或直接拆分十进制数的方法,将积分、会员状态和时间戳合并成一个score存储,从而实现复杂排序。这种方法在数据量不大时是可行的,并可以根据业务需求调整各维度长度。
摘要由CSDN通过智能技术生成

#需求

某应用用户排行规则:
第一排行维度:用户积分;(积分越高,排行越靠前)
第二排行维度:用户是否为会员;(积分相同时,会员排前面)
第三排行维度:用户最近一次登录时间;(前两个维度值相同时,用户最后一次登录时间越近越靠前)
以下是原始数据:

用户  积分  是否会员  最近一次登录时间戳
A    100  	1       1612754184997
B    200  	0       1612754184997
C    200  	1       1612754184997
D    400  	0       1612754184997
E    200  	1       1612754184998

期望的排序结果:

用户  积分  是否会员  最近一次登录时间戳
D    400  	0       1612754184997
E    200  	1       1612754184998
C    200  	1       1612754184997
B    200  	0       1612754184997
A    100  	1       1612754184997

#使用redis的zset来实现三个维度排序的思路

#score 存储格式

Redis的zset的score是用 double 存储的,
一个 64 位浮点数存储时分为3段:

  • 符号位(Sign) : 0代表正,1代表为负;(第一段,占1位)
  • 指数位(Exponent):用于存储科学计数法中的指数数据;(第二段,占11位)
  • 尾数部分(Mantissa):尾数部分;(第三段,占52位)

在这里插入图片描述

double 存储的有效数据是第三段的52位,超出会损失精度,由于标准规定小数点是在有效数字最前面,所以实际可以存储 53 位数字。

#通过“二进制拆分”,将三个维度合并生成score

利用二进制 64 位 long 分段存储各个维度

首先时间戳如果全存储就太长了,可以通过一些计算缩小一些,先忽略毫秒,然后和一个大数计算差值。

long ts = 1609430400000;//2021.1.1 0:0:0
int MAX_SECOND = 1893427200; // 2030.1.1 0:0:0
int sts = (MAX_SECOND - (int)(ts / 1000));// 283996800

这样时间就缩小到了300000000以内。

接下来进行划分,首位标志位不用,剩下63位,然后我划分高33位存分数,1位存标志位,最后29位存时间戳,存储结构是这样的:
在这里插入图片描述
如果要不损失精度,第一段可存储位数 = 53 - 1 - 29 = 23;
第一段最大值 = 2^33 = 8589934591,不损失精度 = 2^23 = 8388607;
第三段最大值 = 2^29 = 536870911;

使用时可以适当缩短第三段时间戳的长度,或者不追求时间戳一定精确的话,第一段分数可以超出不损失精度的长度,也只会损失一点时间戳的精度而已。

具体生成score的代码就不列举了。

#直接拆分十进制数

可以用二进制拆分当然也可以直接拆分十进制数,为了方便,还可以用小数划分维度,比如将积分放在整数位,标志位和时间戳放在小数位。

A    100.11612754184
B    200.01612754184
C    200.11612754184
D    400.01612754184
E    200.11612754184

不过这样不丢精度存储的分数比二进制拆分小。

#总结

数据量不大的情况下直接使用 读数据库 就可以了,比较方便。用 Redis 的时候,如果排序维度多,可以使用拆分二进制或十进制的方法存储,二进制的优点是存储的数比较大,而且可以用位运算,十进制的优点是计算简单,可读性比较好。各个维度的长度还可以做成配置项,这样就可以满足不同的业务需求了。

#参考

redis zset支持多条件排序


Redis的有序集合(zset)可以实现排序。在有序集合中,每个成员都关联一个分数(score),根据分数对成员进行排序。成员可以是唯一的,但分数可以重复。 对于排序,可以使用以下命令: - ZADD:将成员添加到有序集合中,并指定其分数。 - ZRANGE:根据分数范围获取有序集合中的成员。 - ZREVRANGE:根据分数范围获取逆序排列的有序集合中的成员。 - ZRANGEBYSCORE:根据分数获取有序集合中的成员。 - ZREVRANGEBYSCORE:根据分数获取逆序排列的有序集合中的成员。 例如,使用以下命令可以将成员添加到有序集合中并按分数排序: ``` ZADD myzset 1 member1 ZADD myzset 2 member2 ZADD myzset 3 member3 ``` 使用以下命令可以获取有序集合中的成员: ``` ZRANGE myzset 0 -1 ``` 结果将按照分数的升序返回: ``` 1) "member1" 2) "member2" 3) "member3" ``` 如果需要逆序排序,可以使用ZRANGEBYSCORE命令: ``` ZREVRANGE myzset 0 -1 ``` 结果将按照分数的降序返回: ``` 1) "member3" 2) "member2" 3) "member1" ``` 所以,通过使用有序集合的分数来进行排序,可以满足你的需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [redis使用zset实现数据库多字段排序的一种方式](https://blog.csdn.net/Prf_Nie/article/details/126305716)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Redis使用篇 - zset](https://blog.csdn.net/qq_34561892/article/details/108710957)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值