个人技术学习笔记 Personal Tech Study Notes

学习笔记:使用 Spring RedisTemplate 通过 ZSET + HASH 实现分页

本文是本人在学习 Redis 时的整理笔记。学习过程中遇到需要将 MySQL 表中的数据缓存到 Redis 中,并且在展示时还要支持分页查询的场景。最初尝试单独使用 HASH 结构,但发现无法直接满足分页需求,通过查阅公开资料后,学习到可以结合 ZSET 与 HASH 两种结构一起使用来实现分页效果,因此在此做一份学习记录,方便自己日后复习。

一、思路整理

  1. 使用 ZSET 存储表中记录的 id,把 id 作为 ZSET 的 value,并利用 ZSET 的 score 字段进行排序;
  2. 使用 HASH 结构存储完整的数据行,把 id 作为 HASH 的 key
  3. 查询分页时,先通过 zRangeByScore 在 ZSET 中按分数区间取出当前页的 id 列表,再依次到 HASH 中取出对应的数据即可。

二、代码练习

下面是我在学习过程中根据思路整理并练习的示例代码,仅用于自己练习理解 API,非线上项目代码:

/**
 * 存放单个 hash 缓存
 * @param key  键
 * @param hkey hash 内部键
 * @param value 值
 */
public static boolean hput(String key, String hkey, Object value) {
    try {
        redisTemplate.opsForHash().put(key, hkey, value);
        log.debug("hput {} = {}", key + hkey, value);
        return true;
    } catch (Exception e) {
        log.warn("hput {} = {}", key + hkey, value, e);
    }
    return false;
}

/**
 * 分页存入数据
 * @param key   业务 key 前缀
 * @param hkey  数据在 hash 中的 key(可用 id)
 * @param score 排序分数(可用时间戳或 id)
 * @param value 具体数据
 */
public static boolean setPage(String key, String hkey, double score, String value) {
    boolean result = false;
    try {
        redisTemplate.opsForZSet().add(key + ":page", hkey, score);
        result = hput(key, hkey, value);
        log.debug("setPage {}", key);
    } catch (Exception e) {
        log.warn("setPage {}", key, e);
    }
    return result;
}

/**
 * 分页取出 hash 中对应 hkey 的集合
 * @param key    业务 key 前缀
 * @param offset 页码(从 1 开始)
 * @param count  每页条数
 */
public static Set<String> getPage(String key, int offset, int count) {
    Set<String> result = null;
    try {
        // 1 与 100000 是 score 的范围示例,实际使用时按需要调整
        result = redisTemplate.opsForZSet()
                .rangeByScore(key + ":page", 1, 100000, (offset - 1) * count, count);
        log.debug("getPage {}", key);
    } catch (Exception e) {
        log.warn("getPage {}", key, e);
    }
    return result;
}

/**
 * 获取当前 key 已缓存的数据总数(用于分页总数展示)
 */
public static Integer getSize(String key) {
    Integer num = 0;
    try {
        Long size = redisTemplate.opsForZSet().zCard(key + ":page");
        log.debug("getSize {}", key);
        return size.intValue();
    } catch (Exception e) {
        log.warn("getSize {}", key, e);
    }
    return num;
}

三、我的一点心得

本篇为个人学习整理笔记,用于自我复习。参考了公开的技术文章《spring 中 redisTemplate 实现 redis 分页》,并结合自己的理解重新整理注释。
« 返回首页