如何设计在线交友网站的用户表?

时间:2010-12-16 10:00:09

标签: php mysql database-design

我正在开发下一个版本的本地在线交友网站PHP&基于MySQL,我想做正确的事情。用户表非常庞大,预计新版本会增长更多,因为会有很多钱用于推广。

我认为7-8岁的当前版本可能是由不太熟悉PHP和MySQL的人完成的,所以我必须从头开始。

社区目前有20万用户,预计在未来一两年内将增长到500k-1mil。每个用户的个人资料有超过100个属性,我必须能够搜索至少30-40个属性。

你可以想象我制作一张有200k行和100列的表格有点小心。我的前任将用户表分成两个...一个使用最多和搜索的列,另一个使用列的其余(和批量)。但这会导致两个表之间出现大的同步问题。

那么,您认为这是最好的方式吗?

6 个答案:

答案 0 :(得分:5)

这不是本身的答案,但由于这里很少有答案提示属性值模型,我只想跳进去说出我的人生经历。

我曾经尝试过使用这个模型,其中包含120多个属性的表(每年增长5-10个),并且添加大约10万行(每6个月一次),索引变得如此之大以至于需要永远添加或更新单个user_id

我发现这种类型的设计(不是完全不适合任何情况)的问题是你需要在第二个表的user_id,attrib上放一个主键。如果不知道attrib的潜在长度,通常会使用更大的长度值,从而增加索引。就我而言,attribs可能有3到130个字符。而且,value肯定会遭受同样的假设。

正如OP所说,这会导致同步问题。想象一下,如果每个属性(或至少50%的属性)都需要存在。

另外,正如OP建议的那样,搜索需要在30-40个属性上进行,我不能想象30-40个连接如何有效,或者甚至是group_concat()由于长度限制。

我唯一可行的解​​决方案是回到一个包含尽可能多的列的表。我的索引现在变得更小了,搜索也更容易。

编辑:此外,没有规范化问题。要么拥有属性值的查找表,要么让它们ENUM()

编辑2:当然,可以说我应该有一个属性可能值的查找表(减少索引大小),但我应该在该表上进行连接。

答案 1 :(得分:4)

您可以做的是将用户数据分成两个表格。

1)表:用户

这将包含有关用户的“核心”固定信息,例如firstname,lastname,email,username,role_id,registration_date以及那种性质的东西。

配置文件相关信息可以放在自己的表中。这将是一个无限可扩展的表,其中key => val nature。

2)表:user_profile

字段:user_id,选项,值

user_id:1

选项:profile_image

值:/uploads/12/myimage.png

user_id:1

选项:questions_answered

价值:24

希望这有帮助, 保罗。

答案 2 :(得分:1)

实体 - 属性 - 值模型可能适合您:

http://en.wikipedia.org/wiki/Entity-attribute-value_model

不是要有100个增长列,而是添加一个包含三列的表:

user_idpropertyvalue

答案 3 :(得分:1)

通常,您不应为了性能而牺牲数据库完整性。

我要做的第一件事是创建一个包含1百万行虚拟数据的表,并使用像ab这样的压力工具测试一些典型的查询。它很可能会发现它表现得很好--1万行是mysql的小菜一碟。因此,在尝试解决问题之前,请确保您确实拥有它。

如果您发现性能不佳并且数据库确实是一个瓶颈,请考虑一般优化,例如缓存(在所有级别上,从mysql查询缓存到html缓存),获得更好的硬件等。这应该可以解决大多数情况。

答案 4 :(得分:1)

一般情况下,在担心性能之前,应始终正确地使用架构正确!

通过这种方式,您可以做出有关调整架构以解决特定性能问题的明智决策,而不是猜测。

你绝对应该走2桌路线。这将显着减少存储量,代码复杂性以及更改系统以添加新属性的工作量。

假设每个属性都可以用Ordinal number来表示,并且你只是在寻找对称匹配(即你试图根据相似的属性来匹配人,而不是意图的表达)。 ...

在简单的层面上,查找合适匹配的查询可能非常昂贵。实际上,你正在寻找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;

然而,这迫使系统比较每个可用的候选者以找到最佳匹配。应用一点heurisitics,你可以得到一个非常有效的查询:

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维空间内的参考点,然后确定特定用户中哪一个最接近。您将所有属性折叠为单个复合标识符 - 然后您只需要应用相同的方法在候选人子集中找到最佳匹配,这些候选人也与构造型匹配。

答案 5 :(得分:0)

如果没有看到架构,就无法提出任何建议。通常 - 必须将Mysql数据库规范化为至少3NF或BNCF。听起来它现在没有标准化,在1个表中有100列。

此外 - 您可以使用事务和INNODB引擎轻松地使用外键强制引用完整性。

相关问题