通常情况下,在关注性能之前,您应该始终正确地获取模式!
这样,您就可以做出有根据的决策,调整模式以解决特定的性能问题,而不是猜测。
您绝对应该采用两个表的方法。这将显著减少存储量、代码复杂度和更改系统以添加新属性所需的工作量。
假设每个属性都可以由序数表示,并且您只寻找对称匹配(即,您试图基于相似属性匹配人,而不是意图表达)....
在简单的级别上,查找合适的匹配项的查询可能非常昂贵。实际上,您正在寻找N维空间中相同接近度的节点,不幸的是,大多数关系型数据库并没有为此类操作进行设置(我相信PostgreSQL支持此类操作)。因此,大多数人可能会从以下内容开始:
SELECT candidate.id,
COUNT(*)
FROM users candidate,
attributes candidate_attrs,
attributes current_user_attrs
WHERE current_user_attrs.user_id=$current_user
AND candidate.user_id<>$current_user
AND candidate.id=candidate_attrs.user_id
AND candidate_attrs.attr_type=current_user.attr_type
AND candidate_attrs.attr_value=current_user.attr_value
GROUP BY candidate.id
ORDER BY COUNT(*) DESC;
然而,这会强制系统比较每个可用的候选项以找到最佳匹配。应用一些启发式算法,您可以得到非常有效的查询:
SELECT candidate.id,
COUNT(*)
FROM users candidate,
attributes candidate_attrs,
attributes current_user_attrs
WHERE current_user_attrs.user_id=$current_user
AND candidate.user_id<>$current_user
AND candidate.id=candidate_attrs.user_id
AND candidate_attrs.attr_type=current_user.attr_type
AND candidate_attrs.attr_value
BETWEEN current_user.attr_value+$tolerance
AND current_user.attr_value-$tolerance
GROUP BY candidate.id
ORDER BY COUNT(*) DESC;
($tolerance的值将影响返回的行数和查询性能 - 如果您在attr_type、attr_value上建立了索引)。
这可以进一步细化为一个积分评分系统:
SELECT candidate.id,
SUM(1/1+
((candidate_attrs.attr_value - current_user.attr_value)
*(candidate_attrs.attr_value - current_user.attr_value))
) as match_score
FROM users candidate,
attributes candidate_attrs,
attributes current_user_attrs
WHERE current_user_attrs.user_id=$current_user
AND candidate.user_id<>$current_user
AND candidate.id=candidate_attrs.user_id
AND candidate_attrs.attr_type=current_user.attr_type
AND candidate_attrs.attr_value
BETWEEN current_user.attr_value+$tolerance
AND current_user.attr_value-$tolerance
GROUP BY candidate.id
ORDER BY COUNT(*) DESC;
这种方法让你可以做很多不同的事情 - 包括按属性子集搜索,例如:
SELECT candidate.id,
SUM(1/1+
((candidate_attrs.attr_value - current_user.attr_value)
*(candidate_attrs.attr_value - current_user.attr_value))
) as match_score
FROM users candidate,
attributes candidate_attrs,
attributes current_user_attrs,
attribute_subsets s
WHERE current_user_attrs.user_id=$current_user
AND candidate.user_id<>$current_user
AND candidate.id=candidate_attrs.user_id
AND candidate_attrs.attr_type=current_user.attr_type
AND candidate_attrs.attr_value
AND s.subset_name=$required_subset
AND s.attr_type=current_user.attr_type
BETWEEN current_user.attr_value+$tolerance
AND current_user.attr_value-$tolerance
GROUP BY candidate.id
ORDER BY COUNT(*) DESC;
显然,这并不适用于非序数数据(例如出生符号、最喜欢的流行乐队)。如果不了解现有数据结构的更多信息,很难准确地说这将有多大效果。
如果您想添加更多属性,则无需对PHP代码或数据库架构进行任何更改-它可以完全由数据驱动。
另一种方法是识别刻板印象-即N维空间中的参考点,然后计算出特定用户最接近哪个点。您将所有属性折叠成单个复合标识符-然后只需要应用相同的方法在已匹配到刻板印象的候选子集中找到最佳匹配。