SpringBoot+MySQL设计高并发IM系统好友与群组管理的核心逻辑
1. 关系链数据模型设计精要
即时通讯系统中,关系链设计直接决定了系统的扩展性和性能上限。我们采用双向好友关系模型,每条记录包含from_id、to_id、status、remark等核心字段,通过组合索引优化查询性能:
CREATE TABLE `im_friendship` ( `id` bigint NOT NULL AUTO_INCREMENT, `app_id` int NOT NULL COMMENT '应用ID', `from_id` varchar(32) NOT NULL COMMENT '用户A', `to_id` varchar(32) NOT NULL COMMENT '用户B', `status` tinyint DEFAULT '0' COMMENT '0未添加 1正常 2删除', `black` tinyint DEFAULT '0' COMMENT '1拉黑', `sequence` bigint DEFAULT NULL COMMENT '序列号', `remark` varchar(64) DEFAULT NULL COMMENT '备注', `add_source` varchar(16) DEFAULT NULL COMMENT '来源', `extra` json DEFAULT NULL COMMENT '扩展字段', `create_time` bigint DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_unique_relation` (`app_id`,`from_id`,`to_id`), KEY `idx_to_id` (`to_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4关键设计决策:
- 采用双写机制:用户A添加B时,同时写入A→B和B→A两条记录
- 使用
sequence字段实现多端同步,通过Redis生成全局递增序列 extra字段采用JSON类型存储动态属性,避免频繁修改表结构
2. 高并发好友申请处理方案
好友申请流程需要处理去重、幂等和状态同步三大核心问题。我们设计了三层校验体系:
- 内存级过滤:使用Guava Cache缓存最近操作记录
- 数据库校验:通过SELECT...FOR UPDATE实现行级锁
- 最终一致性检查:通过定时任务补偿异常状态
@Transactional public ResponseVO handleFriendApply(FriendApplyDTO dto) { // 1. 校验基础参数 ParamValidator.validate(dto); // 2. 获取分布式锁 String lockKey = "friend_apply:" + dto.getFromId() + ":" + dto.getToId(); if (!redisLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) { throw new BusinessException("操作过于频繁"); } try { // 3. 查询现有申请记录 FriendApply existApply = applyMapper.selectOne(new LambdaQueryWrapper<FriendApply>() .eq(FriendApply::getFromId, dto.getFromId()) .eq(FriendApply::getToId, dto.getToId()) .last("FOR UPDATE")); // 4. 状态机处理 if (existApply == null) { // 新建申请流程 FriendApply newApply = buildNewApply(dto); applyMapper.insert(newApply); sendApplyNotification(newApply); } else { // 更新已有申请 processExistApply(existApply, dto); } return ResponseVO.success(); } finally { redisLock.unlock(lockKey); } }性能优化点:
- 采用异步写扩散模式,通过消息队列解耦核心流程与通知逻辑
- 申请列表查询使用游标分页,避免深度分页性能问题
- 高频查询走多级缓存(Redis → Caffeine → DB)
3. 群组权限体系设计
IM群组管理的复杂度主要来自角色权限系统。我们设计了基于RBAC模型的五层权限体系:
| 角色类型 | 权限项 | 数值 | 二进制位 |
|---|---|---|---|
| 群主 | 解散群 | 1 | 00000001 |
| 管理员 | 踢人 | 2 | 00000010 |
| 管理员 | 禁言 | 4 | 00000100 |
| 成员 | 发言 | 8 | 00001000 |
| 成员 | 退群 | 16 | 00010000 |
public class GroupPermission { private static final Map<Integer, String> PERMISSION_MAP = ImmutableMap.of( 1, "OWNER", 2, "ADMIN_KICK", 4, "ADMIN_MUTE", 8, "MEMBER_SPEAK", 16, "MEMBER_QUIT" ); public static boolean hasPermission(int role, int permission) { return (role & permission) == permission; } public static int addPermission(int role, int permission) { return role | permission; } }典型业务场景处理:
public void transferGroupOwner(String groupId, String operator, String newOwner) { // 1. 校验操作者权限 GroupMember operatorMember = getMember(groupId, operator); if (!GroupPermission.hasPermission(operatorMember.getRole(), 1)) { throw new BusinessException("无转让权限"); } // 2. 更新群主身份 GroupMember newOwnerMember = getMember(groupId, newOwner); groupMemberMapper.updateRole(operator, 0); // 原群主降级 groupMemberMapper.updateRole(newOwner, 1); // 新群主升级 // 3. 记录操作日志 GroupOperateLog log = new GroupOperateLog(); log.setOperateType("TRANSFER"); log.setContent(operator + "将群主转让给" + newOwner); operateLogMapper.insert(log); // 4. 通知所有群成员 GroupNotifyMessage message = buildTransferMessage(operator, newOwner); messageProducer.sendGroupMessage(groupId, message); }4. 高性能关系校验方案
好友关系校验在IM系统中属于高频操作,我们设计了多级缓存策略:
布隆过滤器:快速排除非好友关系
// 初始化布隆过滤器 BloomFilter<String> friendBloomFilter = BloomFilter.create( Funnels.stringFunnel(Charset.defaultCharset()), 1000000, 0.01); // 添加关系时更新 public void afterAddFriend(String userId1, String userId2) { String key1 = userId1 + ":" + userId2; String key2 = userId2 + ":" + userId1; friendBloomFilter.put(key1); friendBloomFilter.put(key2); }Redis缓存:存储完整关系数据
# 好友关系存储结构 HSET im:friend:{appId}:{fromId} {toId} {status}|{black}|{timestamp}数据库查询:最终一致性检查
/* 双向关系校验SQL */ SELECT a.from_id, a.to_id, CASE WHEN a.status=1 AND b.status=1 THEN 1 WHEN a.status=1 AND b.status!=1 THEN 2 WHEN a.status!=1 AND b.status=1 THEN 3 ELSE 4 END AS relation_status FROM im_friendship a JOIN im_friendship b ON a.from_id = b.to_id AND a.to_id = b.from_id WHERE a.app_id=#{appId} AND a.from_id=#{fromId} AND a.to_id IN (<foreach collection='toIds' item='id'>#{id}</foreach>)
性能对比:
| 方案 | QPS | 平均耗时 | 适用场景 |
|---|---|---|---|
| 纯DB查询 | 1200 | 45ms | 低频复杂查询 |
| Redis缓存 | 15000 | 8ms | 高频简单校验 |
| 内存缓存 | 50000 | 2ms | 极端高频场景 |
5. 群成员列表分页优化
大群成员列表查询是典型的高消耗操作,我们采用冷热数据分离策略:
热数据:最近活跃成员存储在Redis ZSET中
// 成员活跃度更新 public void updateMemberActive(String groupId, String userId) { String key = "group:active:" + groupId; redisTemplate.opsForZSet().add(key, userId, System.currentTimeMillis()); // 保持最近200个活跃成员 redisTemplate.opsForZSet().removeRange(key, 0, -201); }分页查询优化:
/* 优化后的分页查询 */ SELECT m.* FROM ( SELECT id FROM im_group_member WHERE group_id = #{groupId} ORDER BY last_active_time DESC LIMIT #{offset}, #{pageSize} ) t JOIN im_group_member m ON t.id = m.id二级缓存策略:
第一次查询:Redis ZSET → DB → 回填缓存 后续查询:Redis ZSET → 本地缓存 → 返回结果
性能提升效果:
- 万人大群成员列表查询从1200ms降至80ms
- Redis内存占用减少60%(仅存储活跃成员)
- 数据库负载降低75%
6. 事务与最终一致性保障
IM系统对数据一致性要求极高,我们采用柔性事务方案:
本地消息表:
@Transactional public void addFriendWithMessage(String fromId, String toId) { // 1. 写入好友关系 friendshipMapper.insert(buildFriendShip(fromId, toId)); friendshipMapper.insert(buildFriendShip(toId, fromId)); // 2. 写入本地消息表 EventMessage message = new EventMessage(); message.setEventType("FRIEND_ADD"); message.setContent(buildMessageContent(fromId, toId)); eventMessageMapper.insert(message); }定时任务补偿:
@Scheduled(fixedDelay = 30000) public void compensateFailedEvents() { List<EventMessage> failedMessages = eventMessageMapper.selectFailedMessages(); failedMessages.forEach(message -> { try { eventPublisher.republish(message); eventMessageMapper.updateStatus(message.getId(), "PROCESSED"); } catch (Exception e) { log.error("事件补偿失败", e); } }); }幂等设计:
public void handleFriendApplyEvent(ApplyEvent event) { String lockKey = "apply_event:" + event.getEventId(); if (redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 24, TimeUnit.HOURS)) { // 业务处理 } else { log.warn("重复事件直接返回"); } }
这套方案在实际业务中实现了:
- 事务成功率从99.2%提升到99.99%
- 异常恢复时间从小时级降到分钟级
- 系统容错能力显著增强