Redis复杂数据更新管理的实现(一)

212 阅读3分钟

「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战

1、问题引入

​ 这里有两个问题,一个时服务重启时,如何将需要同步到redis中的数据进行预加载。另一个是,针对数据结构较为复杂的redis数据,数据库数据更新时,如何对redis中的数据进行更新。显然,这两部分都应该抽象成一个公共工具类,或者一个管理功能,而不是让每个开发人员重复这些代码。下面就先为大家介绍下,服务启动时redis热点数据的预加载实现。

2、Redis数据预加载实现代码

 @Component
public class PreloadCache implements CommandLineRunner { 
	
	@Autowired
	private ComnDao comnDao;
	
    //根据不同的服务名区分不同的服务的热点数据(每个服务只预加载自己服务的redis热点数据)
	@Value("#{'${spring.application.name}'.trim()}")
	private String serverName;
	
	@Override
	public void run(String... args) throws Exception {
		log.info("预加载缓存开始执行");
		preloadCache();
		log.info("预加载缓存执行完毕");
	}
	
	public void preloadCache() throws Exception{
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("servername", serverName);
		String sql = "select * from cache_preload where servername = $S{servername}";
		//查询本服务需要预加载的缓存
		SqlResult result = comnDao.sqlQuery(sql, SqlUtil.getDataSourceSys(), params);
		while(result.next()) {
			String queryid = result.getString("queryid");
            //数据库查询结果处理器,对于复杂结构的redis数据需要进一步处理
			String queryHandler = result.getString("queryhandler");
			if(!queryid.isEmpty()){
				loadCacheByQueryidConfig(result.getString("cachekey"), queryid, result.getString("cacheprefix"), result.getString("noncachekey"), result.getString("cachevalhandler"), result.getString("valuetype"));
			}else if(!queryHandler.isEmpty()) {
				loadCacheByHandlerConfig(result.getString("cachekey"), queryHandler, result.getString("cacheprefix"), result.getString("noncachekey"), result.getString("cachevalhandler"), result.getString("valuetype"));
			}else {
				log.error("缓存配置异常,查询id和查询处理器必须配置一个");
				throw new PromptException("缓存配置异常,查询id和查询处理器必须配置一个");
			}
		}
	}
	
	/**
	 * 根据queryid配置加载缓存
	 * @param cacheKey         		缓存键key
	 * @param queryId	    	    缓存值查询id
	 * @param cachePrefix			缓存前缀
	 * @param nonCacheKey			无需缓存键
	 * @param cacheValHandler		缓存值处理器
	 * @param redisValueType		缓存值类型
	 * @throws Exception 
	 */ 
	private void loadCacheByQueryIdidConfig(String cacheKey, String queryId, String cachePrefix, String nonCacheKey, String cacheValHandler, String redisValueType) throws Exception {
		SqlResult result = comnDao.query(queryId, new HashMap<String, Object>());
		CacheAble cacheAble = SysBeans.getCacheAble();
		List<SqlRow> rows = result.getRows();
		for(SqlRow row : rows) {
			Map<String, Object> val = new HashMap<String, Object>();
			if(!cacheValHandler.isEmpty()) {
				val = CacheUtil.cacheValHandler(nonCacheKey, (Map<String, Object>) KInvoke.invokeDistributedHander(cacheValHandler, row));
			}else {
				val = JSON.parseObject(JSON.toJSONString(CacheUtil.cacheValHandler(nonCacheKey, row)), val.getClass());
			}
			String key = CacheUtil.cacheKeyHandler(cacheKey, cachePrefix, val);
			if(cacheAble.existsKey(key, true)) {
				cacheAble.delCache(true, key);
			}
			if(RedisValueTypeEntity.HASH.equals(redisValueType)) {
				cacheAble.setMap(key, true, CacheUtil.cacheMapValHandler(val));
    		}else {
    			cacheAble.setCache(key, JSON.toJSONString(val), true);
    		}
			
		}
	}
	
