基于Spring Boot与Lettuce的Redis每日时间序列数据存储方案咨询
需求可行性与实现思路
完全可行,Redis的结构特性刚好匹配你的需求,以下是具体实现思路:
数据结构选型
- 用Hash存储每日数值列表:固定前缀+标准日期作为Hash的field(比如
2024-10-12),对应的value存当日的数值列表(借助RedisTemplate的序列化能力直接存Java List即可),Hash的key统一设为daily_values,所有日期的数据集中管理更方便。 - 用Sorted Set维护日期索引:把日期转成当日零点的时间戳作为score,日期字符串作为member,既能快速按时间范围筛选日期,也能轻松定位到最早的过期日期。
每日数据存储流程
每次新增当日数据时:
- 将当日的数值列表存入Hash的对应field中。
- 将当日日期的时间戳和字符串添加到Sorted Set索引里。
自动过期与清理逻辑
每次新增数据后(或每日定时执行),执行以下操作:
- 计算28天前的日期,转成当日零点的时间戳作为阈值。
- 从Sorted Set中取出所有score小于该阈值的日期(即过期日期)。
- 批量删除Hash中这些过期日期对应的field。
- 从Sorted Set中移除这些过期的日期member。
如果业务量较大,建议用定时任务(比如Spring的@Scheduled)每日凌晨执行清理,避免每次新增数据都触发操作。
范围查询实现
查询x日到y日的数据时:
- 将x日和y日分别转成当日零点的时间戳。
- 从Sorted Set中获取score在这两个时间戳之间的所有日期。
- 遍历这些日期,从Hash中取出对应的数值列表,汇总后返回即可。
基于RedisTemplate的代码示例
存储当日数据
import java.time.LocalDate; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; // 格式化当日日期为标准格式 String today = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); List<Double> todayValues = Arrays.asList(1.0, 2.0, 3.0); // 存入Hash redisTemplate.opsForHash().put("daily_values", today, todayValues); // 添加到日期索引Sorted Set long todayTimestamp = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toEpochSecond(); redisTemplate.opsForZSet().add("date_index", today, todayTimestamp);
清理过期数据
import java.time.LocalDate; import java.time.ZoneId; import java.util.Set; // 计算28天前的时间戳阈值 LocalDate cutoffDate = LocalDate.now().minusDays(28); long cutoffTimestamp = cutoffDate.atStartOfDay(ZoneId.systemDefault()).toEpochSecond(); // 获取所有过期日期 Set<String> expiredDates = redisTemplate.opsForZSet().rangeByScore("date_index", 0, cutoffTimestamp); if (expiredDates != null && !expiredDates.isEmpty()) { // 批量删除Hash中的过期数据 redisTemplate.opsForHash().delete("daily_values", expiredDates.toArray()); // 从索引中移除过期日期 redisTemplate.opsForZSet().removeRangeByScore("date_index", 0, cutoffTimestamp); }
范围查询数据
import java.time.LocalDate; import java.time.ZoneId; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; // 定义查询的起止日期 LocalDate startDate = LocalDate.of(2024, 10, 12); LocalDate endDate = LocalDate.of(2024, 10, 13); long startTimestamp = startDate.atStartOfDay(ZoneId.systemDefault()).toEpochSecond(); long endTimestamp = endDate.atStartOfDay(ZoneId.systemDefault()).toEpochSecond(); // 获取范围内的日期 Set<String> targetDates = redisTemplate.opsForZSet().rangeByScore("date_index", startTimestamp, endTimestamp); Map<String, List<Double>> resultMap = new HashMap<>(); if (targetDates != null) { for (String date : targetDates) { @SuppressWarnings("unchecked") List<Double> values = (List<Double>) redisTemplate.opsForHash().get("daily_values", date); if (values != null) { resultMap.put(date, values); } } } // resultMap就是查询范围内的日期与对应数值列表的映射
内容的提问来源于stack exchange,提问作者user1555190
相关产品推荐
相关产品推荐

