V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
phpdever
V2EX  ›  PHP

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

  •  
  •   phpdever · 2017-03-15 08:59:21 +08:00 · 4209 次点击
    这是一个创建于 2814 天前的主题,其中的信息可能已经有所发展或是发生改变。
    各位 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
        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
    Time2
        31
    Time2  
       2017-03-15 22:03:59 +08:00
    5L 的这个方案, 把数据结构中的树 的关系,联想到 数据库设计,厉害啊!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2417 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 16:07 · PVG 00:07 · LAX 08:07 · JFK 11:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.