需求:
短时间内游戏用户数据会多次修改,直接操作数据库并发有限,需要一个方案解决既同步存储数据库,又满足高并发下修改读取操作,避免重复
实现思路:
将请求分为 处理方 ,生产方
生产方将需要更新的数据放在一边,然后在一个redis的set里面添加一个key,这个key就是需要更新的数据的key。
/**
* 更新Redis中User数据(生产方)
*
* @param user 用户data
*/
public void saveUser(User user) {
userRedisTemplate.opsForValue().set("xyddz-user-uid:" + user.getUid(), user, 30, TimeUnit.DAYS);
redisTemplate.opsForSet().add("xyddz-redis-userData-update-queue", "xyddz-user-uid:" + user.getUid());
}
处理方启动一个定时任务,执行方式可以选用fixedDelay方式,执行完一个定时任务以后间隔 xxx ms再次执行,定时任务中从生产方存储更新数据的set中pop弹出一条或者多条数据进行持久化处理或者消息消费。执行成功的弹出以后set中就自动移除了,如果执行失败就可以将这个key放回去或者别的处理方式。时间间隔根据业务时间重要性画风,同步要求高的间隔可以设置短一点,要求低的比如统计之类的就可以稍微长一点。以减少性能占用。
/**
* (处理方)
* @Author Diuut
* @Date 2020/5/12 9:07
*/
@EnableAsync
@Configuration
@Slf4j
public class UserDataUpdateQueue {
@Autowired
private RedisTemplate redisTemplate;
@Resource(name = "userRedis")
private RedisTemplate<String,User> userRedisTemplate;
@Autowired
private UserDao userDao;
@Async("taskExecutor")
@Scheduled(fixedDelay = 500)
public void getUserUpdateQueue() {
while (redisTemplate.opsForSet().size("xyddz-redis-userData-update-queue") > 0) {
List<Object> pop = redisTemplate.opsForSet().pop("xyddz-redis-userData-update-queue", 10);
for (Object userData : pop) {
log.info("userData: {}",userData);
User user = userRedisTemplate.opsForValue().get(userData.toString());
userDao.save(user);
log.info("------持久化user:uid: "+user.getUid()+"----");
}
}
}
}
set中存储的可以是需要修改的数据的key,也可以是一个指令的请求地址,基于set的唯一key机制,自动去重,该方法用于过滤短时间内多次重复请求,比如500ms内修改了3次user数据,3次都修改的是redis中存储的user信息,500ms间隔一到,定时任务开始执行,持久化保存到数据库中的那一次即是最新最近修改的一次user数据,达到为数据库削峰的目的,提高后台并发上限。