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

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

https://gist.github.com/phpdever/0073d5d99d97e23741011e52fd06e950
4236 次点击
所在节点    PHP
31 条回复
Septembers
2017-03-15 09:05:56 +08:00
async + cache?
yangqi
2017-03-15 09:07:33 +08:00
这个是数据库设计的问题,你这个做字符模糊查询肯定没法优化。只能改表结构
phpdever
2017-03-15 09:10:44 +08:00
@yangqi 在代码上无法优化了吗?因为 shares_tree 这个字段存放时是:“-|51|5150|1799|-”这样的。
yangqi
2017-03-15 09:14:48 +08:00
@phpdever 你这段代码上没什么优化的,慢的原因是在数据库查询上,而不是代码本身上。

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

但是左右值的方法,仅适用于少量分类的从属关系,像楼主这种数据结构,用户的从属关系是频繁更新的。左右值是不是就不适用了?
pubby
2017-03-15 10:59:53 +08:00
@dangyuluo 会造成"写放大" 爆炸~~
我看只适合数据小,或者大量静态数据
dapeng
2017-03-15 11:04:43 +08:00
#5 楼链接的中的方法受教了,提到的这种 mysql 结构设计思维 nice !
Immortal
2017-03-15 11:43:24 +08:00
@dangyuluo 应该也适用的 这个左右值如果从属关系变更 新的左右值是可以计算的 如果说层级很深的情况下 相对而言也要多次递归查询之后才能得出新值 但是一般是 读 远大于 写,是可以接受的,具体怎么计算可以研究下,之前做过,三两句话讲不清楚,是个挺有意思的问题

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/347529

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX