在资讯类的项目中,我们常常遇到文章评论和点赞等功能实现。但是这些功能再项目中是高频出现的,如果直接操作数据库的话,对数据库压力太大。那遇到这个问题怎么解决?
首先我们想到的就是添加缓存处理机制。用户点赞可以放在Redis中,然后持久化到数据库中。
点赞、取消点赞是高频次的操作,若每次都读写数据库,大量的操作会影响数据库性能,所以需要做缓存复制代码
知道了解决方案,我们就要去测试效果。
redis安装不用介绍了,大家都可以安装运行。下面来讲springboot怎么解决这个问题。
在启动类中添加缓存注解 @EnableCaching
接下来我们需要配置redis
@Configuration public class RedisConfig { @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<String, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(redisConnectionFactory); template.setKeySerializer(jackson2JsonRedisSerializer); template.setValueSerializer(jackson2JsonRedisSerializer); template.setHashKeySerializer(jackson2JsonRedisSerializer); template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } @Bean @ConditionalOnMissingBean(StringRedisTemplate.class) public StringRedisTemplate stringRedisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } }复制代码
这样基本环境搭建完成,我们需要做的就是redis的工具类了。
现在知道了redis缓存,但是我们怎么把点赞存储到redis中。redis支持数据格式很多,我们需要把点赞人和被点赞人,点赞状态都给记录下来。
由于需要记录点赞人和被点赞人,还有点赞状态(点赞、取消点赞),还要固定时间间隔取出 Redis 中所有点赞数据,分析了下 Redis 数据格式中 Hash 最合适。复制代码
hash的存储方式我们确定了,接下来就是模仿用户点赞存储数据。
如果用户点赞,存储的键为:likedUserId::likedPostId,对应的值为 1 。取消点赞,存储的键为:likedUserId::likedPostId,对应的值为 0 。取数据时把键用 :: 切开就得到了两个id,也很方便。复制代码
现在主要的实现方法就是从redis中定时从缓存中持久化到数据库中。
@Override public List<UserLike> getLikedDataFromRedis() { Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED, ScanOptions.NONE); List<UserLike> list = new ArrayList<>(); while (cursor.hasNext()){ Map.Entry<Object, Object> entry = cursor.next(); String key = (String) entry.getKey(); //分离出 likedUserId,likedPostId String[] split = key.split("::"); String likedUserId = split[0]; String likedPostId = split[1]; Integer value = (Integer) entry.getValue(); //组装成 UserLike 对象 UserLike userLike = new UserLike(likedUserId, likedPostId, value); list.add(userLike); //存到 list 后从 Redis 中删除 redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key); } return list; } @Override public List<LikedCountDTO> getLikedCountFromRedis() { Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, ScanOptions.NONE); List<LikedCountDTO> list = new ArrayList<>(); while (cursor.hasNext()){ Map.Entry<Object, Object> map = cursor.next(); //将点赞数量存储在 LikedCountDT String key = (String)map.getKey(); LikedCountDTO dto = new LikedCountDTO(key, (Integer) map.getValue()); list.add(dto); //从Redis中删除这条记录 redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key); } return list; }复制代码
这二个方法就可以实现点赞记录和点赞次数的统计到数据库中。其实,redis主要就是实现点赞和点赞次数直接的一个缓存的作用。
但是问题来了,如果客户在点赞后,刷新了页面,此时redis没有持久化到数据库中,用户就会觉得点赞失效,继续点赞。导致可以体验不好。因为有定时器的影响,就会带来这种麻烦。大家有更好的解决办法分享一下。