基于redis实现滚动分页功能

需求是基于redis的zset集合将其中的数据以分页的形式查询,要求是查询过的数据不能重复查询到

基于redis zset集合实现滚动分页功能

新建个zset集合向里面添加些测试数据

常规分页基于索引查询page:1,size:5,page:2,size:5......存在危害

此时集合中有7条数据执行分页查询第一次查询查询到的的是id为13,12,11,10,9这几条数据

第二次查询为8,7两条,这是正常情况下分页查询到的数据

另一种情况

执行分页查询第一次查询查询到的的是id为13,12,11,10,9这几条数据,如果当前时间其他用户向集合中添加数据那么新数据会在最顶部新增一条id为14score为当前时间戳的数据,那么这条数据会被放到最上方第二次查询到的数据则会变成9,8,7三条9这条数据则重复查询到,在一些特定场景下这种情况是不允许发生的,为此我们需要实现滚动分页这种动态查询

基于stringRedisTemplate.opsForZSet()提供的reverseRangeByScoreWithScores方法可以实现滚动分页查询

内部需要传递5个参数分别为

key:redis数据的key值

min:最小值,根据score打分查询到的最小的边界

max:最大值,根据score打分查询到的最大的边界

offset:如果score值相同需要跳过的个数(如果score值相同那么还是会被查询到的,因为查询到的数据是根据max传输的值来进行查询的:比如最大值我传入10此时集合内score为10打分的数据存在两条,那么这两条都会被查询到,需要跳过几条传入数值即可)

count:相当于传统分页中的分页条数下方代码写死为2条,正常由前端传入

public R queryBLongOfFollow(Long max, Integer offset) {
        //获取当前用户id
        String userId = threadLocal.get().getId();
        //key
        String key = "all:follows:" + userId;
        //查询收件箱
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().
                reverseRangeByScoreWithScores(key, 0, max, offset, 2);
        if (typedTuples == null || typedTuples.isEmpty()) {
            return R.ok();
        }
        //笔记id集合
        ArrayList<String> listIds = new ArrayList<>();
        //记录最小的时间戳
        long minTime = 0;
        //offset最小的时间戳出现的个数,用来跳过相同分数避免重复数据查询
        int os = 1;
        for (ZSetOperations.TypedTuple<String> tuple : typedTuples) {
            //笔记id
            String blogId = tuple.getValue();
            listIds.add(blogId);
            //时间戳
            long time = tuple.getScore().longValue();
            if (minTime == time) {
                os++;
            } else {
                minTime = time;
                os = 1;
            }
        }
        //将笔记id集合以,拼接
        String idsStr = StrUtil.join(",", listIds);
        //按照顺序查询笔记集合
        List<Blog> blogList = query().in("id", listIds)
                .last("order by field(id," + idsStr + ")").list();
        for (Blog b : blogList) {
            isLikeBLong(b);
            if (b == null) {
                return R.error("查询失败");
            }
            User user = userService.query().eq("id", b.getUserId()).one();
            b.setUser(user);
        }
        ScrollResult result = new ScrollResult();
        result.setList(blogList);
        result.setMinTime(minTime);
        result.setOffset(os);
        return R.ok().data("result",result);
    }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于 Redis 实现分布式锁可以利用 Redis 的原子性操作和超时特性来实现。下面是一个基本的实现思路: 1. 获取锁:使用 Redis 的 SETNX 命令,如果指定的锁 key 不存在,则设置该 key 的值为当前时间戳加上锁的超时时间,并返回成功;否则,返回失败。 2. 释放锁:使用 Redis 的 EVAL 命令,通过 Lua 脚本来实现原子性的删除锁。脚本的内容是先判断锁是否存在且超时,如果是则删除锁并返回成功;否则,返回失败。 下面是一个简单的 Python 代码示例: ```python import redis import time class RedisLock: def __init__(self, redis_client, lock_key, expire_time): self.redis = redis_client self.lock_key = lock_key self.expire_time = expire_time def acquire(self): while True: timestamp = int(time.time() * 1000) + self.expire_time acquired = self.redis.set(self.lock_key, timestamp, nx=True, px=self.expire_time) if acquired: return True time.sleep(0.001) def release(self): lua_script = """ if redis.call("exists", KEYS[1]) == 1 then local current_value = tonumber(redis.call("get", KEYS[1])) if current_value and current_value <= tonumber(ARGV[1]) then return redis.call("del", KEYS[1]) end end return 0 """ self.redis.eval(lua_script, 1, self.lock_key, int(time.time() * 1000) + self.expire_time) # 使用示例 redis_client = redis.Redis(host='localhost', port=6379, db=0) lock = RedisLock(redis_client, 'my_lock', 1000) # 锁的超时时间为 1000 毫秒 if lock.acquire(): try: # 执行需要加锁的代码 pass finally: lock.release() ``` 需要注意的是,以上代码仅是一个简单的实现示例,实际使用中还需要考虑异常处理、锁的可重入性、锁的可拥有时间等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值