阿里真题:千万用户社交系统设计
面试重要程度:⭐⭐⭐⭐⭐
真题来源:阿里巴巴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圣经
阿里巴巴公司氛围 661人发布