redis之zset在实际项目中的应用

背景
最近接到个需求,要求汇总用户一个月内在不同的供应商处购买的商品sku(上限1000条),去重后按照购买时间去倒序排序,当用户再次在对应的供应商处购买商品时,弹出之前的购买sku列表。
分析
这个需求,简化后就是要求对用户(User)和供应商(Supplier) 做一个唯一键,在这个唯一键上,按照时间倒序去汇总好去重后的(Sku)。略微思考,使用redis的zset可以比Mysql,或者es更为方便处理。zset本身就能实现去重的功能,且对于同一元素进行重复赋值,会更新score;排序可以通过使用购买时间的timestamp作为score 来实现;保证一个唯一键只有一个月内1000条历史数据,可以通过zremrangebyscore和zremrangebyrank两个zset自带方法实现。
核心代码

简化后的商品sku购买历史数据库DO

public class SccPurchaseDetailDO implements Serializable {
        /**
         * ID
         */
        private Long id;

        /**
         * 订单号
         */
        private String bizOrderNo;

        /**
         * 商品行ID, 同一个订单号, 可以采购多条sku, itemId用于区分该订单号下不同sku购买排序
         */
        private Integer itemId;

        /**
         * 二级类目id
         */
        private Long skuId;

        /**
         * 创建时间
         */
        private Date gmtCreate;
    }

简化后的zset缓存计算逻辑

/**
     * 创建zset的key - 含供货商信息
     *
     * @param userId 用户id
     * @param supplierEntId 供应商企业id 
     * @return
     */
    private String makePayeeZSetKey(Long userId, Long supplierEntId) {
        String key = userId + "t" + supplierEntId;
        return RedisConstance.PURCHASE_DETAIL_ITEM_STATISTIC_ZSET_KEY + Md5Utils.getMD5(key.getBytes());
    }

    /**
     * 将购买记录存入zset
     * 
     * 对于同一个批次的购买记录, 支付时间是相同的
     * @param detailList  在一次购买中,所有sku购买记录历史列表
     * @param userId 购买人
     * @param supplierEntId 供应商企业id
     * @param paidTime 此次购买的支付时间
     */
    private void handlePurchaseRecord(List<SccPurchaseDetailDO> detailList, Long userId, Long supplierEntId, Date paidTime) {
        if (detailList.isEmpty()) {
            return;
        }

        String payeeKey = makePayeeZSetKey(userId, supplierEntId);

        long finishTime = paidTime.getTime();

        Set<ZSetOperations.TypedTuple<String>> tuples = new HashSet<>();
        for (SccPurchaseDetailDO d : detailList) {
            // itemId 从1-999, 代表此次购买中, 不同sku先后购买顺序
            // 这里留了3个位给同一个购买批次中的不同sku
            double score = finishTime * 1000 + d.getItemId() + 0.0D;
            ZSetOperations.TypedTuple<String> r = new DefaultTypedTuple<>(d.getSkuId().toString(), score);
            tuples.add(r);
        }
        
        redisTemplate.opsForZSet().add(payeeKey, tuples);
        // 防止那些就买一次的人, 数据一直存在redis中
        redisTemplate.expire(payeeKey, 30, TimeUnit.DAYS);
        // 删除过期数据
        redisTemplate.opsForZSet().removeRangeByScore(payeeKey, 0, oneMonthAgo * 1000 + 999);
        Long size = redisTemplate.opsForZSet().zCard(payeeKey);
        if (size != null && size > maxSize) {
            // 排序是时间越小的 越前面
            redisTemplate.opsForZSet().removeRange(payeeKey, 0, size - maxSize);
        }
    }

简化后的数据获取代码

// score是按照时间戳去赋值的
// 按照时间从近到远, 获取对应条数的sku采购历史
redisTemplate.opsForZSet().reverseRange(makePayeeZSetKey(request), start, end)

总结
redis的zset,在进行一些去重排序的工作时,是非常有用的,特别是对一个相同的zset中的元素,重复赋值,会更新score,省去了我们对于元素是否已经存在的判断。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Redis ZSet(Sorted Set)是一种带权重的有序集合。在 Redis ,每个元素都有一个分数,用于排序。ZSet 的成员是唯一的,但分数(score)可以相同。 ZSet 的底层实现使用了跳跃表(skip list),这是一种用于快速排序的数据结构。因此,ZSet 可以在常数时间内执行插入、删除和查找操作。 你可以使用 ZSet 来实现排行榜、消息时间线等功能。它还可以用于范围查询,例如查找排名前 10 的用户。 ### 回答2: Redis的ZSET(有序集合)是一种无序、不能重复,但其每个元素都与一个浮点数值相关联的数据结构。 ZSET可以理解为一种特殊的哈希表,其每个元素都有一个分数(score)与之对应。这个分数可以用来对元素进行排序,并且允许根据分数范围进行查询。 ZSET的元素不重复,即每个元素是唯一的,但可以具有相同的分数。元素的值(value)可以是任意不重复的字符串。 ZSET的常见操作包括: 1. 添加元素:使用ZADD命令向ZSET添加一个或多个元素,可以同时指定元素的分数。 2. 删除元素:使用ZREM命令从ZSET删除指定的元素。 3. 修改元素分数:使用ZINCRBY命令为指定元素的分数增加或减少一个给定的值。 4. 查询元素排名:使用ZRANK或ZREVRANK命令查询指定元素在ZSET的排名。其ZRANK按照分数从低到高排序,而ZREVRANK按照分数从高到低排序。 5. 查询元素分数:使用ZSCORE命令查询指定元素的分数。 6. 查询元素范围:使用ZRANGEBYSCORE或ZREVRANGEBYSCORE命令查询分数在指定范围内的元素。可以指定返回的元素数量、是否返回分数等参数。 ZSET常用于需要排序和排名的场景,比如排行榜、热门文章列表等。由于ZSET底层使用了跳跃表和哈希表,所以在插入和删除操作上具有较高的性能。 需要注意的是,由于ZSET的实现和一般的哈希表不同,所以ZSET的插入、删除和查询操作的时间复杂度都是O(log N)。 ### 回答3: Redis的有序集合(zset)是一种特殊的数据结构,它是一个无序的字符串集合,每个字符串都关联一个浮点数作为分值。在有序集合,每个字符串元素都有一个唯一的分值,通过分值进行排序。通过分值的排序,有序集合可以按照从小到大或从大到小的顺序进行迭代。 Redis的有序集合是一个双层结构,一层用字典(hash)结构存储元素和分值的对应关系,另一层用跳跃表(skiplist)实现有序性。跳跃表是一种有序的链表结构,通过建立多层索引节点提高查找效率,因此在有序集合,插入、删除、查找元素的时间复杂度都是O(logN)。 有序集合在实际应用具有广泛的用途。例如,可以将有序集合用作排行榜,将玩家分数作为分值,根据分值对玩家进行排名。另外,有序集合还可以用于处理范围查询问题,例如查询某个分值范围内的元素。 Redis提供了丰富的命令操作有序集合,可以实现插入元素、删除元素、修改元素的分值,以及根据分值范围查询元素等操作。例如,通过ZADD命令可以向有序集合插入元素,通过ZREM命令可以删除元素,通过ZINCRBY命令可以修改元素分值,通过ZRANGE命令可以按照分值范围查询元素。 总之,Redis的有序集合(zset)是一种强大的数据结构,通过分值的排序和跳跃表的实现,可以高效地实现元素的插入、删除、修改和查询等操作,并且在实际应用可以发挥重要的作用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值