我们目前的设置看起来有点像这样。
public_entry(5.000.000行)→telephone_number(5.000.000行)→user(400.000行)
3个表,右边的箭头表示包含右表中的外键(整数)的外键约束。
现在我们有两个"观点"我们希望在我们的网络应用程序中显示的数据。
每个结果应该得到一个分数,分数是否符合您的需求(例如,您寻找水管工,如果数字在您所在区域,相关用户是水管工,电话号码应该得分高)。
我们尝试了两种方案来解决这个问题。
第一种方法在表上执行SELECT with INNER JOIN,如下所示
SELECT ..., (...) as score
FROM public_entry pe
INNER JOIN telephone_numer tn ON tn.id = pe.numberid
INNER JOIN user u ON u.id = tn.userid WHERE ... ORDER BY score
在较小的系统上使用此查询,即使在负载下,生产系统的1/4也能很好地执行。 然而,当我们将这个查询放入生产系统时,它会破坏执行时间超过30秒。
第二种方法是在public_entry上使用单个SELECT过滤所有public_entries而不使用任何JOIN并迭代它们为每个public_entry调用一个SELECT来获取telephone_number和用户,计算得分并丢弃结果如果telephone_number和user不匹配我们的过滤器/兴趣。
通常从不考虑第二种方法,因为它为单个页面加载创建了300多个查询。 Foreach结果并在foreach中调用SELECT通常被认为是不好的风格。
然而,第二种方法在生产系统上执行。不好,但不会超过1-3秒,但在测试系统上也表现不佳。
您对问题的位置有什么建议吗?
修改
查询
SELECT COUNT(p.id)
FROM public_entry p, fon f, user u
WHERE p.isweb = 1
AND f.hidden = 0
AND f.deleted = 0
AND f.id = p.fonid
AND u.id = f.userid
AND u.gender = "female"
此查询的执行时间为3秒。
这只是一个示例查询。我可以拿出去哪里,它的表现稍差一点。一般情况下,如果我们对数据进行单个INNER JOIN的SELECT COUNT(),则查询会爆炸(30秒)
答案 0 :(得分:0)
我没有你想要的神奇答案,但这里有一些表现不佳的'原因',以及一些可能的解决方法(有警告)。
isweb
,hidden
,deleted
和gender
中哪一个最“有选择性”?这个优化器认为它们没用,而且很烦人。也就是说,如果每个都有两个值,那么该字段上的INDEX可能是无用的。因此,它选择一个表,进行完整扫描,然后进入下一个表等。请注意,在EXPLAIN
中,它首先选择了最小的表(user
)。这通常是当WHERE
子句看起来没有用时优化器所执行的操作。
MySQL是否能完成所有这些工作,或者你所做的一切工作都是同样的努力。也许你可以更快地完成它,因为你可以在内存中有一个简单的关联数组,而MySQL被编码为允许表存放在磁盘上,逐块“缓存”在RAM中。但是,如果你没有足够的内存来加载所有东西,你就会陷入MySQL。
如果您实际删除了“隐藏”和“已删除”行,则任务会更快一些。
你的两个选择看起来不太相似。你是否建议有各种各样的SELECT?你有效地需要查看所有3个表中的大部分来获得“得分”或“计数”吗?
让我们从数据仓库方法看一下......有些数据是“静态的”;也就是说,不变,可以归纳?如果是这样,将小计(COUNT(*)
)预先计算到摘要表中将使最终查询更快。 DW通常涉及白天的小计。但它要求这些小计不会改变。
COUNT(x)
有检查x
是NULL
的开销。通常这不是必需的,COUNT(*)
可以为您提供所需的内容。
您多久运行一次相同的SELECT?或者,至少,类似的SELECT?你需要最高分吗?我正在钓鱼,在半夜运行所有可能的查询,然后使用24小时的结果。请注意,通过一次执行多项操作,某些查询可以更快地运行。例如,代替“女性”与“男性”的两个SELECT,执行一个SELECT和GROUP BY gender
。