20250808优化-单体电池表拆分优化
This commit is contained in:
@ -7,10 +7,8 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.BoundSetOperations;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.core.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
@ -287,4 +285,67 @@ public class RedisCache
|
||||
redisTemplate.opsForValue().multiSet(cacheMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置键值对,并为每个键设置相同的过期时间
|
||||
* @param keyValueMap 键值对集合
|
||||
* @param timeout 过期时间
|
||||
* @param unit 时间单位
|
||||
*/
|
||||
public void multiSetWithExpire(Map<String, String> keyValueMap, long timeout, TimeUnit unit) {
|
||||
if (keyValueMap.isEmpty()) {
|
||||
return; // 空集合直接返回
|
||||
}
|
||||
|
||||
// 使用Redis管道批量执行命令
|
||||
redisTemplate.executePipelined(new SessionCallback<Object>() {
|
||||
@Override
|
||||
public Object execute(RedisOperations operations) throws DataAccessException {
|
||||
// 循环处理每个键值对
|
||||
for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
|
||||
// 1. 设置键值对
|
||||
operations.opsForValue().set(key, value);
|
||||
// 2. 设置过期时间
|
||||
operations.expire(key, timeout, unit);
|
||||
}
|
||||
return null; // 管道操作无需返回值
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定站点的所有历史测试数据缓存(最精准)
|
||||
* @param testSiteId 测试数据所属的站点ID(如"test-site-001")
|
||||
*/
|
||||
public void deleteTestCacheBySite(String testSiteId) {
|
||||
System.out.println("开删除缓存redis:" + testSiteId + "<UNK>");
|
||||
if (testSiteId == null || testSiteId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 构造键前缀:测试站点ID开头的所有缓存键(无论粒度和时间)
|
||||
String pattern = testSiteId + "_*";
|
||||
deleteCacheByPattern(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据键前缀批量删除缓存(核心方法)
|
||||
* @param pattern 键匹配模式(支持Redis的通配符:*表示任意字符,?表示单个字符)
|
||||
*/
|
||||
public void deleteCacheByPattern(String pattern) {
|
||||
try {
|
||||
// 1. 查找所有匹配的键(注意:keys命令在大数据量时可能阻塞Redis,建议生产环境用scan)
|
||||
Set<String> keys = redisTemplate.keys(pattern);
|
||||
if (keys == null || keys.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 批量删除匹配的键
|
||||
long deleteCount = redisTemplate.delete(keys);
|
||||
System.out.println("删除缓存数据:" + deleteCount + "<UNK>");
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
@ -95,7 +96,6 @@ public abstract class AbstractBatteryDataProcessor {
|
||||
// 传递分片索引和是否最后一个分片的标记
|
||||
ioExecutor.execute(() -> processShard(shard, isLastShard));
|
||||
}
|
||||
//shards.forEach(shard -> ioExecutor.execute(() -> processShard(shard)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -245,20 +245,11 @@ public abstract class AbstractBatteryDataProcessor {
|
||||
public void batchUpdateHourCache(List<EmsBatteryDataHour> hourDataList) {
|
||||
if (hourDataList.isEmpty()) return;
|
||||
|
||||
// 1. 构建缓存键值对(批量操作提升效率)
|
||||
Map<String, String> tempCacheMap = new HashMap<>(hourDataList.size());
|
||||
for (EmsBatteryDataHour data : hourDataList) {
|
||||
EmsBatteryData cacheData = new EmsBatteryData();
|
||||
BeanUtils.copyProperties(data, cacheData);
|
||||
// 2. 生成唯一缓存键(与数据库唯一键一致)
|
||||
String cacheKey = generateCacheKey(cacheData, "hour");
|
||||
// 3. 存储最高温度和最后更新时间(两个独立缓存项)
|
||||
tempCacheMap.put(cacheKey, data.getTemperature().toString());
|
||||
}
|
||||
Map<String, String> tempCacheMap = buildCacheMap(hourDataList, "hour");
|
||||
|
||||
try {
|
||||
// 4. 批量设置缓存(Redis批量操作减少网络交互)
|
||||
redisCache.multiSet(tempCacheMap);
|
||||
// 批量设置缓存
|
||||
redisCache.multiSetWithExpire(tempCacheMap, 1, TimeUnit.HOURS);
|
||||
} catch (Exception e) {
|
||||
log.error("批量更新小时级缓存失败", e);
|
||||
// 缓存更新失败不影响主流程,但需记录日志便于排查
|
||||
@ -343,20 +334,11 @@ public abstract class AbstractBatteryDataProcessor {
|
||||
public void batchUpdateDayCache(List<EmsBatteryDataDay> dayDataList) {
|
||||
if (dayDataList.isEmpty()) return;
|
||||
|
||||
// 1. 构建缓存键值对(批量操作提升效率)
|
||||
Map<String, String> tempCacheMap = new HashMap<>(dayDataList.size());
|
||||
for (EmsBatteryDataDay data : dayDataList) {
|
||||
EmsBatteryData cacheData = new EmsBatteryData();
|
||||
BeanUtils.copyProperties(data, cacheData);
|
||||
// 2. 生成唯一缓存键(与数据库唯一键一致)
|
||||
String cacheKey = generateCacheKey(cacheData, "day");
|
||||
// 3. 存储最高温度和最后更新时间(两个独立缓存项)
|
||||
tempCacheMap.put(cacheKey, data.getTemperature().toString());
|
||||
}
|
||||
Map<String, String> tempCacheMap = buildCacheMap(dayDataList, "hour");
|
||||
|
||||
try {
|
||||
// 4. 批量设置缓存(Redis批量操作减少网络交互)
|
||||
redisCache.multiSet(tempCacheMap);
|
||||
// 批量设置缓存
|
||||
redisCache.multiSetWithExpire(tempCacheMap, 24, TimeUnit.HOURS);
|
||||
} catch (Exception e) {
|
||||
log.error("批量更新小时级缓存失败", e);
|
||||
// 缓存更新失败不影响主流程,但需记录日志便于排查
|
||||
@ -454,20 +436,11 @@ public abstract class AbstractBatteryDataProcessor {
|
||||
public void batchUpdateMonthCache(List<EmsBatteryDataMonth> monthDataList) {
|
||||
if (monthDataList.isEmpty()) return;
|
||||
|
||||
// 1. 构建缓存键值对(批量操作提升效率)
|
||||
Map<String, String> tempCacheMap = new HashMap<>(monthDataList.size());
|
||||
for (EmsBatteryDataMonth data : monthDataList) {
|
||||
EmsBatteryData cacheData = new EmsBatteryData();
|
||||
BeanUtils.copyProperties(data, cacheData);
|
||||
// 2. 生成唯一缓存键(与数据库唯一键一致)
|
||||
String cacheKey = generateCacheKey(cacheData, "month");
|
||||
// 3. 存储最高温度和最后更新时间(两个独立缓存项)
|
||||
tempCacheMap.put(cacheKey, data.getTemperature().toString());
|
||||
}
|
||||
Map<String, String> tempCacheMap = buildCacheMap(monthDataList, "hour");
|
||||
|
||||
try {
|
||||
// 4. 批量设置缓存(Redis批量操作减少网络交互)
|
||||
redisCache.multiSet(tempCacheMap);
|
||||
// 批量设置缓存
|
||||
redisCache.multiSetWithExpire(tempCacheMap, 24*30, TimeUnit.HOURS);
|
||||
} catch (Exception e) {
|
||||
log.error("批量更新月级缓存失败", e);
|
||||
// 缓存更新失败不影响主流程,但需记录日志便于排查
|
||||
@ -659,6 +632,30 @@ public abstract class AbstractBatteryDataProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
// 通用构建缓存键值对的方法(根据不同数据类型适配)
|
||||
private <T> Map<String, String> buildCacheMap(List<T> dataList, String granularity) {
|
||||
Map<String, String> cacheMap = new HashMap<>(dataList.size());
|
||||
for (T data : dataList) {
|
||||
EmsBatteryData cacheData = new EmsBatteryData();
|
||||
BeanUtils.copyProperties(data, cacheData);
|
||||
String cacheKey = generateCacheKey(cacheData, granularity);
|
||||
|
||||
// 根据数据类型获取温度值(实际项目中可通过接口或反射统一处理)
|
||||
String temperature;
|
||||
if (data instanceof EmsBatteryDataHour) {
|
||||
temperature = ((EmsBatteryDataHour) data).getTemperature().toString();
|
||||
} else if (data instanceof EmsBatteryDataDay) {
|
||||
temperature = ((EmsBatteryDataDay) data).getTemperature().toString();
|
||||
} else if (data instanceof EmsBatteryDataMonth) {
|
||||
temperature = ((EmsBatteryDataMonth) data).getTemperature().toString();
|
||||
} else {
|
||||
throw new IllegalArgumentException("不支持的数据类型");
|
||||
}
|
||||
cacheMap.put(cacheKey, temperature);
|
||||
}
|
||||
return cacheMap;
|
||||
}
|
||||
|
||||
// 新增定时任务,每1分钟强制刷新一次队列(防止数据长期积压)
|
||||
@Scheduled(fixedRate = 60000) // 1分钟 = 60000毫秒
|
||||
protected void scheduledFlushBatch() {
|
||||
|
||||
@ -195,7 +195,7 @@
|
||||
soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc),
|
||||
soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh),
|
||||
inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance),
|
||||
update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by)
|
||||
update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by),
|
||||
temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature)
|
||||
</insert>
|
||||
</mapper>
|
||||
|
||||
@ -196,7 +196,7 @@
|
||||
soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc),
|
||||
soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh),
|
||||
inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance),
|
||||
update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by)
|
||||
update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by),
|
||||
temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature)
|
||||
</insert>
|
||||
</mapper>
|
||||
@ -196,7 +196,7 @@
|
||||
soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc),
|
||||
soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh),
|
||||
inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance),
|
||||
update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by)
|
||||
update_by = IF(VALUES(temperature) > temperature, VALUES(update_by), update_by),
|
||||
temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature)
|
||||
</insert>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user