阿里真题:千万用户社交系统设计

面试重要程度:⭐⭐⭐⭐⭐

真题来源:阿里巴巴2024春招技术面试

考察重点:大规模系统架构设计、高并发处理、数据存储方案

预计阅读时间:50分钟

真题背景

面试官: "设计一个支持千万用户的社交系统,类似微博。需要支持用户关注、发布动态、点赞评论、消息推送等功能。系统要求支持每秒10万QPS,99.9%可用性,用户动态能够实时推送给粉丝。请详细设计系统架构,包括数据库设计、缓存策略、消息推送方案等。"

考察意图:

  • 大规模分布式系统架构设计能力
  • 高并发场景下的技术方案选择
  • 数据存储和缓存的合理设计
  • 实时推送和消息系统设计

🎯 需求分析与架构设计

系统需求分析

/**
 * 千万用户社交系统需求定义
 */
public class SocialSystemRequirements {
    
    // 用户规模
    public static final long TOTAL_USERS = 10_000_000;        // 千万用户
    public static final long DAILY_ACTIVE_USERS = 2_000_000;  // 日活200万
    public static final long PEAK_CONCURRENT_USERS = 500_000; // 峰值并发50万
    
    // 性能需求
    public static final long MAX_QPS = 100_000;               // 10万QPS
    public static final int MAX_LATENCY_MS = 200;             // 响应时间<200ms
    public static final double AVAILABILITY = 99.9;           // 99.9%可用性
    
    // 业务数据量估算
    public static final long DAILY_POSTS = 1_000_000;         // 日发布100万条动态
    public static final long DAILY_INTERACTIONS = 50_000_000; // 日互动5000万次
    public static final int AVG_FOLLOWERS = 100;              // 平均粉丝数100
    public static final int MAX_FOLLOWERS = 10_000_000;       // 最大粉丝数1000万
}

整体架构设计

/**
 * 社交系统整体架构
 */
@Component
public class SocialSystemArchitecture {
    
    /**
     * 核心微服务设计
     */
    @Service
    public class UserService {
        
        @Autowired
        private UserRepository userRepository;
        
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
        
        /**
         * 用户注册
         */
        public RegisterResult register(RegisterRequest request) {
            try {
                // 1. 参数验证
                validateRegisterRequest(request);
                
                // 2. 检查用户名/手机号是否已存在
                if (userRepository.existsByUsername(request.getUsername())) {
                    return RegisterResult.error("用户名已存在");
                }
                
                // 3. 创建用户
                User user = User.builder()
                    .username(request.getUsername())
                    .phone(request.getPhone())
                    .password(passwordEncoder.encode(request.getPassword()))
                    .nickname(request.getNickname())
                    .status(UserStatus.ACTIVE)
                    .createTime(new Date())
                    .build();
                
                user = userRepository.save(user);
                
                // 4. 初始化用户相关数据
                initUserData(user.getId());
                
                // 5. 缓存用户信息
                String cacheKey = "user:profile:" + user.getId();
                redisTemplate.opsForValue().set(cacheKey, user, Duration.ofHours(2));
                
                return RegisterResult.success(user.getId());
                
            } catch (Exception e) {
                log.error("User registration failed", e);
                return RegisterResult.error("注册失败");
            }
        }
        
        /**
         * 获取用户资料
         */
        @Cacheable(value = "user:profile", key = "#userId")
        public UserProfile getUserProfile(Long userId) {
            User user = userRepository.findById(userId);
            if (user == null) {
                return null;
            }
            
            UserStats stats = getUserStats(userId);
            
            return UserProfile.builder()
                .userId(user.getId())
                .username(user.getUsername())
                .nickname(user.getNickname())
                .avatar(user.getAvatar())
                .followingCount(stats.getFollowingCount())
                .followersCount(stats.getFollowersCount())
                .postsCount(stats.getPostsCount())
                .build();
        }
    }
    
    /**
     * 关注服务
     */
    @Service
    public class FollowService {
        
        @Autowired
        private FollowRepository followRepository;
        
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
        
