基于Redis的持久化缓冲器---延迟同步数据库,将要更新到数据库的数据暂存redis,延迟更新到数据库

简介

在高并发场景下,像访问次数以及访问记录保存是非常影响性能的,每一次详情页面读取就会产生一条 update 和 insert 。所以需要一个持久化缓冲机制,将高频的数据库写入操作放到Redis去缓冲,设定一个延迟时间,到时间后才去持久化到数据库。

如果项目已经集成Redis,则可以很方便地通过简单代码就实现数据的缓冲,极大程度增加系统吞吐率和响应速度。

(本文介绍的是一个轻量级的实现,还有一些细节没有处理,只是为了满足项目当前需求。如果对数据持久化的可靠性以及到达顺序有高要求,则需要考虑用消息队列kafka或者rabbitmq来实现)

 

使用效果

Redis持久化缓冲器源码

package outservice.redis.buffer;

import java.io.Serializable;
import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;


import outservice.redis.RedisUtil;
import outservice.redis.StringUtils;

/**
 * 数据缓冲服务<br>
 * 频繁需要更新数据库的操作可以先在缓存中更新,延迟写入数据库

 * @author zhaojunfu
 *
 */
@Component
public class PersistenceBuffer {
	
	private static final Logger logger = Logger.getLogger(PersistenceBuffer.class);

	@Autowired
	private RedisUtil redisUtil;
	
	@Autowired
	private ThreadPoolTaskExecutor threadPool;
	
	/**
	 * 数字递增缓冲<br>
	 * 适用场景:详情页面的访问数累计
	 * @param key 该数字在redis的缓存key
	 * @param delayMinutes 延迟更新到数据库的时间(毫秒)
	 * @param executeable  从缓存到持久化的具体实现
	 */
	public void increase(String key,long delayMinutes,IncreaseExecuteble executeable) {
		key = BufferType.Increase+"-"+key;
		String keyTime = "time-"+key;
		try{
			boolean keyIsExist=redisUtil.hasKey(key);
			if(!keyIsExist){
				redisUtil.set(key, executeable.query());
				redisUtil.set(keyTime, System.currentTimeMillis()+delayMinutes);
			}else{
//				redisUtil.incr(key, 1);
				//TODO 暂时先用set的方式,incr 涉及到修改全局序列化器
				redisUtil.set(key,1+ StringUtils.toLong( String.valueOf(redisUtil.get(key))));
				long dbTime = StringUtils.toLong( String.valueOf(redisUtil.get(keyTime)));
				if(System.currentTimeMillis()>=dbTime){
					//更新数据库,更新下次写入时间
					executeable.persistence(StringUtils.toLong( String.valueOf(redisUtil.get(key))));
					redisUtil.set(keyTime, System.currentTimeMillis()+delayMinutes);
				}
			}
		}catch(Exception e){
			logger.error(e,e);
//			persistence.increasePersistence(StringUtils.toLong( String.valueOf(redisUtil.get(key))));
		}
	}
	
	/**
	 * 数据插入缓冲<br>
	 * 适用场景:延迟插入访问日志记录,关系记录等非强业务性数据<br>
	 * @param key 该数据在redis的缓存key
	 * @param insertData 插入的数据(必须实现Serializable)
	 * @param delayMinutes 延迟插入时间(毫秒)
	 * @param executeable 插入的具体方法实现
	 */
	public void insert(String key,Serializable insertData,long delayMinutes,final InsertExecuteble executeable) {
		key = BufferType.Insert+"-"+key;
		String keyTime = "time-"+key;
		try{
			boolean keyIsExist=redisUtil.hasKey(keyTime);
			if(!keyIsExist){
				redisUtil.set(keyTime, System.currentTimeMillis()+delayMinutes);
			}
//			redisUtil.getRedisTemplate().opsForList().rightPush(key, value);
			SetOperations<String, Object> set = redisUtil.getRedisTemplate().opsForSet();
			set.add(key, insertData);
			long dbTime =StringUtils.toLong( String.valueOf(redisUtil.get(keyTime)));
			if(System.currentTimeMillis()>=dbTime){
				synchronized (this) {
					//再次判断是否还存在key
					if(!redisUtil.hasKey(key)){
						return;
					}
					final Set<Object> datas = set.members(key);
					//清空缓存数据
					redisUtil.del(key);
					redisUtil.del(keyTime);
					//用线程池异步执行持久化任务
					threadPool.execute(new Runnable() {
						@Override
						public void run() {
							executeable.persistence(datas);
						}
					});
				}
			}
			
		}catch(Exception e){
			logger.error(e,e);
		}
	}
	
}

由于里面有一些其他类的引用,所以特地抽出了一个Redis扩展相关的工程

https://download.csdn.net/download/u011177064/11885500

 

 

发布了16 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u011177064/article/details/102643682