利用Redis實現(xiàn)點贊功能的示例代碼
提到點贊,大家一想到的是不是就是朋友圈的點贊呀?其實點贊對我們來說并不陌生,我們經(jīng)常會在手機軟件或者網(wǎng)頁中看到它,今天就讓我們來了解一下它的實現(xiàn)吧。我們常見的設(shè)計思路大概分為兩種:一種自然是用 MySQL 等數(shù)據(jù)庫直接落地存儲, 另外一種就是將點贊的數(shù)據(jù)保存到 Redis 等緩存里,在一定時間后刷回 MySQL 等數(shù)據(jù)庫。
MySQL 和 Redis優(yōu)缺點
首先我們來說一下兩種方法各自的優(yōu)缺點:我們以 MySQL 和 Redis 為例。
1、直接寫入數(shù)據(jù)庫:
優(yōu)點:這種方法實現(xiàn)簡單,只需完成數(shù)據(jù)庫的增刪改查就行;
缺點:數(shù)據(jù)庫讀寫壓力大,如果遇到熱門文章在短時間內(nèi)被大量點贊的情況,直接操作數(shù)據(jù)庫會給數(shù)據(jù)庫帶來巨大壓力,影響效率。
2、使用 Redis 緩存:
優(yōu)點:性能高,讀寫速度快,緩解數(shù)據(jù)庫讀寫的壓力;
缺點:開發(fā)復(fù)雜,不能保證數(shù)據(jù)安全性即 redis 掛掉的時候會丟失數(shù)據(jù), 同時不及時同步 redis 中的數(shù)據(jù), 可能會在 redis 內(nèi)存置換的時候被淘汰掉。不過對于點贊數(shù)據(jù)我們不需要那么精確,丟失一點數(shù)據(jù)問題不大。
接下來就從以下三個方面對點贊功能做詳細(xì)的介紹
•Redis 緩存設(shè)計
•數(shù)據(jù)庫設(shè)計
•開啟定時任務(wù)持久化存儲到數(shù)據(jù)庫
1、Redis 緩存設(shè)計及實現(xiàn)
Redis 的整合我們在上一篇文章中已經(jīng)介紹過了,此處就不再贅述了。我們了解到,我們在做點贊的時候需要記錄以下幾類數(shù)據(jù):一類是某用戶被其他用戶點贊的詳細(xì)記錄,一類是??紤]到查詢與存取方便快捷,我這邊采用 Hash 結(jié)構(gòu)進(jìn)行存儲,存儲結(jié)構(gòu)如下:
(1)某用戶被其他用戶點贊的詳細(xì)記錄:MAP_USER_LIKED
為鍵值,被點贊用戶id::點贊用戶id為 filed,1或者0為 value
(2)某用戶被點贊的數(shù)量統(tǒng)計:MAP_USER_LIKED_COUNT
為鍵值,被點贊用戶id為 filed,count
為 value
部分代碼如下
/** * 將用戶被其他用戶點贊的數(shù)據(jù)存到redis */ @Override public void saveLiked2Redis(String likedUserId, String likedPostId) { String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId); redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED,key, LikedStatusEnum.LIKE.getCode()); } //取消點贊 @Override public void unlikeFromRedis(String likedUserId, String likedPostId) { String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId); redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED,key,LikedStatusEnum.UNLIKE.getCode()); } /** * 將被點贊用戶的數(shù)量+1 */ @Override public void incrementLikedCount(String likedUserId) { redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT,likedUserId,1); } //-1 @Override public void decrementLikedCount(String likedUserId) { redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, likedUserId, -1); } /** * 獲取Redis中的用戶點贊詳情記錄 */ @Override public List<UserLikeDetail> getLikedDataFromRedis() { Cursor<Map.Entry<Object,Object>> scan = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED, ScanOptions.NONE); List<UserLikeDetail> list = new ArrayList<>(); while (scan.hasNext()){ Map.Entry<Object, Object> entry = scan.next(); String key = (String) entry.getKey(); String[] split = key.split("::"); String likedUserId = split[0]; String likedPostId = split[1]; Integer value = (Integer) entry.getValue(); //組裝成 UserLike 對象 UserLikeDetail userLikeDetail = new UserLikeDetail(likedUserId, likedPostId, value); list.add(userLikeDetail); //存到 list 后從 Redis 中刪除 redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key); } return list; } /** * 獲取Redis中的用戶被點贊數(shù)量 */ @Override public List<UserLikCountDTO> getLikedCountFromRedis() { Cursor<Map.Entry<Object,Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, ScanOptions.NONE); List<UserLikCountDTO> list = new ArrayList<>(); while(cursor.hasNext()){ Map.Entry<Object, Object> map = cursor.next(); String key = (String) map.getKey(); Integer value = (Integer) map.getValue(); UserLikCountDTO userLikCountDTO = new UserLikCountDTO(key,value); list.add(userLikCountDTO); //存到 list 后從 Redis 中刪除 redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT,key); } return list; }
Redis 存儲結(jié)構(gòu)如圖
2、數(shù)據(jù)庫設(shè)計
這里我們可以和直接將點贊數(shù)據(jù)存到數(shù)據(jù)庫一樣,設(shè)計兩張表:
(1)用戶被其他用戶點贊的詳細(xì)記錄:user_like_detail
DROP TABLE IF EXISTS `user_like_detail`; CREATE TABLE `user_like_detail` ( `id` int(11) NOT NULL AUTO_INCREMENT, `liked_user_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '被點贊的用戶id', `liked_post_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '點贊的用戶id', `status` tinyint(1) NULL DEFAULT 1 COMMENT '點贊狀態(tài),0取消,1點贊', `create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '創(chuàng)建時間', `update_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改時間', PRIMARY KEY (`id`) USING BTREE, INDEX `liked_user_id`(`liked_user_id`) USING BTREE, INDEX `liked_post_id`(`liked_post_id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用戶點贊表' ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
(2)用戶被點贊的數(shù)量統(tǒng)計:user_like_count
DROP TABLE IF EXISTS `user_like_count`; CREATE TABLE `user_like_count` ( `id` int(11) NOT NULL AUTO_INCREMENT, `like_num` int(11) NULL DEFAULT 0, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
3、開啟定時任務(wù)持久化存儲到數(shù)據(jù)庫
我們使用 Quartz 來實現(xiàn)定時任務(wù),將 Redis 中的數(shù)據(jù)存儲到數(shù)據(jù)庫中,為了演示效果,我們可以設(shè)置一分鐘或者兩分鐘存儲一遍數(shù)據(jù),這個視具體業(yè)務(wù)而定。在同步數(shù)據(jù)的過程中,我們首先要將 Redis 中的數(shù)據(jù)在數(shù)據(jù)庫中進(jìn)行查重,舍棄重復(fù)數(shù)據(jù),這樣我們的數(shù)據(jù)才會更加準(zhǔn)確。
部分代碼如下
//同步redis的用戶點贊數(shù)據(jù)到數(shù)據(jù)庫 @Override @Transactional public void transLikedFromRedis2DB() { List<UserLikeDetail> list = redisService.getLikedDataFromRedis(); list.stream().forEach(item->{ //查重 UserLikeDetail userLikeDetail = userLikeDetailMapper.selectOne(new LambdaQueryWrapper<UserLikeDetail>() .eq(UserLikeDetail::getLikedUserId, item.getLikedUserId()) .eq(UserLikeDetail::getLikedPostId, item.getLikedPostId())); if (userLikeDetail == null){ userLikeDetail = new UserLikeDetail(); BeanUtils.copyProperties(item, userLikeDetail); //沒有記錄,直接存入 userLikeDetail.setCreateTime(LocalDateTime.now()); userLikeDetailMapper.insert(userLikeDetail); }else{ //有記錄,需要更新 userLikeDetail.setStatus(item.getStatus()); userLikeDetail.setUpdateTime(LocalDateTime.now()); userLikeDetailMapper.updateById(item); } }); } @Override @Transactional public void transLikedCountFromRedis2DB() { List<UserLikCountDTO> list = redisService.getLikedCountFromRedis(); list.stream().forEach(item->{ UserLikeCount user = userLikeCountMapper.selectById(item.getKey()); //點贊數(shù)量屬于無關(guān)緊要的操作,出錯無需拋異常 if (user != null){ Integer likeNum = user.getLikeNum() + item.getValue(); user.setLikeNum(likeNum); //更新點贊數(shù)量 userLikeCountMapper.updateById(user); } }); }
至此我們就實現(xiàn)了基于 Redis 的點贊功能,我們還需要注意一點:查詢用戶點贊情況時,需要同時查詢數(shù)據(jù)庫+緩存中的數(shù)據(jù)。
以上就是利用Redis實現(xiàn)點贊功能的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Redis點贊功能的資料請關(guān)注本站其它相關(guān)文章!
版權(quán)聲明:本站文章來源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。