<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Linux下安裝Redis或者Docker下安裝Redis並且啟動(redis-server)
1.在 pom.xml 中引入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2.在啟動類上新增註釋 @EnableCaching
3.編寫 Redis 設定類 RedisConfig
@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 資料格式中 Hash 最合適。
因為 Hash 裡的資料都是存在一個鍵裡,可以通過這個鍵很方便的把所有的點贊資料都取出。這個鍵裡面的資料還可以存成鍵值對的形式,方便存入點贊人、被點贊人和點贊狀態。
設點贊人的 id 為 giveLikeId,被點贊人的 id 為 likeUserId ,點贊時狀態為 1,取消點贊狀態為 0。將點贊人 id 和被點贊人 id 作為鍵,兩個 id 中間用 :: 隔開,點贊狀態作為值。
類似於這樣:
在service層操作Redis
public interface RedisService { /** * 點贊。狀態為1 * @param likedUserId * @param giveLikeId */ void saveLiked2Redis(String likedUserId, String giveLikeId); /** * 取消點贊。將狀態改變為0 * @param likedUserId * @param giveLikeId */ void unlikeFromRedis(String likedUserId, String giveLikeId); /** * 從Redis中刪除一條點贊資料 * @param likedUserId * @param giveLikeId */ void deleteLikedFromRedis(String likedUserId, String giveLikeId); /** * 該使用者的點贊數加1 * @param likedUserId */ void incrementLikedCount(String likedUserId); /** * 該使用者的點贊數減1 * @param likedUserId */ void decrementLikedCount(String likedUserId); /** * 獲取Redis中儲存的所有點贊資料 * @return */ List<UserLike> getLikedDataFromRedis(); /** * 獲取Redis中儲存的所有點贊數量 * @return */ List<LikedCountDTO> getLikedCountFromRedis();}
實現類 RedisServiceImpl :
@Service @Slf4j public class RedisServiceImpl implements RedisService { @Autowired RedisTemplate redisTemplate; @Autowired LikedService likedService; @Override public void saveLiked2Redis(String likedUserId, String giveLikeId) { String key = RedisKeyUtils.getLikedKey(likedUserId, giveLikeId); redisTemplate.opsForHash().put(RedisKeyUtils.MAP_USER_LIKED, key, LikedStatusEnum.LIKE.getCode()); } @Override public void unlikeFromRedis(String likedUserId, String giveLikeId) { String key = RedisKeyUtils.getLikedKey(likedUserId, giveLikeId); redisTemplate.opsForHash().put(RedisKeyUtils.MAP_USER_LIKED, key, LikedStatusEnum.UNLIKE.getCode()); } @Override public void deleteLikedFromRedis(String likedUserId, String giveLikeId) { String key = RedisKeyUtils.getLikedKey(likedUserId, giveLikeId); redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_USER_LIKED, key); } @Override public void incrementLikedCount(String likedUserId) { redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_USER_LIKED_COUNT,likedUserId,1); } @Override public void decrementLikedCount(String likedUserId) { redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_USER_LIKED_COUNT,likedUserId,-1); } @Override public List<UserLike> getLikedDataFromRedis() { Cursor<Map.Entry<Object, Object>> cursor =redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_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,giveLikeId String[] split = key.split("::"); String likedUserId = split[0]; String giveLikeId = split[1]; Integer value = (Integer) entry.getValue(); //組裝成 UserLike 物件 UserLike userLike = new UserLike(likedUserId, giveLikeId, value); list.add(userLike); //存到 list 後從 Redis 中刪除 redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_USER_LIKED, key); } return list; } @Override public List<LikedCountDTO> getLikedCountFromRedis() { Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_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_USER_LIKED_COUNT, key); } return list; } }
RedisKeyUtils, 用於根據一定規則生成 key
public class RedisKeyUtils { //儲存使用者點贊資料的key public static final String MAP_USER_LIKED = "MAP_USER_LIKED"; //儲存使用者被點贊數量的key public static final String MAP_USER_LIKED_COUNT = "MAP_USER_LIKED_COUNT"; /** * 拼接被點讚的使用者id和點讚的人的id作為key。格式 001::1006 * @param likedUserId 被點讚的人id * @param giveLikeId 點讚的人的id * @return */ public static String getLikedKey(String likedUserId, String giveLikeId){ StringBuilder builder = new StringBuilder(); builder.append(likedUserId); builder.append("::"); builder.append(giveLikeId); return builder.toString(); } }
LikedStatusEnum 使用者點贊狀態的列舉類
/** * 使用者點讚的狀態 */ @Getter public enum LikedStatusEnum { LIKE(1, "點贊"), UNLIKE(0, "取消點贊/未點贊"), ; private Integer code; private String msg; LikedStatusEnum(Integer code, String msg) { this.code = code; this.msg = msg; } }
資料庫表中至少要包含三個欄位:被點贊使用者 id,點贊使用者 id,點贊狀態。再加上主鍵 id,建立時間,修改時間就行了。
create table `user_like`( `id` int not null auto_increment, `liked_user_id` varchar(32) not null comment '被點讚的使用者id', `give_liked_id` varchar(32) not null comment '點讚的使用者id', `status` tinyint(1) default '1' comment '點贊狀態,0取消,1點贊', `create_time` timestamp not null default current_timestamp comment '建立 時間', `update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改時間', primary key(`id`), INDEX `liked_user_id`(`liked_user_id`), INDEX `give_liked_id`(`give_liked_id`) ) comment '使用者點贊表';
對應的物件 UserLike
/** * 使用者點贊表 */ @Entity @Data public class UserLike { //主鍵id @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; //被點讚的使用者的id private String likedUserId; //點讚的使用者的id private String giveLikedId; //點讚的狀態.預設未點贊 private Integer status = LikedStatusEnum.UNLIKE.getCode(); public UserLike() { } public UserLike(String likedUserId, String giveLikedId, Integer status) { this.likedUserId = likedUserId; this.giveLikedId = giveLikedId; this.status = status; } }
在service層運算元據庫
public interface LikedService { /** * 儲存點贊記錄 * @param userLike * @return */ UserLike save(UserLike userLike); /** * 批次儲存或修改 * @param list */ List<UserLike> saveAll(List<UserLike> list); /** * 根據被點贊人的id查詢點贊列表(即查詢都誰給這個人點贊過) * @param likedUserId 被點贊人的id * @param pageable * @return */ Page<UserLike> getLikedListByLikedUserId(String likedUserId, Pageable pageable); /** * 根據點贊人的id查詢點贊列表(即查詢這個人都給誰點贊過) * @param giveLikedId * @param pageable * @return */ Page<UserLike> getLikedListByGiveLikedId(String giveLikedId, Pageable pageable); /** * 通過被點贊人和點贊人id查詢是否存在點贊記錄 * @param likedUserId * @param giveLikedId * @return */ UserLike getByLikedUserIdAndGiveLikedId(String likedUserId, String giveLikedId); /** * 將Redis裡的點贊資料存入資料庫中 */ void transLikedFromRedis2DB(); /** * 將Redis中的點贊數量資料存入資料庫 */ void transLikedCountFromRedis2DB(); }
LikedServiceImpl 實現類
@Service @Slf4j public class LikedServiceImpl implements LikedService { @Autowired UserLikeRepository likeRepository; @Autowired RedisService redisService; @Autowired UserService userService; @Override @Transactional public UserLike save(UserLike userLike) { return likeRepository.save(userLike); } @Override @Transactional public List<UserLike> saveAll(List<UserLike> list) { return likeRepository.saveAll(list); } @Override public Page<UserLike> getLikedListByLikedUserId(String likedUserId, Pageable pageable) { return likeRepository.findByLikedUserIdAndStatus(likedUserId, LikedStatusEnum.LIKE.getCode(), pageable); } @Override public Page<UserLike> getLikedListByGiveLikedId(String giveLikedId, Pageable pageable) { return likeRepository.findByGiveLikedIdAndStatus(giveLikedId, LikedStatusEnum.LIKE.getCode(), pageable); } @Override public UserLike getByLikedUserIdAndGiveLikedId(String likedUserId, String giveLikedId) { return likeRepository.findByLikedUserIdAndLikedPostId(likedUserId, giveLikedId); } @Override @Transactional public void transLikedFromRedis2DB() { List<UserLike> list = redisService.getLikedDataFromRedis(); for (UserLike like : list) { UserLike ul = getByLikedUserIdAndGiveLikedId(like.getLikedUserId(), like.getGiveLikedId()); if (ul == null){ //沒有記錄,直接存入 save(like); }else{ //有記錄,需要更新 ul.setStatus(like.getStatus()); save(ul); } } } @Override @Transactional public void transLikedCountFromRedis2DB() { List<LikedCountDTO> list = redisService.getLikedCountFromRedis(); for (LikedCountDTO dto : list) { UserInfo user = userService.findById(dto.getId()); //點贊數量屬於無關緊要的操作,出錯無需拋異常 if (user != null){ Integer likeNum = user.getLikeNum() + dto.getCount(); user.setLikeNum(likeNum); //更新點贊數量 userService.updateInfo(user); } } } }
這裡使用的是定時任務 Quartz框架
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
@Configuration public class QuartzConfig { private static final String LIKE_TASK_IDENTITY = "LikeTaskQuartz"; @Bean public JobDetail quartzDetail(){ return JobBuilder.newJob(LikeTask.class).withIdentity(LIKE_TASK_IDENTITY).storeDurably().build(); } @Bean public Trigger quartzTrigger(){ SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(10) //設定時間週期單位秒 .withIntervalInHours(2) //兩個小時執行一次 .repeatForever(); return TriggerBuilder.newTrigger().forJob(quartzDetail()) .withIdentity(LIKE_TASK_IDENTITY) .withSchedule(scheduleBuilder) .build(); } }
/** * 點讚的定時任務 */ @Slf4j public class LikeTask extends QuartzJobBean { @Autowired LikedService likedService; private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { log.info("LikeTask-------- {}", sdf.format(new Date())); //將 Redis 裡的點贊資訊同步到資料庫裡 likedService.transLikedFromRedis2DB(); likedService.transLikedCountFromRedis2DB(); } }
在定時任務中直接呼叫 LikedService 封裝的方法完成資料同步。
1.點贊 / 取消點贊 跟 點贊數 +1/ -1 應該保證是原子操作 ,不然出現並行問題就會有兩條重複的點贊記錄 , 所以要給整個原子操作加鎖 。
2.同時需要在 Spring Boot 的系統關閉勾點函數中補充同步 redis 中點贊資料到 mysql 中的過程 . 不然有可能出現距離上一次同步 1 小時 59 分的時候伺服器更新 , 把整整兩小時的點贊資料都給清空了 。
到此這篇關於手把手教你用Redis 實現點贊功能並且與資料庫同步的文章就介紹到這了,更多相關Redis 點贊內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45