按时间排序的朋友圈点赞功能
1. 数据结构重新设计
1.1 使用ZSET存储点赞信息
key: post:likes:sorted:{postId}
value: 用户ID作为member,时间戳作为score
这样可以天然支持按时间排序
1.2 保留用户点赞集合(可选)
key: user:likes:{userId}
value: 用户点赞过的postId集合
2. 核心功能实现改进
2.1 点赞/取消点赞Lua脚本
-- KEYS[1]: post:likes:sorted:{postId} (ZSET)
-- KEYS[2]: user:likes:{userId} (SET)
-- ARGV[1]: userId
-- ARGV[2]: postId
-- ARGV[3]: timestamp
-- ARGV[4]: action (1-点赞, 0-取消)
local action = tonumber(ARGV[4])
local userId = ARGV[1]
local postId = ARGV[2]
local timestamp = tonumber(ARGV[3])
if action == 1 then
-- 点赞操作
if redis.call('ZSCORE', KEYS[1], userId) == false then
redis.call('ZADD', KEYS[1], timestamp, userId)
redis.call('SADD', KEYS[2], postId)
return 1 -- 点赞成功
end
return 0 -- 已点赞过
else
-- 取消点赞
if redis.call('ZSCORE', KEYS[1], userId) ~= false then
redis.call('ZREM', KEYS[1], userId)
redis.call('SREM', KEYS[2], postId)
return 1 -- 取消成功
end
return 0 -- 未点赞过
end
2.2 获取按时间排序的点赞用户列表
# 按时间升序获取(从早到晚)
ZRANGE post:likes:sorted:{postId} 0 -1 WITHSCORES
# 按时间降序获取(从晚到早)
ZREVRANGE post:likes:sorted:{postId} 0 -1 WITHSCORES
# 分页获取(示例:获取第2页,每页10条)
ZREVRANGE post:likes:sorted:{postId} 10 19 WITHSCORES
3. Java实现示例
public class SortedLikeService {
private Jedis jedis;
// 点赞/取消点赞
public boolean likePost(long userId, long postId, boolean isLike) {
String script = "上述改进后的Lua脚本";
String sha = jedis.scriptLoad(script);
long result = (long) jedis.evalsha(sha,
Arrays.asList(
"post:likes:sorted:" + postId,
"user:likes:" + userId
),
Arrays.asList(
String.valueOf(userId),
String.valueOf(postId),
String.valueOf(System.currentTimeMillis()),
isLike ? "1" : "0"
));
return result == 1;
}
// 获取按时间排序的点赞用户列表
public List<LikeUser> getLikedUsersByTime(long postId, int page, int size, boolean desc) {
String key = "post:likes:sorted:" + postId;
int start = (page - 1) * size;
int end = start + size - 1;
Set<Tuple> tuples = desc ?
jedis.zrevrangeWithScores(key, start, end) :
jedis.zrangeWithScores(key, start, end);
return tuples.stream()
.map(t -> new LikeUser(
t.getElement(),
(long) t.getScore()))
.collect(Collectors.toList());
}
class LikeUser {
String userId;
long timestamp;
// 构造方法、getter/setter省略
}
}
4. 性能优化补充
- 分页缓存:对热门动态的前几页点赞列表进行缓存
- 二级索引:为每个用户维护一个按时间排序的点赞历史
key: user:likes:sorted:{userId} value: postId作为member,时间戳作为score
- 数据预热:提前加载热门动态的点赞数据
5. 数据一致性增强
- 双写日志:记录所有点赞操作的日志,用于恢复和审计
- 定时同步:定期将Redis数据同步到关系型数据库
- 读写分离:对点赞列表的读取可以使用从节点
6. 扩展功能建议
- 点赞时间格式化:在应用层将时间戳转换为"刚刚"、"5分钟前"等友好格式
- 点赞用户信息缓存:缓存点赞用户的基本信息(头像、昵称)
- 点赞动画效果:根据点赞时间实现不同的动画效果(如最新点赞有特殊效果)
这种改进方案在保持高性能的同时,完美支持了按时间排序展示点赞用户的需求,并且仍然保持了O(logN)的操作复杂度。