I read an article before, talking about the application scenarios of Redis, one of which is to realize the “like” function, which is shallow on paper and must be practiced

Function point design

For example, the gold-digging website, which I like to post articles, has the function of “like”, which counts the total number of “like” on articles and the number of “like” on all articles of users. Therefore, the “like” function module designed has the following function points:

  • The number of likes for an article
  • The number of likes for all posts by the user
  • Articles that users like
  • Persisted inMySQLThe database

Database design

  • Redis is a K-V database. There is no unified data structure. Different K-V storage structures are designed for different function points

    • The number of likes used by a user for an articleHashMapData structure,HashMapIn thekeyforarticleId.valueforSet.SetThe value is userID, i.e.,HashMap<String, Set<String>>
    • Total number of likes used by usersHashMapData structure,HashMapIn thekeyforuserId.valueforStringKeep track of total likes
    • Users like articles usedHashMapData structure,HashMapIn thekeyforuserId.valueforSet.SetThe value in is the articleID, i.e.,HashMap<String, Set<String>>
  • MySQL database design two main tables, article table and user_like_article table

    • articleTable structure
    The field values The field type instructions
    article_name varchar The article name
    content blob The article content
    total_like_count bigint Total number of likes

    The total number of likes for the article needs to be synchronized with the number of likes in Redis

    • user_like_articleTable structure
    The field values The field type instructions
    user_id bigint The user ID
    article_id bigint The article ID

    It is an intermediate table that records information about articles that users like

Note: The id, DELETED, GMT_CREATE, gmT_MODIFIED columns are omitted from the table structure design

The flow chart

The flowchart is relatively simple, and the basic implementation steps of “like” and “unlike” are the same

  • Parameter verification is performed on incoming parametersnullValue judgment
  • Logical verification For users to like, users can not like the same article repeatedly for unlike, users can not cancel the unliked article
  • depositRedisThe data stored mainly includes the number of likes for all articles, the number of likes for a certain article and the articles that users like
  • Scheduled task The scheduled task is executed once an hourRedisRead data persisted toMySQLIn the

Code function implementation

  • give a like
public void likeArticle(Long articleId, Long likedUserId, Long likedPostId) { validateParam(articleId, likedUserId, likedPostId); // Verify logger.info(ArticleId :{}, likedUserId:{}, likedPostId:{}, articleId, likedUserId, likedPostId); Synchronized (this) {synchronized (this) {synchronization (articleId, likedUserId, likedPostId); Redistemplate.opsforhash ().increment(TOTAL_LIKE_COUNT_KEY, string.valueof (likedUserId), 1); +1 String userLikeResult = (String) redistemplate.opsForHash ().get(user_article_key, String.valueOf(likedPostId)); Set<Long> articleIdSet = userLikeResult == null ? new HashSet<>() : FastjsonUtil.deserializeToSet(userLikeResult, Long.class); articleIdSet.add(articleId); redisTemplate.opsForHash().put(USER_LIKE_ARTICLE_KEY, String.valueOf(likedPostId), FastjsonUtil.serialize(articleIdSet)); ArticleLikedResult = (String) redistemplate.opsForHash ().get(ARTICLE_LIKED_USER_KEY, String.valueOf(articleId)); Set<Long> likePostIdSet = articleLikedResult == null ? new HashSet<>() : FastjsonUtil.deserializeToSet(articleLikedResult, Long.class); likePostIdSet.add(likedPostId); redisTemplate.opsForHash().put(ARTICLE_LIKED_USER_KEY, String.valueOf(articleId), FastjsonUtil.serialize(likePostIdSet)); logger.info(ArticleId :{}, likedUserId:{}, likedPostId:{}", articleId, likedUserId, likedPostId); }}Copy the code
  • Cancel the thumb up
public void unlikeArticle(Long articleId, Long likedUserId, Long likedPostId) { validateParam(articleId, likedUserId, likedPostId); // Check logger.info(ArticleId :{}, likedUserId:{}, likedPostId:{}", articleId, likedUserId, likedPostId); / / 1. User synchronized to the total number of thumb up - 1 (this) {/ / only thumb up user can cancel the thumb up unlikeArticleLogicValidate (articleId likedUserId, likedPostId); Long totalLikeCount = Long.parseLong((String)redisTemplate.opsForHash().get(TOTAL_LIKE_COUNT_KEY, String.valueOf(likedUserId))); redisTemplate.opsForHash().put(TOTAL_LIKE_COUNT_KEY, String.valueOf(likedUserId), String.valueOf(--totalLikeCount)); 1 String userLikeResult = (String) redistemplate.opsForHash ().get(USER_LIKE_ARTICLE_KEY, String.valueOf(likedPostId)); Set<Long> articleIdSet = FastjsonUtil.deserializeToSet(userLikeResult, Long.class); articleIdSet.remove(articleId); redisTemplate.opsForHash().put(USER_LIKE_ARTICLE_KEY, String.valueOf(likedPostId), FastjsonUtil.serialize(articleIdSet)); String articleLikedResult = (String) redistemplate.opsForHash ().get(ARTICLE_LIKED_USER_KEY, String.valueOf(articleId)); Set<Long> likePostIdSet = FastjsonUtil.deserializeToSet(articleLikedResult, Long.class); likePostIdSet.remove(likedPostId); redisTemplate.opsForHash().put(ARTICLE_LIKED_USER_KEY, String.valueOf(articleId), FastjsonUtil.serialize(likePostIdSet)); } logger.info(ArticleId :{}, likedUserId:{}, likedPostId:{}", articleId, likedUserId, likedPostId);
}
Copy the code
  • Asynchronous fall library
@Scheduled(cron = "0, 0, 0, 1 * *? ")
public void redisDataToMySQL() {
    logger.info("Time :{}, start persisting Redis data to MySQL", LocalDateTime.now().format(formatter)); Map<String, String> articleCountMap = redistemplate.opsForHash (). Entries (ARTICLE_LIKED_USER_KEY); articleCountMap = redistemplate.opsForHash ().for(Map.Entry<String, String> entry : articleCountMap.entrySet()) { String articleId = entry.getKey(); Set<Long> userIdSet = FastjsonUtil.deserializeToSet(entry.getValue(), Long.class); / / 1. The synchronization of an article the total number of thumb up to MySQL synchronizeTotalLikeCount (articleId userIdSet); / / 2. Simultaneous users like article synchronizeUserLikeArticle (articleId userIdSet); } logger.info("Time :{}, stop executing Redis data persistence task to MySQL", LocalDateTime.now().format(formatter));
}
Copy the code

Description:

  • To solve the problem of concurrency, addsynchronizeKeyword implementation
  • In addition, there is access to a certain article like the number of users all articles like the number of users like the article method implementation, method implementation is relatively simple not to explain, can be found in the complete code

Existing deficiencies

  • In the user uptick/unuptick method,RedisTransactions are not guaranteed
  • This application is only applicable to the single-machine environment. In the distributed environment, concurrency problems exist and distributed locks need to be completed

After National Day, you are still excited about your vacation

Welcome to fork and star for complete code address