我是这样优化用户关系查询的

C_PlusPlus
• 阅读 2009

0x01

我们有一个业务场景是需要将用户邀请的下级用户根据其用户等级给统计出来(不止统计一级).
现有的解决方案:
mysql中一张用户表
主要的3个字段:user_id, parent _id,level,
典型的一个树形结构.
因为数据量很大,查询次数很多,所以重算这数据基本上重算一次要花个几分钟.

0x02

优化版本1:
假如我将没所有用户对应的所有直接下级用户的id取出,存放到redis中的数组,像这样

U:110->[111,112,113,...]

将用户等级也放到redis中,像这样

L:110->0

然后通过map的方式取到下级的下级用户并合并起来,再通过用户等级进行分组。

users.stream.map(u-> jedis.lrange(0,-1)).flatMap(u-> u).collect(grouppingBy(u-> jedis.get("L:" + u)))...

LRANGE
时间复杂度:O(S+N), S 为偏移量 start , N 为指定区间内元素的数量。`
优化结果:
计算机器:I5 六代 8G内存
时间:25秒

0x03

优化版本2:
使用Set存放用户,通过SUNION命令获取下级ID

时间复杂度:O(N), N 是所有给定集合的成员数量之和。

获取当前下级的用户代码则更简单了,首先将当前用户通过CPU并行的方式把SET的key计算出来,然后通过SUNION将所有的用户取出来:

private Set<String> downLevelAllUser(Set<String> users) {
    return jedis.sunion(users.parallelStream().map(s ->
            "U:" + s
    ).collect(Collectors.toList()).toArray(new String[0]));
}

这些用户的等级都查出来,那就获取他们的用户等级并进行分组.

private Map<String, List<String>> groupUser(Set<String> users) {
    return jedis.mget(users.parallelStream().map(u -> "L:" + u)
            .collect(Collectors.toList()).toArray(new String[0]))
            .parallelStream().collect(Collectors.groupingBy(r -> r));
}

主要功能都实现了,那么试试计算8级所花时间

public List<Map<String, Integer>> downLevel8UserRole(int start_user) {
        Set<String> u1 = downLevel1Users(start_user);
        Map<String, List<String>> m1 = groupUser(u1);
        Set<String> u2 = downLevelAllUser(u1);
        Map<String, List<String>> m2 = groupUser(u2);
        Set<String> u3 = downLevelAllUser(u2);
        Map<String, List<String>> m3 = groupUser(u3);
        Set<String> u4 = downLevelAllUser(u3);
        Map<String, List<String>> m4 = groupUser(u4);
        Set<String> u5 = downLevelAllUser(u4);
        Map<String, List<String>> m5 = groupUser(u5);
        Set<String> u6 = downLevelAllUser(u5);
        Map<String, List<String>> m6 = groupUser(u6);
        Set<String> u7 = downLevelAllUser(u6);
        Map<String, List<String>> m7 = groupUser(u7);
        Set<String> u8 = downLevelAllUser(u7);
        Map<String, List<String>> m8 = groupUser(u8);
        return Arrays.asList(m1, m2, m3, m4, m5, m6, m7, m8).parallelStream().map(m -> {
            Map<String, Integer> newMap = new HashMap<>();
            m.forEach((k, v) -> newMap.put(k, v.size()));
            return newMap;
        }).collect(Collectors.toList());
    }

优化结果:
总用户数为:120W
计算机器:I5 六代 8G内存
时间:4秒

好了,优化完毕了,大神们还有什么高招,欢迎来指导!!!!!!

点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
mysql DISTINCT根据某字段去重同时查出其他字段
mysql有个关键字distinct用来去重的,但是使用时只能放在查询字段的最前边,如:SELECTDISTINCTuser\_id,ageFROMt\_user;若不是放在最前边,如:SELECTuser\_id,DISTINCTageFROMt\_user;是会报错的。那么如果我们只想根据age字段来去重,并且要查出user\_id
Stella981 Stella981
3年前
Redis的bitmap如何在Golang中使用
为什么需要Bitmapbitmap的主要应用场景为低空间的去重业务场景,比如活跃用户统计和用户行为统计。Golang如何操作funcmain(){rdb:redis.NewClient(&redis.Options{Addr:"127.0.0.1:
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql——GROUP BY和HAVING
GROUPBY语法可以根据给定数据列的每个成员对查询结果进行分组统计,最终得到一个分组汇总表。select子句中的列名必须为分组列或列函数,列函数对于groupby子句定义的每个组返回一个结果。某个员工信息表结构和数据如下:  id  name  dept  salary  edlevel     hiredate   1  张
Wesley13 Wesley13
3年前
ThinkPHP 根据关联数据查询 hasWhere 的使用实例
很多时候,模型关联后需要根据关联的模型做查询。场景:广告表(ad),广告类型表(ad\_type),现在需要筛选出广告类型表中id字段为1且广告表中status为1的列表先看关联的设置部分 publicfunctionadType(){return$thisbelongsTo('A
Wesley13 Wesley13
3年前
MySQL关于用户关注粉丝表的设计方案
一、数据结构分析用户关注粉丝是一个多对多的数据模型,分析对象的数据特征,我们给每个用户设计一个关注者属性和粉丝属性,用于存储用户的关注者id和粉丝id,如用户1:$arr1\'follow''\2,3,4\,'fans'\4,5,6\,\二、用户逻辑关系梳理
Stella981 Stella981
3年前
ELK学习笔记之ElasticSearch的索引详解
0x00ElasticSearch的索引和MySQL的索引方式对比Elasticsearch是通过Lucene的倒排索引技术实现比关系型数据库更快的过滤。特别是它对多条件的过滤支持非常好,比如年龄在18和30之间,性别为女性这样的组合查询。倒排索引很多地方都有介绍,但是其比关系型
Stella981 Stella981
3年前
PHP利用32进制生成固定长度字符串对id加密解密
我们在实际项目运用中,难免会要求对ID进行加密,生成特定的字符串,比如生成用户邀请码,这样不用查数据库也可以反向解密到id为什么使用32进制因为数字加字母长度为36位,32位生成后不用区别用户输入不用区分大小写<?phpclassIDAES{$baseChar'0123456789
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这