You need to enable JavaScript to run this app. Java Spring Boot 基于Redis存储学生列表并实现多条件过滤查询
优惠活动
大模型
产品
解决方案
定价
更多

Java Spring Boot 基于Redis存储学生列表并实现多条件过滤查询

Redis存储自定义Student列表并实现多条件检索的Java实现

选型逻辑

因为检索条件包含精确匹配课程ID、ID范围查询、报名日期范围查询三类要求,不能采用将全量Student列表序列化后单key存储的方案——数据量稍大就会出现全量拉取、反序列化的性能损耗。实际落地时搭配Redis原生Hash存储全量实体、ZSet做维度索引的结构,即可在不引入额外组件的前提下完成需求。

实体类定义

直接匹配给出的Student结构,注意日期类型的序列化处理:

import java.util.Date;

public class Student {
    private int id;
    private int course_id;
    private Date addmisson_date;
    private String name;

    // 省略全参构造、getter、setter方法
}

存储结构设计

所有Key统一采用固定前缀做隔离,避免和其他业务数据冲突:

  • 学生全量信息:student:info:{studentId} 类型为Hash,逐字段存储学生的id、course_id、报名日期时间戳、姓名
  • 课程维度ID索引:student:index:course:{courseId} 类型为ZSet,score存学生id(整数原生适配范围查询),value存学生id
  • 课程维度报名日期索引:student:index:course:{courseId}:date 类型为ZSet,score存报名日期的毫秒时间戳(适配时间范围查询),value存学生id

代码实现

列表存储逻辑

遍历传入的Student列表,逐次写入全量Hash数据,同步更新两个维度的ZSet索引:

import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import java.util.List;

public void saveStudentData(List<Student> studentList, RedisTemplate<String, Object> redisTemplate) {
    HashOperations<String, Object, Object> hashOps = redisTemplate.opsForHash();
    ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();

    for (Student student : studentList) {
        // 写入学生全量Hash数据
        String infoKey = String.format("student:info:%d", student.getId());
        hashOps.put(infoKey, "id", student.getId());
        hashOps.put(infoKey, "course_id", student.getCourse_id());
        hashOps.put(infoKey, "addmisson_date", student.getAddmisson_date().getTime());
        hashOps.put(infoKey, "name", student.getName());

        // 更新课程下的ID范围索引
        String courseIdIndexKey = String.format("student:index:course:%d", student.getCourse_id());
        zSetOps.add(courseIdIndexKey, student.getId(), student.getId());

        // 更新课程下的报名日期范围索引
        String courseDateIndexKey = String.format("student:index:course:%d:date", student.getCourse_id());
        zSetOps.add(courseDateIndexKey, student.getId(), student.getAddmisson_date().getTime());
    }
}

多条件检索逻辑

检索入参为:指定课程ID、ID区间上下限、报名日期区间上下限,流程为按维度过滤取交集、拉取匹配学生的全量信息:

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

public List<Student> queryMatchedStudents(int targetCourseId, int idMin, int idMax,
                                          Date dateStart, Date dateEnd,
                                          RedisTemplate<String, Object> redisTemplate) {
    HashOperations<String, Object, Object> hashOps = redisTemplate.opsForHash();
    ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
    List<Student> result = new ArrayList<>();

    // 第一步:按ID范围过滤匹配的学生
    String courseIdIndexKey = String.format("student:index:course:%d", targetCourseId);
    Set<Object> idRangeMatched = zSetOps.rangeByScore(courseIdIndexKey, idMin, idMax);

    // 第二步:按报名日期范围过滤匹配的学生
    String courseDateIndexKey = String.format("student:index:course:%d:date", targetCourseId);
    Set<Object> dateRangeMatched = zSetOps.rangeByScore(courseDateIndexKey, dateStart.getTime(), dateEnd.getTime());

    // 取两个集合的交集,得到同时满足所有条件的学生ID
    idRangeMatched.retainAll(dateRangeMatched);

    // 遍历交集ID,拉取全量学生信息组装返回
    for (Object idObj : idRangeMatched) {
        int studentId = (int) idObj;
        String infoKey = String.format("student:info:%d", studentId);
        Map<Object, Object> studentInfo = hashOps.entries(infoKey);
        
        Student student = new Student();
        student.setId((Integer) studentInfo.get("id"));
        student.setCourse_id((Integer) studentInfo.get("course_id"));
        student.setAddmisson_date(new Date((Long) studentInfo.get("addmisson_date")));
        student.setName((String) studentInfo.get("name"));
        result.add(student);
    }
    return result;
}

落地注意事项

  • 别图省事把整个Student列表序列化成存在单个key的JSON里,只要数据量破万,每次检索都要拉全量反序列化,性能会掉的非常明显
  • 单课程下学生规模超过10万时,可以切换为Redis Search的结构化文档索引替代自定义ZSet索引,减少手动维护集合交集的内存开销
  • 日期字段统一存为毫秒时间戳作为ZSet的score,别直接存格式化字符串,不然范围查询会完全失效
  • 检索过程中如果生成了临时的交集中间集合,记得设置10秒以内的过期时间,别让无意义的结果占Redis内存

内容的提问来源于stack exchange,提问作者THE ANKIT VERMA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.12 09:54:07