        /**
         * 关注用户
         */
        @Transactional
        public FollowResult followUser(Long followerId, Long followeeId) {
            try {
                // 1. 参数验证
                if (followerId.equals(followeeId)) {
                    return FollowResult.error("不能关注自己");
                }
                
                // 2. 检查是否已关注
                if (isFollowing(followerId, followeeId)) {
                    return FollowResult.error("已经关注该用户");
                }
                
                // 3. 创建关注关系
                Follow follow = Follow.builder()
                    .followerId(followerId)
                    .followeeId(followeeId)
                    .createTime(new Date())
                    .build();
                
                followRepository.save(follow);
                
                // 4. 更新统计数据
                updateFollowStats(followerId, followeeId, 1);
                
                // 5. 更新缓存
                updateFollowCache(followerId, followeeId, true);
                
                // 6. 发送关注通知
                notificationService.sendFollowNotification(followerId, followeeId);
                
                // 7. 触发时间线更新
                timelineService.onUserFollowed(followerId, followeeId);
                
                return FollowResult.success();
                
            } catch (Exception e) {
                log.error("Follow user failed", e);
                return FollowResult.error("关注失败");
            }
        }
        
        /**
         * 检查关注关系
         */
        public boolean isFollowing(Long followerId, Long followeeId) {
            String cacheKey = "follow:relation:" + followerId + ":" + followeeId;
            
            Boolean cached = (Boolean) redisTemplate.opsForValue().get(cacheKey);
            if (cached != null) {
                return cached;
            }
            
            boolean isFollowing = followRepository.existsByFollowerIdAndFolloweeId(followerId, followeeId);
            redisTemplate.opsForValue().set(cacheKey, isFollowing, Duration.ofHours(1));
            
            return isFollowing;
        }
    }
}

📱 动态发布与时间线设计

动态发布服务

/**
 * 动态发布服务
 */
@Service
public class PostService {
    
    @Autowired
    private PostRepository postRepository;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    /**
     * 发布动态
     */
    @Transactional
    public PostResult publishPost(PublishPostRequest request) {
        try {
            // 1. 内容审核
            ContentAuditResult auditResult = contentAuditService.auditContent(request.getContent());
            if (!auditResult.isPass()) {
                return PostResult.error("内容审核未通过");
            }
            
            // 2. 创建动态
            Post post = Post.builder()
                .userId(request.getUserId())
                .content(request.getContent())
                .images(request.getImages())
                .type(request.getType())
                .status(PostStatus.PUBLISHED)
                .createTime(new Date())
                .build();
            
            post = postRepository.save(post);
            
            // 3. 更新用户统计
            userStatsService.incrementPostsCount(request.getUserId());
            
            // 4. 缓存动态
            String cacheKey = "post:detail:" + post.getId();
            redisTemplate.opsForValue().set(cacheKey, post, Duration.ofHours(6));
            
            // 5. 异步推送到时间线
            PostPublishedEvent event = PostPublishedEvent.builder()
                .postId(post.getId())
                .userId(request.getUserId())
                .publishTime(post.getCreateTime())
                .build();
            
            rabbitTemplate.convertAndSend("post.exchange", "post.published", event);
            
            return PostResult.success(post.getId());
            
        } catch (Exception e) {
            log.error("Publish post failed", e);
            return PostResult.error("发布失败");
        }
    }
    
    /**
     * 获取动态详情
     */
    public PostDetail getPostDetail(Long postId, Long viewerId) {
        String cacheKey = "post:detail:" + postId;
        
        PostDetail cached = (PostDetail) redisTemplate.opsForValue().get(cacheKey);
        if (cached != null) {
            setUserRelatedInfo(cached, viewerId);
            return cached;
        }
        
        Post post = postRepository.findById(postId);
        if (post == null) {
            return null;
        }
        
        PostDetail detail = buildPostDetail(post, viewerId);
        redisTemplate.opsForValue().set(cacheKey, detail, Duration.ofHours(6));
        
        return detail;
    }
}

时间线服务设计

/**
 * 时间线服务 - 推拉结合策略
 */
@Service
public class TimelineService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 大V用户阈值(粉丝数超过10万)
    private static final int BIG_V_THRESHOLD = 100000;
    
    /**
     * 处理动态发布事件
     */
    @RabbitListener(queues = "post.published.queue")
    public void handlePostPublished(PostPublishedEvent event) {
        Long userId = event.getUserId();
        Long postId = event.getPostId();
        
        // 获取用户粉丝数
        int followersCount = userStatsService.getFollowersCount(userId);
        
        if (followersCount <= BIG_V_THRESHOLD) {
            // 普通用户:推模式,直接推送到粉丝时间线
    

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论
欢迎讨论
点赞 回复 分享
发布于 09-06 11:26 江西

相关推荐

昨天 13:12
已编辑
门头沟学院 Java
斯卡蒂味的鱼汤:知道你不会来数马,就不捞你😂最近数马疯狂扩招,招聘要求挺低的,你能力肯定够,应该就是因为太强了,知道你不会来才不捞你
投递腾讯云智研发等公司10个岗位
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务