	/**
	 * 根据处理器配置加载缓存
	 * @param cacheKey         		缓存键key
	 * @param queryHandler			缓存查询处理器
	 * @param cachePrefix			缓存前缀
	 * @param nonCacheKey			无需缓存键
	 * @param cacheValHandler		缓存值处理器
	 * @param redisValueType		缓存值类型
	 * @throws Exception 
	 */ 
	private void loadCacheByHandlerConfig(String cacheKey, String queryHandler, String cachePrefix, String nonCacheKey, String cacheValHandler, String redisValueType) throws Exception {
		List<Map<String, Object>> rows = (List<Map<String, Object>>) KInvoke.invokeDistributedHander(queryHandler, comnDao);
		CacheAble cacheAble = SysBeans.getCacheAble();
		for(Map<String, Object> map : rows) {
			if(!cacheValHandler.isEmpty()) {
				 map = (Map<String, Object>) KInvoke.invokeDistributedHander(cacheValHandler, map);
			}
			String key = CacheUtil.cacheKeyHandler(cacheKey, cachePrefix, map);
			map = CacheUtil.cacheValHandler(nonCacheKey, map);
			if(cacheAble.existsKey(key, true)) {
				cacheAble.delCache(true, key);
			}
			if(RedisValueTypeEntity.HASH.equals(redisValueType)) {
				cacheAble.setMap(key, true, CacheUtil.cacheMapValHandler(map));
			}else {
				cacheAble.setCache(key, JSON.toJSONString(map), true);
			}
			
		}
	}
}
 
//缓存工具,定义缓存的存储结构
public class CacheUtil {

	public static String getUuidKey (String key){
		return RequestSupport.getLocalRequest().getAttribute(FilterAttrConfig.UUID_STR)+":"+key;
	}
	
	/**
	 * 缓存键处理器
	 * @param cacheKey	缓存键
	 * @param cacheVal	缓存值
	 * @return
	 */
	public static String cacheKeyHandler(String cacheKey, Map<String, Object> cacheVal) {
		StringBuffer cacheK = new StringBuffer();
		String[] cacheKeys = cacheKey.split("\\+");
		for(String key : cacheKeys) {
			cacheK.append(cacheVal.get(key));
		}
		return cacheK.toString();
	}
	
	/**
	 * 缓存键处理器
	 * @param cacheKey		缓存键
	 * @param cachePrefix	缓存前缀
	 * @param cacheVal		缓存值
	 * @return
	 */
	public static String cacheKeyHandler(String cacheKey, String cachePrefix, Map<String, Object> cacheVal) {
		StringBuffer cacheK = new StringBuffer(cachePrefix);
		return cacheK.append(cacheKeyHandler(cacheKey, cacheVal)).toString();
	}
	
	/**
	 * 缓存值处理器
	 * @param nonCacheKey	不缓存键
	 * @param cacheVal		缓存值
	 * @return
	 */
	public static Map<String, Object> cacheValHandler(String nonCacheKey, Map<String, Object> cacheVal) {
		String[] nonCacheKeys = nonCacheKey.split(",");
		ConcurrentHashMap<String, Object> retMap = new ConcurrentHashMap<String, Object>();
		for(String key : cacheVal.keySet()) {
			if(null != cacheVal.get(key)) {
				retMap.put(key, cacheVal.get(key));
			}
		}
		for(String key : nonCacheKeys) {
			//增加对模糊键的处理  XXX结尾、XXX开头、包含XXX  使用#做通配符
			String containsPattern = ".*\\#.*";
			String atPattern = "^\\#.*";
			String endPattern = ".*\\#$";
			if(key.matches(endPattern)){
				//XXX开头
				String regex = "^" + key.replaceAll("#", "") + ".*";
				Set<String> keys = retMap.keySet();
				for(String valKey : keys) {
					if(valKey.matches(regex)) {
						retMap.remove(valKey);
					}
				}
			}else if(key.matches(atPattern)) {
				//XXX结尾
				String regex = ".*" + key.replaceAll("#", "") + "$";
				Set<String> keys = retMap.keySet();
				for(String valKey : keys) {
					if(valKey.matches(regex)) {
						retMap.remove(valKey);
					}
				}
			}else if(key.matches(containsPattern)) {
				//包含XXX
				String regex = "^" + key.substring(0, key.indexOf("#")) + ".*" + key.substring(key.indexOf("#"), key.length()) + "$";
				Set<String> keys = retMap.keySet();
				for(String valKey : keys) {
					if(valKey.matches(regex)) {
						retMap.remove(valKey);
					}
				}
			} else {
				retMap.remove(key);
			}
		}
		return retMap;
	}
	
	/**
	 * 缓存map值处理器
	 * @param cacheVal		缓存值
	 * @return
	 */
	public static Map<String, String> cacheMapValHandler(Map<String, Object> cacheVal) {
		return cacheVal.entrySet().stream()
			    .filter(entry -> entry.getValue() != null)
			    .collect(Collectors.toMap(Entry::getKey, entry -> entry.getValue().toString()));
	}
	
}

好了、本期就先介绍到这里,有什么需要交流的,大家可以随时私信我。😊