使用Redis实现朋友圈点赞功能,包括记录点赞数量、支持查看、点赞和取消点赞操作,以及查看点赞顺序。通过使用ZSet数据结构,可以有效地存储点赞用户ID和点赞时间戳,实现点赞功能。
我们使用 Spring Boot + Redisson + MyBatis 来实现朋友圈点赞功能,其中包括点赞、取消点赞、查看点赞列表和点赞顺序。使用 Redis 的 ZSet 数据结构存储点赞信息,value
为用户 ID,score
为点赞时间的时间戳。
首先,在 pom.xml
中添加依赖:
org.springframework.boot
spring-boot-starter
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.0
org.redisson
redisson-spring-boot-starter
3.16.6
mysql
mysql-connector-java
runtime
在 application.yml
中配置 Redis 连接信息:
spring:
redis:
host: localhost
port: 6379
在 MyBatis 中创建一张 post
表,用于存储朋友圈的帖子信息。数据库只用于存储帖子基本信息,点赞功能全部通过 Redis 实现。
CREATE TABLE post (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
content TEXT NOT NULL,
user_id BIGINT NOT NULL
);
定义 PostMapper
用于操作 post
表。该表只存储帖子相关信息。
@Mapper
public interface PostMapper {
@Select("SELECT * FROM post WHERE id = #{postId}")
Post selectPostById(@Param("postId") Long postId);
@Insert("INSERT INTO post (content, user_id) VALUES (#{content}, #{userId})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertPost(Post post);
}
接下来,我们通过 Redisson 操作 Redis 的 ZSet 结构来实现点赞功能。我们将每个帖子的点赞信息存储在 Redis 中,Key 命名为 post:like:{postId}
,其中 postId
是具体的帖子 ID,value
是用户 ID,score
是点赞的时间戳。
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.util.List;
import java.util.Set;
@Service
public class LikeService {
@Autowired
private RedissonClient redissonClient;
// 点赞操作
public void likePost(Long postId, Long userId) {
String key = "post:like:" + postId;
RScoredSortedSet likeSet = redissonClient.getScoredSortedSet(key);
// 使用当前时间戳作为评分,记录用户ID
likeSet.add(Instant.now().getEpochSecond(), userId);
}
// 取消点赞操作
public void unlikePost(Long postId, Long userId) {
String key = "post:like:" + postId;
RScoredSortedSet likeSet = redissonClient.getScoredSortedSet(key);
likeSet.remove(userId);
}
// 获取点赞用户ID列表(按时间顺序)
public Set getLikes(Long postId) {
String key = "post:like:" + postId;
RScoredSortedSet likeSet = redissonClient.getScoredSortedSet(key);
// 按时间顺序返回用户ID列表
return likeSet.readAll();
}
// 获取点赞用户ID列表(按时间逆序)
public List getLikesByTimeDesc(Long postId) {
String key = "post:like:" + postId;
RScoredSortedSet likeSet = redissonClient.getScoredSortedSet(key);
// 逆序返回点赞用户ID
return likeSet.valueRangeReversed(0, -1);
}
// 获取点赞数量
public int getLikeCount(Long postId) {
String key = "post:like:" + postId;
RScoredSortedSet likeSet = redissonClient.getScoredSortedSet(key);
return likeSet.size();
}
}
实现对 Post
表的操作,创建帖子以及获取帖子信息。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PostService {
@Autowired
private PostMapper postMapper;
public Post createPost(String content, Long userId) {
Post post = new Post();
post.setContent(content);
post.setUserId(userId);
postMapper.insertPost(post);
return post;
}
public Post getPost(Long postId) {
return postMapper.selectPostById(postId);
}
}
定义 PostController
来处理前端请求,包括创建帖子、点赞、取消点赞、获取点赞列表等操作。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Set;
@RestController
@RequestMapping("/posts")
public class PostController {
@Autowired
private PostService postService;
@Autowired
private LikeService likeService;
// 创建帖子
@PostMapping("/create")
public ResponseEntity createPost(@RequestParam String content, @RequestParam Long userId) {
Post post = postService.createPost(content, userId);
return ResponseEntity.ok(post);
}
// 点赞
@PostMapping("/like")
public ResponseEntity likePost(@RequestParam Long postId, @RequestParam Long userId) {
likeService.likePost(postId, userId);
return ResponseEntity.ok("Post liked successfully");
}
// 取消点赞
@PostMapping("/unlike")
public ResponseEntity unlikePost(@RequestParam Long postId, @RequestParam Long userId) {
likeService.unlikePost(postId, userId);
return ResponseEntity.ok("Post unliked successfully");
}
// 获取点赞用户列表(按时间顺序)
@GetMapping("/likes/{postId}")
public ResponseEntity> getLikes(@PathVariable Long postId) {
Set likes = likeService.getLikes(postId);
return ResponseEntity.ok(likes);
}
// 获取点赞用户列表(按时间逆序)
@GetMapping("/likesDesc/{postId}")
public ResponseEntity> getLikesByTimeDesc(@PathVariable Long postId) {
List likes = likeService.getLikesByTimeDesc(postId);
return ResponseEntity.ok(likes);
}
// 获取点赞数量
@GetMapping("/likeCount/{postId}")
public ResponseEntity getLikeCount(@PathVariable Long postId) {
int likeCount = likeService.getLikeCount(postId);
return ResponseEntity.ok(likeCount);
}
}
Post
public class Post {
private Long id;
private String content;
private Long userId;
// Getters and setters
}
在 application.yml
中配置 Redisson 的 Redis 连接:
redisson:
single-server-config:
address: "redis://localhost:6379"
通过 Redis 的 ZSet
数据结构,我们可以高效地实现朋友圈的点赞功能,包括点赞、取消点赞、查看点赞顺序等操作。ZSet
的 value
是用户 ID,score
是点赞时间戳,能够实现按时间顺序排序的点赞列表。
在设计数据模型时,将点赞详情存储到 Redis 中而没有直接存储到数据库中,通常是出于性能、响应速度和并发处理的考虑。但存在数据丢失的风险。让我们来分析一下为什么采用 Redis 存储点赞详情的设计,以及如何应对 Redis 数据丢失的情况。
Redis 是一种内存存储,具有极高的读写性能,适合处理高并发、实时性的场景。下面是选择 Redis 存储点赞详情的几个主要原因:
ZSet
数据结构特别适合处理需要按时间排序的场景(比如按时间查看点赞顺序)。在关系型数据库中,频繁地对点赞数据进行排序查询可能会导致性能瓶颈。尽管 Redis 的性能非常高,但它是内存型数据库,这带来以下几个风险:
Redis 支持持久化机制,通过 RDB 或 AOF 的方式将数据写入磁盘,防止意外宕机或重启后数据丢失。
save 900 1
(900秒内至少有1次修改)来设置保存频率。推荐使用 AOF 持久化,因为它更能保证数据的完整性,虽然带来一定的性能开销。
Redis 配置持久化示例:
save 900 1 # 900秒内至少有1次数据修改,保存RDB快照
appendonly yes # 开启AOF日志
可以设计一个机制,将 Redis 中的点赞数据定期同步到关系型数据库,以此作为备份方案。通过这种策略,即使 Redis 数据丢失,也可以通过数据库进行数据恢复。
最佳案例:
public void likePost(Long postId, Long userId) {
// 1. Redis 中记录点赞
String key = "post:like:" + postId;
RScoredSortedSet likeSet = redissonClient.getScoredSortedSet(key);
likeSet.add(Instant.now().getEpochSecond(), userId);
// 2. 数据库中记录点赞
postLikeMapper.insertLike(postId, userId, Instant.now().getEpochSecond());
}
public void syncLikesToDatabase() {
// 从 Redis 中获取所有点赞数据
for (Post post : allPosts) {
Set likes = likeService.getLikes(post.getId());
for (Long userId : likes) {
postLikeMapper.insertLike(post.getId(), userId, Instant.now().getEpochSecond());
}
}
}
设计一种恢复机制,确保 Redis 数据丢失时,可以从数据库恢复数据。可以定期从数据库读取点赞记录,并将这些记录重新加载到 Redis 中。
恢复 Redis 数据的示例代码:
public void restoreLikesFromDatabase(Long postId) {
// 查询数据库中该帖子的所有点赞记录
List likes = postLikeMapper.selectLikesByPostId(postId);
// 恢复到 Redis 中
String key = "post:like:" + postId;
RScoredSortedSet likeSet = redissonClient.getScoredSortedSet(key);
for (PostLike like : likes) {
likeSet.add(like.getTimestamp(), like.getUserId());
}
}
通常情况下,Redis 的点赞功能是基于以下原则设计的:
通过结合 Redis 的性能优势和数据库的持久化特性,可以设计一个既高效又可靠的点赞功能。