In information projects, we often encounter the function implementation of article comment and comment. However, these functions appear frequently in projects. If you operate the database directly, you will have too much pressure on the database. How to solve this problem?
redis cache
The first thing we think about is to add a caching mechanism. Users' likes can be placed in Redis and then persisted to the database.
Liking and canceling liking are high-frequency operations. If the database is read and written every time, a large number of operations will affect the performance of the database, so caching is needed
actual combat
Knowing the solution, we're going to test the results.
There is no need to introduce the redis installation. Everyone can install and run it. Let's talk about how spring boot solves this problem.
Import dependency
Add cache annotation @ EnableCaching to startup class
Next, we need to configure 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; } }
After the basic environment is built, we need to do the redis tool class.
redis storage format
Now we know the redis cache, but how can we store likes in redis. Redis supports a lot of data formats. We need to record the likes and dislikes, and the likes status.
Because of the need to record the likes and the likes, as well as the likes status (likes and cancels likes), it is also necessary to take out all likes data in Redis at a fixed time interval, and analyze the best Hash data format in Redis.
We have determined the storage mode of hash, and then we will imitate the user's likes to store data.
If you click like, the stored key is: likedUserId::likedPostId, and the corresponding value is 1. Cancel liking. The stored key is: likedUserId::likedPostId. The corresponding value is 0. When fetching data, you can cut the key with:: to get two IDS, which is also very convenient.
Now the main implementation method is to periodically persist from the cache to the database from 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(); //Separate likedUserId, likedPostId String[] split = key.split("::"); String likedUserId = split[0]; String likedPostId = split[1]; Integer value = (Integer) entry.getValue(); //Assemble as UserLike object UserLike userLike = new UserLike(likedUserId, likedPostId, value); list.add(userLike); //Delete from Redis after saving to list 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(); //Store likes in LikedCountDT String key = (String)map.getKey(); LikedCountDTO dto = new LikedCountDTO(key, (Integer) map.getValue()); list.add(dto); //Delete this record from Redis redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key); } return list; }
These two methods can be used to record the likes and count the likes in the database. In fact, redis is mainly to realize the cache function of praise and praise times.
But there is a problem. If the customer refreshes the page after clicking like and redis is not persisted in the database, the user will feel that clicking like is invalid and continue to click like. It can lead to a bad experience. Because of the influence of timer, it will bring this kind of trouble. We have better solutions to share.