Site Overlay

基于Redis Set的消息同步(执行)方案

需求:

短时间内游戏用户数据会多次修改,直接操作数据库并发有限,需要一个方案解决既同步存储数据库,又满足高并发下修改读取操作,避免重复

实现思路:

将请求分为 处理方 ,生产方

生产方将需要更新的数据放在一边,然后在一个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数据,达到为数据库削峰的目的,提高后台并发上限。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

A beliving heart is your magic My heart
Copyright © 2020 Diuut. All Rights Reserved. | Catch Vogue by Catch Themes