企业级开发 如何设计数据库和缓存来实现微信朋友圈好友可见

下面我将详细分析微信朋友圈"好友可见"功能的实现流程,并使用Mermaid语法绘制时序图。

核心流程分析

1. 数据模型

  • 用户关系:使用Redis的Set存储每个用户的好友列表(user:{userId}:friends)。
  • 朋友圈内容:使用Redis的Hash存储朋友圈信息,包含可见性字段(PUBLIC/FRIENDS/PRIVATE)。
  • 索引结构:使用Sorted Set按时间排序存储用户的朋友圈ID(user:{userId}:moments)。

2. 发布流程

  1. 用户创建朋友圈并设置可见性
  2. 系统将内容存入Redis Hash
  3. 将朋友圈ID添加到用户的Sorted Set
  4. 如果是"好友可见",还会加入全局好友可见集合(moments:friends

3. 查询流程

  1. 获取当前用户的好友列表
  2. 遍历好友,获取每个好友的最新朋友圈ID
  3. 批量获取朋友圈内容
  4. 检查每条朋友圈的可见性权限
  5. 返回符合条件的朋友圈列表

逻辑:

1. 数据模型定义

// User.java - 用户模型
public class User {
    private String userId;
    private String username;
    // getter/setter/constructors
}

// Moment.java - 朋友圈模型
public class Moment {
    private String momentId;
    private String userId;          // 发布者ID
    private String content;         // 内容
    private long timestamp;         // 发布时间
    private Visibility visibility;  // 可见性
    
    public enum Visibility {
        PUBLIC,     // 公开
        FRIENDS,    // 仅好友可见
        PRIVATE     // 仅自己可见
    }
    // getter/setter/constructors
}

2. Redis工具类

// RedisUtil.java - Redis连接工具
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisUtil {
    private static final JedisPool jedisPool;
    
    static {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(100);
        config.setMaxIdle(10);
        config.setMinIdle(5);
        jedisPool = new JedisPool(config, "localhost", 6379);
    }
    
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }
    
    public static void close(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}

3. 好友关系管理服务

// FriendService.java - 好友关系管理
import redis.clients.jedis.Jedis;
import java.util.Set;

public class FriendService {
    
    // 添加好友(双向关系)
    public void addFriend(String userId, String friendId) {
        try (Jedis jedis = RedisUtil.getJedis()) {
            jedis.sadd(getFriendsKey(userId), friendId);
            jedis.sadd(getFriendsKey(friendId), userId);
        }
    }
    
    // 移除好友
    public void removeFriend(String userId, String friendId) {
        try (Jedis jedis = RedisUtil.getJedis()) {
            jedis.srem(getFriendsKey(userId), friendId);
            jedis.srem(getFriendsKey(friendId), userId);
        }
    }
    
    // 判断是否为好友
    public boolean isFriend(String userId, String targetId) {
        try (Jedis jedis = RedisUtil.getJedis()) {
            return jedis.sismember(getFriendsKey(userId), targetId);
        }
    }
    
    // 获取用户的好友列表
    public Set<String> getFriends(String userId) {
        try (Jedis jedis = RedisUtil.getJedis()) {
            return jedis.smembers(getFriendsKey(userId));
        }
    }
    
    private String getFriendsKey(String userId) {
        return "user:" + userId + ":friends";
    }
}

4. 朋友圈服务

// MomentService.java - 朋友圈核心服务
import redis.clients.jedis.Jedis;
import java.util.*;

public class MomentService {
    private final FriendService friendService = new FriendService();
    
    // 发布朋友圈
    public void publishMoment(Moment moment) {
        try (Jedis jedis = RedisUtil.getJedis()) {
            // 存储朋友圈内容到Hash
            String momentKey = getMomentKey(moment.getMomentId());
            jedis.hset(momentKey, "userId", moment.getUserId());
            jedis.hset(momentKey, "content", moment.getContent());
            jedis.hset(momentKey, "timestamp", String.valueOf(moment.getTimestamp()));
            jedis.hset(momentKey, "visibility", moment.getVisibility().name());
            
            // 添加到用户的朋友圈Sorted Set(按时间排序)
            String userMomentsKey = getUserMomentsKey(moment.getUserId());
            jedis.zadd(userMomentsKey, moment.getTimestamp(), moment.getMomentId());
            
            // 如果是好友可见,添加到全局好友可见集合
            if (moment.getVisibility() == Moment.Visibility.FRIENDS) {
                jedis.sadd("moments:friends", moment.getMomentId());
            }
        }
    }
    
    // 获取用户可见的朋友圈列表(分页)
    public List<Moment> getVisibleMoments(String viewerId, int page, int pageSize) {
        List<Moment> result = new ArrayList<>();
        try (Jedis jedis = RedisUtil.getJedis()) {
            // 获取当前用户的好友列表
            Set<String> friendIds = friendService.getFriends(viewerId);
            
            // 遍历好友,获取每个好友的最新朋友圈
            for (String friendId : friendIds) {
                String userMomentsKey = getUserMomentsKey(friendId);
                // 获取好友最新的朋友圈ID(按时间倒序)
                Set<String> momentIds = jedis.zrevrange(
                    userMomentsKey, 
                    (page - 1) * pageSize, 
                    page * pageSize - 1
                );
                
                // 批量获取朋友圈内容
                for (String momentId : momentIds) {
                    String momentKey = getMomentKey(momentId);
                    Map<String, String> momentData = jedis.hgetAll(momentKey);
                    
                    // 转换为Moment对象
                    Moment moment = mapToMoment(momentId, momentData);
                    
                    // 检查可见性权限
                    if (canView(viewerId, moment)) {
                        result.add(moment);
                    }
                }
            }
            
            // 按时间倒序排序(确保全局时间顺序)
            result.sort(Comparator.comparingLong(Moment::getTimestamp).reversed());
            return result;
        }
    }
    
    // 判断用户是否有权限查看朋友圈
    private boolean canView(String viewerId, Moment moment) {
        if (moment.getVisibility() == Moment.Visibility.PUBLIC) {
            return true; // 公开内容所有人可见
        } else if (moment.getVisibility() == Moment.Visibility.PRIVATE) {
            return viewerId.equals(moment.getUserId()); // 仅自己可见
        } else { // FRIENDS
            return friendService.isFriend(viewerId, moment.getUserId()); // 仅好友可见
        }
    }
    
    // 将Redis Hash数据转换为Moment对象
    private Moment mapToMoment(String momentId, Map<String, String> data) {
        Moment moment = new Moment();
        moment.setMomentId(momentId);
        moment.setUserId(data.get("userId"));
        moment.setContent(data.get("content"));
        moment.setTimestamp(Long.parseLong(data.get("timestamp")));
        moment.setVisibility(Moment.Visibility.valueOf(data.get("visibility")));
        return moment;
    }
    
    private String getMomentKey(String momentId) {
        return "moment:" + momentId;
    }
    
    private String getUserMomentsKey(String userId) {
        return "user:" + userId + ":moments";
    }
}

5. 测试代码示例

// Main.java - 功能测试示例
public class Main {
    public static void main(String[] args) {
        FriendService friendService = new FriendService();
        MomentService momentService = new MomentService();
        
        // 创建用户
        String userA = "user1001";
        String userB = "user1002";
        String userC = "user1003";
        
        // 添加好友关系
        friendService.addFriend(userA, userB); // A和B是好友
        friendService.addFriend(userA, userC); // A和C是好友
        
        // 用户B发布一条朋友圈(仅好友可见)
        Moment momentB = new Moment(
            UUID.randomUUID().toString(),
            userB,
            "今天去爬山了,风景不错!",
            System.currentTimeMillis(),
            Moment.Visibility.FRIENDS
        );
        momentService.publishMoment(momentB);
        
        // 用户C发布一条朋友圈(公开)
        Moment momentC = new Moment(
            UUID.randomUUID().toString(),
            userC,
            "新做的菜,大家看看怎么样?",
            System.currentTimeMillis(),
            Moment.Visibility.PUBLIC
        );
        momentService.publishMoment(momentC);
        
        // 用户A获取可见的朋友圈列表
        List<Moment> visibleMoments = momentService.getVisibleMoments(userA, 1, 10);
        
        // 输出结果
        System.out.println("用户A可见的朋友圈:");
        for (Moment moment : visibleMoments) {
            System.out.println("[" + moment.getUserId() + "] " + 
                              moment.getContent() + " (时间: " + 
                              new Date(moment.getTimestamp()) + ")");
        }
    }
}

核心流程说明

  1. 好友关系管理
    • 使用Redis的Set存储每个用户的好友列表(user:{userId}:friends
    • 添加/删除好友时维护双向关系
    • 通过SISMEMBER快速判断两个用户是否为好友
  1. 朋友圈发布
    • 将朋友圈内容存储为Redis的Hash结构
    • 使用Sorted Set按时间排序存储用户的朋友圈ID
    • 对于"好友可见"的内容,额外添加到全局集合moments:friends
  1. 可见性控制
    • 公开内容:所有用户可见
    • 私密内容:仅发布者可见
    • 好友可见内容:仅发布者的好友可见
  1. 查询流程
    • 获取当前用户的好友列表
    • 遍历好友,获取每个好友的最新朋友圈ID
    • 批量获取朋友圈内容并检查可见性权限
    • 最终按时间排序返回结果

优化建议

  1. 批量操作
    使用Redis Pipeline或MGET命令减少网络开销:
// 批量获取多个朋友圈内容示例
List<String> momentKeys = momentIds.stream()
    .map(id -> "moment:" + id)
    .collect(Collectors.toList());
List<String> results = jedis.mget(momentKeys.toArray(new String[0]));
  1. 缓存热点数据
    对于活跃用户的好友列表和朋友圈内容,可以使用本地缓存(如Guava Cache):
LoadingCache<String, Set<String>> friendCache = CacheBuilder.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build(new CacheLoader<String, Set<String>>() {
        @Override
        public Set<String> load(String userId) {
            return friendService.getFriends(userId);
        }
    });
  1. 异步处理
    点赞、评论等非核心操作可以通过消息队列异步处理,避免阻塞主流程。

这个实现充分利用了Redis的集合和排序功能,能够高效地实现朋友圈的好友可见性控制。在实际生产环境中,还需要考虑分布式锁、限流降级、数据持久化等问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员多多_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值