Redis 分页排序查询

2023-05-16

Redis是一个高效的内存数据库,它支持包括String、List、Set、SortedSet和Hash等数据类型的存储,在Redis中通常根据数据的key查询其value值,Redis没有条件查询,在面对一些需要分页或排序的场景时(如评论,时间线),Redis就不太好不处理了。

前段时间在项目中需要将每个主题下的用户的评论组装好写入Redis中,每个主题会有一个topicId,每一条评论会和topicId关联起来,得到大致的数据模型如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  

{
    topicId: 'xxxxxxxx',
    comments: [
        {
            username: 'niuniu',
            createDate: 1447747334791,
            content: '在Redis中分页',
            commentId: 'xxxxxxx',
            reply: [
                {
                    content: 'yyyyyy'
                    username: 'niuniu'
                },
                ...
            ]
        },
        ...
    ]
}
  

将评论数据从MySQL查询出来组装好存到Redis后,以后每次就可以从Redis获取组装好的评论数据,从上面的数据模型可以看出数据都是key-value型数据,无疑要采用hash进行存储,但是每次拿取评论数据时需要分页而且还要按createDate字段进行排序,hash肯定是不能做到分页和排序的。

那么,就挨个看一下Redis所支持的数据类型:

1、String: 主要用于存储字符串,显然不支持分页和排序。
2、Hash: 主要用于存储key-value型数据,评论模型中全是key-value型数据,所以在这里Hash无疑会用到。
3、List: 主要用于存储一个列表,列表中的每一个元素按元素的插入时的顺序进行保存,如果我们将评论模型按createDate排好序后再插入List中,似乎就能做到排序了,而且再利用List中的LRANGE key start stop指令还能做到分页。嗯,到这里List似乎满足了我们分页和排序的要求,但是评论还会被删除,就需要更新Redis中的数据,如果每次删除评论后都将Redis中的数据全部重新写入一次,显然不够优雅,效率也会大打折扣,如果能删除指定的数据无疑会更好,而List中涉及到删除数据的就只有LPOP和RPOP这两条指令,但LPOP和RPOP只能删除列表头和列表尾的数据,不能删除指定位置的数据,所以List也不太适合
(转载的时候看了下,是有 LREM命令可以做到删除,但是LRANGE 似乎是一个耗时命令 O(N) )
4、Set: 主要存储无序集合,无序!排除。
5、SortedSet: 主要存储有序集合,SortedSet的添加元素指令ZADD key score member [[score,member]…]会给每个添加的元素member绑定一个用于排序的值score,SortedSet就会根据score值的大小对元素进行排序,在这里就可以将createDate当作score用于排序,SortedSet中的指令ZREVRANGE key start stop又可以返回指定区间内的成员,可以用来做分页,SortedSet的指令ZREM key member可以根据key移除指定的成员,能满足删评论的要求,所以,SortedSet在这里是最适合的(时间复杂度O(log(N))

所以,我需要用到的数据类型有SortSet和Hash,SortSet用于做分页排序,Hash用于存储具体的键值对数据,我画出了如下的结构图:

      在上图的SortSet结构中将每个主题的topicId作为set的key,将与该主题关联的评论的createDate和commentId分别作为set的score和member,commentId的顺序就根据createDate的大小进行排列。
      当需要查询某个主题某一页的评论时,就可主题的topicId通过指令zrevrange topicId (page-1)×10 (page-1)×10+perPage这样就能找出某个主题下某一页的按时间排好顺序的所有评论的commintId。page为查询第几页的页码,perPage为每页显示的条数。
当找到所有评论的commentId后,就可以把这些commentId作为key去Hash结构中去查询该条评论对应的内容。
这样就利用SortSet和Hash两种结构在Redis中达到了分页和排序的目的。


博主额外添加的实现算法:

@Test
	public void sortedSetPagenation(){
		for  ( int  i =  1 ; i <=  100 ; i+=10) {  
			// 初始化CommentId索引 SortSet
			RedisClient.zadd("topicId", i, "commentId"+i);
			// 初始化Comment数据 Hash
			RedisClient.hset("Comment_Key","commentId"+i, "comment content .......");
		}  
		// 倒序取 从0条开始取 5条 Id 数据
		LinkedHashSet<String> sets = RedisClient.zrevrangebyscore("topicId", "80", "1", 0, 5);
		String[] items = new String[]{};
		System.out.println(sets.toString());
		// 根据id取comment数据
		List<String> list = RedisClient.hmget("Comment_Key", sets.toArray(items));
		for(String str : list){
			System.out.println(str);
		}
	}
工具类:
package com.util;

import java.util.LinkedHashSet;
import java.util.List;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Redis 客户端集群版
 *
 * @author babylon
 * 2016-5-10
 */
public class RedisClient{
	
	private static  JedisPool jedisPool;
	
	static {
		JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(Global.MAX_ACTIVE);   
        config.setMaxIdle(Global.MAX_IDLE);
        config.setMaxWaitMillis(-1);
        config.setTestOnBorrow(Global.TEST_ON_BORROW);
        config.setTestOnReturn(Global.TEST_ON_RETURN);
        jedisPool = new JedisPool("redis://:"+Global.REDIS_SERVER_PASSWORD+"@"+Global.REDIS_SERVER_URL+":"+Global.REDIS_SERVER_PORT);
//		jedisPool = new JedisPool(config, Global.REDIS_SERVER_URL,  Integer.parseInt(Global.REDIS_SERVER_PORT), "zjp_Redis_224");
	}

	public static  String set(String key, String value) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.set(key, value);
		jedis.close();
		return result;
	}

	public static  String get(String key) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.get(key);
		jedis.close();
		return result;
	}

	public static Long hset(String key, String item, String value) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.hset(key, item, value);
		jedis.close();
		return result;
	}

	public static  String hget(String key, String item) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.hget(key, item);
		jedis.close();
		return result;
	}
	
	/**
	 * Redis Hmget 命令用于返回哈希表中,一个或多个给定字段的值。
		如果指定的字段不存在于哈希表,那么返回一个 nil 值。
	 * @param key
	 * @param item
	 * @return 一个包含多个给定字段关联值的表,表值的排列顺序和指定字段的请求顺序一样。
	 */
	public static  List<String> hmget(String key, String... item) {
		Jedis jedis = jedisPool.getResource();
		List<String> result = jedis.hmget(key, item);
		jedis.close();
		return result;
	}

	public static  Long incr(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.incr(key);
		jedis.close();
		return result;
	}

	public static  Long decr(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.decr(key);
		jedis.close();
		return result;
	}

	public static Long expire(String key, int second) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.expire(key, second);
		jedis.close();
		return result;
	}

	public static Long ttl(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.ttl(key);
		jedis.close();
		return result;
	}

	public static Long hdel(String key, String item) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.hdel(key, item);
		jedis.close();
		return result;
	}

	public static Long del(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.del(key);
		jedis.close();
		return result;
	}
	
	public static Long rpush(String key, String... strings) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.rpush(key, strings);
		jedis.close();
		return result;
	}

	/**
	 * Redis Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素, 1 表示列表的第二个元素,以此类推。 
	 * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
	 * @param string
	 * @param start
	 * @param end
	 * @return
	 */
	public static List<String> lrange(String key, int start, int end) {
		Jedis jedis = jedisPool.getResource();
		List<String> result = jedis.lrange(key, start, end);
		jedis.close();
		return result;
	}

	/**
	 * 从列表中从头部开始移除count个匹配的值。如果count为零,所有匹配的元素都被删除。如果count是负数,内容从尾部开始删除。
	 * @param string
	 * @param string2
	 * @param i
	 */
	public static Long lrem(String key, Long count, String value) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.lrem(key, count, value);
		jedis.close();
		return result;
	}

	/**
	 * Redis Zadd 命令用于将一个或多个成员元素及其分数值加入到有序集当中。
		如果某个成员已经是有序集的成员,那么更新这个成员的分数值,并通过重新插入这个成员元素,来保证该成员在正确的位置上。
		分数值可以是整数值或双精度浮点数。
		如果有序集合 key 不存在,则创建一个空的有序集并执行 ZADD 操作。
		当 key 存在但不是有序集类型时,返回一个错误。
	 * @param string
	 * @param i
	 * @param string2
	 * @return 被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员。
	 */
	public static Long zadd(String key, double score, String member) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.zadd(key, score, member);
		jedis.close();
		return result;
	}
	
	/**
	 * Redis Zrevrangebyscore 返回有序集中指定分数区间内的所有的成员。有序集成员按分数值递减(从大到小)的次序排列。
		具有相同分数值的成员按字典序的逆序(reverse lexicographical order )排列。
		除了成员按分数值递减的次序排列这一点外, ZREVRANGEBYSCORE 命令的其他方面和 ZRANGEBYSCORE 命令一样。
	 * @param key
	 * @param max
	 * @param min
	 * @param offset
	 * @param count
	 * @return 指定区间内,带有分数值(可选)的有序集成员的列表。
	 */
	public static LinkedHashSet<String> zrevrangebyscore(String key, String max, String min, int offset, int count){
		Jedis jedis = jedisPool.getResource();
		LinkedHashSet<String> result = (LinkedHashSet<String>) jedis.zrevrangeByScore(key, max, min, offset, count);
		jedis.close();
		return result;
	}

}



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis 分页排序查询 的相关文章

  • StackDocklet 完美攻略

    爱美之心 xff0c 人皆有之 相信很多朋友和我一样 xff0c 都很喜欢个性化一下自己的计算机 下面两张图是笔者的笔记本电脑的桌面截图 图1 xff1a 以Fan方式显示 图2 xff1a 以Grid方式显示 在上面两张图片中 xff0c
  • [Android] ADT关联源码与API文档 [2013-06-26更新]

    注意 xff1a 进行下述操作前请确保JDK已正确安装并设置环境变量 1 下载最新版ADT 例如 xff1a adt bundle windows x86 64 20130522 zip 官网下载 xff1a http developer
  • 基于管道的进程间数据传输

    实验原理图 xff1a 具体代码如下 xff1a span class token macro property span class token directive keyword include span span class toke
  • Linux-rpm安装MySQL

    目录 一 概述 二 下载 三 安装 四 卸载 五 常用设置 1 修改 root 用户密码 xff08 密码效验规则 xff09 2 创建用户与权限分配 六 可能遇到的问题 一 概述 MySQL是一种关系型数据库 xff08 存放数据库的仓库
  • hugging face 模型库的使用及加载 Bert 预训练模型

    一 hugging face 模型库 Hugging face 是一个专注于 NLP 的公司 xff0c 拥有一个开源的预训练模型库 Transformers xff0c 里面囊括了非常多的模型例如 BERT GPT GPT2 ToBERT
  • 【手把手教程】若依微服务版服务器部署

    若依微服务版服务器部署 xff08 手把手教程 xff09 x1f4d4 笔记介绍 大家好 xff0c 这里是千寻简笔记 xff0c 我是作者星辰 xff0c 笔记内容整理并发布 xff0c 内容有误请指出 xff0c 笔记源码已开源 xf
  • 两分钟彻底让你明白Android中onInterceptTouchEvent与onTouchEvent(图文)!

    Hi 大家好 xff0c 今天给大家分享一下Android中onInterceptTouchEvent与onTouchEvent 记得楼主以前刚开始找工作的时候 xff0c 被人问了关于Android中事件传递 xff0c 当时还是菜鸟一枚
  • GCC中的pie和fpie选项

    http www lingcc com 2010 01 08 10609 GCC中的pie和fpie选项 Position Independent Executable是Binutils glibc和gcc的一个功能 xff0c 能用来创建
  • Debian中文美化

    对于使用X Window的用户而言 xff0c 拥有一个漂亮的界面是非常重要的 xff0c 只有这样 xff0c 工作的时候才能心情娱悦 xff0c 否则 xff0c 看着大小不一或者是朦朦胧胧的字体 xff0c 恐怕没人会有心情做自己的工
  • ESLint 命令行

    要在Node js 上运行 ESLint xff0c 必须先安装 npm 安装好npm后 xff0c 可以运行下列命令 xff1a npm i g eslint 这表示从npm仓库安装了ESLint CLI 然后继续使用下列命令运行ESLi
  • Ubuntu18.04安装Hexo

    最近突然想搞一个自己的博客 xff0c 于是乎准备在Ubuntu上安装一下Hexo框架 xff0c 然而很不幸的是遇到了重重险阻 xff0c 踩了不少坑 xff0c 最后能艰难爬上来实属不易 xff0c 下面就给大家奉上我的 脚印 xff0
  • 使用PackageManager来获取应用信息

    PackageManager可以获取到一个APP的各项信息 xff0c 如包名 版本号 图标等信息 获取PackageManager对象的方法很简单 xff1a PackageManager packageManager 61 contex
  • activemq 动态消息队列

    前提 xff1a springboot整合activemq时利用注解及相关 配置类是可以直接 使用 activemq消息队列去处理业务逻辑的 xff0c 但是我在生产环境碰到了一个 情况是 真实数据 有几千万 直接用上面说的情况 xff0c
  • Springboot + Spring Security 实现前后端分离登录认证及权限控制

    Springboot 43 Spring Security 实现前后端分离登录认证及权限控制 前言本文主要的功能文章目录文章正文一 准备工作1 统一错误码枚举2 统一json返回体3 返回体构造工具4 pom5 配置文件 二 数据库表设计建
  • JVM内存分配规则

    JVM内存分配规则 堆内存分配 xff1a JDK8 默认的 xff0c 新生代 xff08 Young xff09 与老年代的比例值为 1 2 xff08 该值可以通过参数 XX NewRatio来指定 xff09 新生代分为 Eden和
  • RT-Thread 开发者能力认证考试样题(RCEA)

    V1 0 RT Thread 开发者能力认证考试样题 RCEA xff09 注 xff1a 样题仅为部分题目 第一小卷 单选题 40 题 40 分钟 40 0 分 1 对两个字符 a 和 b 进行初始化 xff1a char a 61 34
  • Collections.max(list) 和 Math.max() 效率比较实验

    前情提要 xff1a 在力扣 559 N叉树的最大深度 中 xff0c 官方题解出现了 Collections max 的用法 xff0c 本人使用的是 Math max 这一古老而强大的函数 为什么官方答案选用的是 list 呢 xff1
  • (原创)Lottie动画使用介绍

    前言 我们都知道 xff0c 安卓里面有三种基本的播放动画方式 xff1a 帧动画 xff0c 属性动画 xff0c 补间动画 今天介绍一种新的实现方式 xff1a Lottie动画 Lottie是爱彼迎开源的一个动画框架 可以支持iOS
  • 【JavaAgent】字节码编程 - 使用Byte-buddy工具实现插件化开发

    简介 假设我们有需求 xff1a 所有的控制器 xff08 Controller xff09 添加耗时 xff0c 对所有的服务 xff08 Service xff09 添加调用信息 xff0c 使用一个agent代理 xff0c 也就是所
  • onNewIntent详解

    记得刚开始学的时候 xff0c 感觉intent还挺简单 xff0c 结果现在总是被intent困扰 xff0c 发现自己太天真了 xff0c 为了弄清楚 xff0c 总结一下 xff0c 一 onNewIntent 老规矩 xff0c 先

