20250808优化-单体电池表拆分优化

This commit is contained in:
2025-08-24 17:12:42 +08:00
parent 725ae14548
commit d6276dc21f
5 changed files with 102 additions and 44 deletions

View File

@ -7,10 +7,8 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
@ -287,4 +285,67 @@ public class RedisCache
redisTemplate.opsForValue().multiSet(cacheMap); 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) {
}
}
} }

View File

@ -28,6 +28,7 @@ import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -95,7 +96,6 @@ public abstract class AbstractBatteryDataProcessor {
// 传递分片索引和是否最后一个分片的标记 // 传递分片索引和是否最后一个分片的标记
ioExecutor.execute(() -> processShard(shard, isLastShard)); 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) { public void batchUpdateHourCache(List<EmsBatteryDataHour> hourDataList) {
if (hourDataList.isEmpty()) return; if (hourDataList.isEmpty()) return;
// 1. 构建缓存键值对(批量操作提升效率) Map<String, String> tempCacheMap = buildCacheMap(hourDataList, "hour");
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());
}
try { try {
// 4. 批量设置缓存Redis批量操作减少网络交互 // 批量设置缓存
redisCache.multiSet(tempCacheMap); redisCache.multiSetWithExpire(tempCacheMap, 1, TimeUnit.HOURS);
} catch (Exception e) { } catch (Exception e) {
log.error("批量更新小时级缓存失败", e); log.error("批量更新小时级缓存失败", e);
// 缓存更新失败不影响主流程,但需记录日志便于排查 // 缓存更新失败不影响主流程,但需记录日志便于排查
@ -343,20 +334,11 @@ public abstract class AbstractBatteryDataProcessor {
public void batchUpdateDayCache(List<EmsBatteryDataDay> dayDataList) { public void batchUpdateDayCache(List<EmsBatteryDataDay> dayDataList) {
if (dayDataList.isEmpty()) return; if (dayDataList.isEmpty()) return;
// 1. 构建缓存键值对(批量操作提升效率) Map<String, String> tempCacheMap = buildCacheMap(dayDataList, "hour");
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());
}
try { try {
// 4. 批量设置缓存Redis批量操作减少网络交互 // 批量设置缓存
redisCache.multiSet(tempCacheMap); redisCache.multiSetWithExpire(tempCacheMap, 24, TimeUnit.HOURS);
} catch (Exception e) { } catch (Exception e) {
log.error("批量更新小时级缓存失败", e); log.error("批量更新小时级缓存失败", e);
// 缓存更新失败不影响主流程,但需记录日志便于排查 // 缓存更新失败不影响主流程,但需记录日志便于排查
@ -454,20 +436,11 @@ public abstract class AbstractBatteryDataProcessor {
public void batchUpdateMonthCache(List<EmsBatteryDataMonth> monthDataList) { public void batchUpdateMonthCache(List<EmsBatteryDataMonth> monthDataList) {
if (monthDataList.isEmpty()) return; if (monthDataList.isEmpty()) return;
// 1. 构建缓存键值对(批量操作提升效率) Map<String, String> tempCacheMap = buildCacheMap(monthDataList, "hour");
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());
}
try { try {
// 4. 批量设置缓存Redis批量操作减少网络交互 // 批量设置缓存
redisCache.multiSet(tempCacheMap); redisCache.multiSetWithExpire(tempCacheMap, 24*30, TimeUnit.HOURS);
} catch (Exception e) { } catch (Exception e) {
log.error("批量更新月级缓存失败", 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分钟强制刷新一次队列防止数据长期积压 // 新增定时任务每1分钟强制刷新一次队列防止数据长期积压
@Scheduled(fixedRate = 60000) // 1分钟 = 60000毫秒 @Scheduled(fixedRate = 60000) // 1分钟 = 60000毫秒
protected void scheduledFlushBatch() { protected void scheduledFlushBatch() {

View File

@ -195,7 +195,7 @@
soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc), soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc),
soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh), soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh),
inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance), 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) temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature)
</insert> </insert>
</mapper> </mapper>

View File

@ -196,7 +196,7 @@
soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc), soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc),
soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh), soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh),
inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance), 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) temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature)
</insert> </insert>
</mapper> </mapper>

View File

@ -196,7 +196,7 @@
soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc), soc = IF(VALUES(temperature) > temperature, VALUES(soc), soc),
soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh), soh = IF(VALUES(temperature) > temperature, VALUES(soh), soh),
inter_resistance = IF(VALUES(temperature) > temperature, VALUES(inter_resistance), inter_resistance), 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) temperature = IF(VALUES(temperature) > temperature, VALUES(temperature), temperature)
</insert> </insert>
</mapper> </mapper>