超大型组和用户缓存redis

java 复制代码
package com.kongjs.im.transfer.service;

import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@EnableScheduling
@Service
public class ImGroupService {

    private static final int SHARD_COUNT = 128; // 分片数,可配置
    private static final String GROUP_MEMBERS = "group:{%s}:members:{%s}";

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 计算用户属于哪个分片
    private int getShardId(Long groupId, Long userId) {
        // 混合 groupId 和 userId 使分布更均匀
        long hash = (groupId.hashCode() * 31L + userId.hashCode()) & 0x7fffffff;
        return (int) (hash % SHARD_COUNT);
    }

    private String getShardKey(Long groupId, int shardId) {
        return String.format(GROUP_MEMBERS, groupId, shardId);
    }

    // 添加成员
    public void addMember(Long groupId, Long userId) {
        int shardId = getShardId(groupId, userId);
        String key = getShardKey(groupId, shardId);
        stringRedisTemplate.opsForSet().add(key, userId.toString());
    }

    // 移除成员
    public void removeMember(Long groupId, Long userId) {
        int shardId = getShardId(groupId, userId);
        String key = getShardKey(groupId, shardId);
        stringRedisTemplate.opsForSet().remove(key, userId.toString());
    }

    // 判断成员是否存在
    public boolean isMember(Long groupId, Long userId) {
        int shardId = getShardId(groupId, userId);
        String key = getShardKey(groupId, shardId);
        Boolean exists = stringRedisTemplate.opsForSet().isMember(key, userId.toString());
        return Boolean.TRUE.equals(exists);
    }

    // 获取群所有成员数量(跨分片求和)
    public long getMemberCount(Long groupId) {
        // 构建所有分片 key
        List<String> keys = new ArrayList<>();
        for (int i = 0; i < SHARD_COUNT; i++) {
            keys.add(getShardKey(groupId, i));
        }
        // 定义 Lua 脚本
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText("""
                local total = 0
                for i, key in ipairs(KEYS) do
                    local size = redis.call('scard', key)
                    if size then total = total + size end
                end
                return total
                """);
        //script.setResultType(Long.class);
        Long count = stringRedisTemplate.execute(script, keys);
        return count != null ? count : 0L;
    }

    public List<Long> getAllGroupMembers(Long groupId) {
        // 构造 128 个分片 key
        List<String> keys = new ArrayList<>();
        for (int i = 0; i < SHARD_COUNT; i++) {
            keys.add(getShardKey(groupId, i));
        }
        DefaultRedisScript<List<Long>> script = new DefaultRedisScript<>();
        script.setScriptText("""
                    local result = {}
                    for i, key in ipairs(KEYS) do
                        local members = redis.call('smembers', key)
                        for _, uid in ipairs(members) do
                            table.insert(result, uid)
                        end
                    end
                    return result
                """);
        //script.setResultType(List.class);
        List<Long> list = stringRedisTemplate.execute(script, keys);
        return CollectionUtils.isEmpty(list) ? Collections.emptyList() : list;
    }

    //    @Scheduled(fixedDelay = 1000)
    public void syncGroupMembers() {
        long groupId = 1001;
        /*for (int i = 0; i < 1000000; i++) {
            addMember(groupId, (1L + i) * 2);
        }*/
        long memberCount = getMemberCount(groupId);
        List<Long> allGroupMembers = getAllGroupMembers(groupId);
        System.out.println(memberCount);
        System.out.println(allGroupMembers);
    }
}
相关推荐
轻刀快马9 小时前
Redis 架构进阶:全景解析 RDB、AOF 与混合持久化机制
redis
Albert Edison13 小时前
【Redis】Centos7.9 安装 Redis 5 教程
数据库·redis·缓存
Steadfast_GG14 小时前
Redis中的通用命令
redis·缓存
小二·14 小时前
Redis 内存溢出(OOM)排查与恢复实战
数据库·redis·bootstrap
pqk6V6Vep14 小时前
Redis 分布式锁进阶第一篇讲解
数据库·redis·分布式
giaz14n9X14 小时前
Redis 分布式锁进阶第六十一篇
数据库·redis·分布式
JAVA面经实录91717 小时前
Redis 知识体系(完整版)
java·redis·nosql数据库·nosql
颜笑晏晏18 小时前
长输入短输出场景下的 SGLang 推理性能实测前缀缓存、PD 分离配比与参数调优
缓存·推理优化·sglang·ai infra·pd分离
ManageEngine卓豪18 小时前
数据库可观测性:MySQL与Redis监控核心监控指标与全栈运维解决方案
数据库·redis·mysql·数据库性能·数据库监控
真实的菜19 小时前
Redis 从入门到精通(十四):Redis 7.x 新特性全解 —— 系列收官之作
数据库·redis·缓存