使用 PHP 循环获取用户关系树的性能问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
phpdever
V2EX    PHP

使用 PHP 循环获取用户关系树的性能问题

  •  
  •   phpdever 2017-03-15 08:59:21 +08:00 4660 次点击
    这是一个创建于 3136 天前的主题,其中的信息可能已经有所发展或是发生改变。
    各位 V 友好,我现在遇到一个问题,我先简单描述下,这个函数的作用是传入用户 id ,通过用户 id 去查找 user 表中的"shares_tree"字段,也就是 A 找 B , B 找 C ,依次循环下去,如果找到的用户少的话查询效率还可以,但是用户数较多的情况下,查询速度会变得异常缓慢,我知道问题是出在在循环内部调用函数,但目前我没有更好的方法优化,还望各位 V 友能够指点一二。

    31 条回复    2017-03-15 22:03:59 +08:00
    Septembers
        1
    Septembers  
       2017-03-15 09:05:56 +08:00 via iPhone
    async + cache?
    yangqi
        2
    yangqi  
       2017-03-15 09:07:33 +08:00
    这个是数据库设计的问题,你这个做字符模糊查询肯定没法优化。只能改表结构
    phpdever
        3
    phpdever  
    OP
       2017-03-15 09:10:44 +08:00
    @yangqi 在代码上无法优化了吗?因为 shares_tree 这个字段存放时是:“-|51|5150|1799|-”这样的。
    yangqi
        4
    yangqi  
       2017-03-15 09:14:48 +08:00
    @phpdever 你这段代码上没什么优化的,慢的原因是在数据库查询上,而不是代码本身上。

    字段这么存不能用索引,慢是肯定的了。如果需要查询肯定不能这么寸,要另外建一个多对多关系表
    Immortal
        5
    Immortal  
       2017-03-15 09:26:55 +08:00   6
    sheep3
        6
    sheep3  
       2017-03-15 09:29:38 +08:00   1
    1. 小心循环依赖导致无限递归,最好加一个栈深度值判断一下。
    2. 要不用 redis ,存 userid->user_tree_kids[]
    sheep3
    &nbp;   7
    sheep3  
       2017-03-15 09:36:30 +08:00
    @Immortal 这个有意思!
    jianzhiyao020
        8
    jianzhiyao020  
       2017-03-15 09:37:48 +08:00   2
    首先,
    多对多的多次查询肯定是坑,
    你这个需求,应该是这样子的,
    分销系统,
    一个用户有一个上级,
    可以有多个下级,
    那么我们可以这样处理,
    一个字段存 tree ,
    一级分销 /二级分销 /三级分销 /四级分销 /,
    该 tree 字段可以建立索引,
    查询本级的子节点可以用,
    tree like 我的 tree+'%' (伪代码)
    因为 like 在前缀确认的情况下,
    是可以使用索引的,
    还需要一个字段,存当前层级,标注当前层级 0,1,2,3,4,5,6,7,8,9 ,
    查询的时候,比自己大的就是后面的人,
    这种方法,理论上可以实现无限分销。
    Immortal
        9
    Immortal  
       2017-03-15 09:41:38 +08:00
    @sheep3 之前遇到过类似问题发现的 自己代码封装点方法就好
    dapeng
        10
    dapeng  
       2017-03-15 09:46:34 +08:00   1
    #8 楼靠谱,感觉用 redis 的并不是能很好的解决这个问题,一是数据递增变化大,二是当数据量达到一定量级时这 redis 内存得设置多大;
    最优方法还是数据库设计这块,冗余字段, 用空间换时间
    EchoUtopia
        11
    EchoUtopia  
       2017-03-15 09:48:41 +08:00
    使用图数据库,或者你查询出来的时候,自己构建一个有向图放在内存里,我目前有个需求跟你类似,是这样打算的
    Immortal
        12
    Immortal  
       2017-03-15 10:06:15 +08:00
    @jianzhiyao020 其实这样设计有点不灵活 查询起来基本没问题 但是修改起来问题很大 要更新索引 更新层级 尤其是遇到那种"升级"上的时候 更为繁琐 比如从四级->二级 连带的所有下级用户 和 层级 和 下级用户的层级 索引等等 全部要更新一次 如果层级深 这个估计也不会快
    jianzhiyao020
        13
    jianzhiyao020  
       2017-03-15 10:10:55 +08:00
    @Immortal
    分销系统的话,
    这种层级维护的可能基本为 0 ,
    主要是看需求,
    如果说类似文件变动,
    肯定是多对多,
    但是,
    多对多也产生了一个回溯上级的问题,
    特别是分销系统,需要给上级分发奖励,
    所以我觉得该类问题,应该是提需求,
    而不是提技术难点。
    如有疏漏,
    请再次提出。
    感谢。
    AbrahamGreyson
        14
    AbrahamGreyson  
       2017-03-15 10:26:26 +08:00 via iPhone   1
    用嵌套集合模型重构数据库。
    whahuzhihao
        15
    whahuzhihao  
       2017-03-15 10:35:49 +08:00
    为何不用 mongoDB ?
    hjxx
        16
    hjxx  
       2017-03-15 10:39:18 +08:00   1
    用左右值的方式 见 5 楼的链接
    dangyuluo
        17
    dangyuluo  
       2017-03-15 10:52:23 +08:00
    @Immortal
    @hjxx

    但是左右值的方法,仅适用于少量分类的从属关系,像楼主这种数据结构,用户的从属关系是频繁更新的。左右值是不是就不适用了?
    pubby
        18
    pubby  
       2017-03-15 10:59:53 +08:00   1
    @dangyuluo 会造成"写放大" 爆炸~~
    我看只适合数据小,或者大量静态数据
    dapeng
        19
    dapeng  
       2017-03-15 11:04:43 +08:00
    #5 楼链接的中的方法受教了,提到的这种 mysql 结构设计思维 nice !
    Immortal
        20
    Immortal  
       2017-03-15 11:43:24 +08:00
    @dangyuluo 应该也适用的 这个左右值如果从属关系变更 新的左右值是可以计算的 如果说层级很深的情况下 相对而言也要多次递归查询之后才能得出新值 但是一般是 读 远大于 写,是可以接受的,具体怎么计算可以研究下,之前做过,三两句话讲不清楚,是个挺有意思的问题
    fuxkcsdn
        21
    fuxkcsdn  
       2017-03-15 12:32:46 +08:00 via iPhone   1
    5 楼的左右值方法受教了,得去把数据结构书籍再翻出来细读了
    对于真无限级别的场景,这方法真不错
    对于有限(或假无限)级别的场景, 8 楼的方法更合适
    lucifer4he
        22
    lucifer4he  
       2017-03-15 14:50:52 +08:00   1
    你需要 PG 递归查询。
    TJT
        23
    TJT  
       2017-03-15 15:10:41 +08:00
    之前也做过类似的需求,这是测试代码。把所有 source_id = x 的都取出来,就可以根据 refer_id 重建整棵树了。

    https://ws1.sinaimg.cn/large/a95d59d7gy1fdnjiw4d39j20d804tdg6
    TJT
        24
    TJT  
       2017-03-15 15:11:06 +08:00
    phpdever
        25
    phpdever  
    OP
       2017-03-15 15:11:48 +08:00
    @TJT 图片破裂啦
    TJT
        26
    TJT  
       2017-03-15 15:11:52 +08:00
    phpdever
        27
    phpdever  
    OP
       2017-03-15 15:15:22 +08:00
    @TJT 嗯,谢谢大神啦,我先折腾一下,如果解决了再拿出来分享。
    fhefh
        28
    fhefh  
       2017-03-15 16:19:25 +08:00
    先 mark ~~~
    none110
        29
    none110  
       2017-03-15 17:28:32 +08:00 via iPhone
    mark 备看
    eamon666
        30
    eamon666  
       2017-03-15 19:05:16 +08:00
    要是以前的话我会用 8 楼的办法来搞,现在可能更趋向于 MONGODB
    1Satoshi
        31
    1Satoshi  
       2017-03-15 22:03:59 +08:00
    5L 的这个方案, 把数据结构中的树 的关系,联想到 数据库设计,厉害啊!
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1077 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 23:09 PVG 07:09 LAX 16:09 JFK 19:09
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86