随机推荐

  • import _ssl ImportError: DLL load failed

    问题描述 简单讲一下我的问题 xff1a 使用pycharm打开Django项目 xff08 虚拟环境编译器 xff09 xff0c 当测试发送邮件功能时 xff0c 出现报错 xff0c import ssl if we can t im
  • 【荐书】李彦宏《智能革命》:技术是时代的信仰

    我来了 xff0c 天上的云乘着风飞翔 xff0c 心中的梦占据一个反向 xff0c 方舟扬帆起航 xff0c 一路带着我们纵情歌唱 xff0c 方舟扬帆起航 xff0c 脉络就在大海之上 xff0c 进步的时光 xff0c 迎着你看涛浪潮
  • vue 打包去除console.log()

    忘记在哪里看到的 记录下 在vue config js中 xff0c 配置 chainWebpack span class token punctuation span config span class token punctuation
  • minio+thumbor

    minio 安装 span class token function wget span https dl min io server minio release linux amd64 minio span class token fun
  • axios自定义请求头跨域,thinkphp设置

    axios添加自定义请求头 在axios拦截器中 headers XXX 61 XX 配置后 thinkphp 返回跨域 需要对thinkphp中间件进行重新设置 主要配置在tp 复制 vendor topthink framework s
  • git webhook linux 切换www用户

    vim etc passwd 把 www x 1000 1000 home www sbin nologin 改成 www x 1000 1000 home www bin bash ESC键结束 wq 保存文件并退出编辑 切换www用户
  • mint下nginx和uwsgi部署django

    参考 xff1a http www cnblogs com fnng p 5268633 html 安装Nginx apt get install nginx 启动Nginx xff1a etc init d nginx start 启动
  • (原创)Android安装APK报错Installation failed due to: ‘INSTALL_FAILED_TEST_ONLY‘

    这个问题遇到是在vivo手机与oppo手机 xff0c 换成在华为 小米等手机中未发现该问题 其实是因为Android Studio 自3 0 开始自带一个隐藏的功能 xff0c 那就是编译打包 debug 包时 xff0c 会自动在 ap
  • strapi-api使用说明

    strapi controllers 返回项目中可用的控制器的对象 位于该 api controllers文件夹中的每个JavaScript文件都将被解析为该strapi controllers对象 由于这个对象 xff0c 您可以访问项目
  • sublime tex4配置vue开发环境

    前提 已安装package control 参考网站 https lsp sublimetext io https www prettier cn 安装包快捷键 ctrl 43 alt 43 p 输入install package 安装以下
  • win下mkcert+phpenv本地ssl环境配置

    环境win mkcert 安装过程略 使用Chocolatey安装比较方便 1 choco install mkcert 2 mkcert install 弹出窗口点安装 3 mkcert shop test 注意点 shop test 本
  • Android http post/get

    虽然在Android开发中有很多网络框架 xff0c 可是都比较臃肿 xff0c 要考虑很多请求姿势 xff0c 所以自己写了一个 感谢大家发表评论 xff01 1 网络请求一般分为post和get两种方式 xff0c 在工作中我比较常用的
  • Android相机,图库获取图片

    大家的APP中经常会有从手机相机 图库获取图片 xff0c 这里封装了方法可以方便大家 xff0c 解决了Android7 0资源uri的获取方式 xff0c 兼容SDK19以上的机子 xff0c 有权限请求整合 xff0c 欢迎大家下架使
  • 《Streaming System》流式系统-序章

    本文翻译摘抄自 Streaming System xff0c 在阅读的时候进行翻译 xff0c 同时方便广大同学 xff0c 如有错误或侵权 xff0c 烦请指出 偶尔有机会搜到了这本书籍 xff0c 还看到知乎有有问小伙伴对此书佩服的五体
  • 《Streaming System》 第二章:数据处理的四要素 What Where When and How

    本文由 Streaming System 一书第二章的提炼翻译而来 xff0c 译者才疏学浅 xff0c 如有错误 xff0c 欢迎指正 转载请注明出处 xff0c 侵权必究 本章主要介绍鲁棒的处理乱序数据的核心概念 xff0c 这些概念的
  • 《Streaming System》 第三章:Watermarks

    简介 本章主要介绍鲁棒的处理乱序数据的核心概念 xff0c 这些概念的运用使流处理系统超越批处理系统的关键所在 本章我们从流计算系统的底层机制深入来探讨一下watermark 学习这些机制有助于我们更好理解和使用watermark 我们将讨
  • Centos 7 开放查看端口 防火墙关闭打开

    Centos 7 firewall 命令 xff1a 查看已经开放的端口 xff1a firewall span class hljs attribute cmd span span class hljs subst span span c
  • linux CentOS 安装rz和sz命令 lrzsz

    lrzsz在linux里可代替ftp上传和下载 lrzsz 官网入口 xff1a http freecode com projects lrzsz lrzsz是一个unix通信套件提供的X xff0c Y xff0c 和ZModem文件传输
  • (原创)Flutter开发问题:项目启动一直卡在Running Gradle task ‘assembleDebug‘

    问题描述 按照flutter官网步骤安装Flutter SDK Android studio等 xff0c 在创建第一个flutter项目后run的过程一直是Running Gradle task assembleDebug 针对这个问题
  • Redis 分页排序查询

    Redis是一个高效的内存数据库 xff0c 它支持包括String List Set SortedSet和Hash等数据类型的存储 xff0c 在Redis中通常根据数据的key查询其value值 xff0c Redis没有条件查询 xf