2009-04-08 120 views
0

我正在设计一个非常简单的(在功能方面)但困难的(在可伸缩性方面)系统,用户可以互相发送消息。把它想象成一个非常简单的聊天服务。用户可以通过php页面插入消息。该消息很短并且具有收件人姓名。需要一些数据库模式设计的建议

在另一个php页面上,用户可以查看一次发送给他的所有消息,然后在数据库中删除它们。而已。这就是这个系统所需的全部功能。我应该如何去设计这个(从数据库/ php的角度来看)?

到目前为止,我有表是这样的:

  • 字段1 - >消息(VARCHAR)
  • 场2 - >收件人(VARCHAR)

现在对于SQL INSERT,我发现,无论数据库中的行数如何,所花费的时间都是不变的。所以我的send.php将有一个很好的保证返回时间。

但是拉下邮件,随着行数增加,我的pull.php将花费更长的时间!我发现sql select(和delete)会随着行增长而花费更长的时间,即使在我为添加了收件人字段的索引后,情况也是如此。

现在,如果只是简单的情况下,用户将不得不等待更长的时间,然后他们的消息被拉到PHP然后它会一直OK。但我担心的是,当每个pull.php服务时间花费很长时间时,php服务器将开始拒绝某些请求的连接。或者更糟糕的是服务器可能会死亡。

所以问题是,如何设计这样的尺寸?任何提示/提示?

PS。关于数字的一些估计:

  • 用户数以5万开头并上升。
  • 每个用户平均有大约10条消息存储在另一端可能会拉下来。
  • 每个用户每天发送大约10-20条消息。

UPDATE从阅读到目前为止的答案:

我只想从pull.php不利于拉低少消息澄清。即使只是拉一条消息,桌子很大时也需要很长时间。这是因为该表的所有消息,所以你必须做一个选择是这样的:即使你改变它到该

select message from DB where recipient = 'John' 

它没有太大的帮助

select top 1 message from DB where recipient = 'John' 

所以从远答案看起来好像表越长,选择的速度越慢,或者稍微好一些,没有办法绕过它。如果是这样的话,我应该如何处理这个从PHP端?我不希望php页面在http上失败,因为用户会感到困惑,并最终变得像疯了一样令人耳目一新,这使得它变得更糟。

+0

如果收件人的索引不工作,它是什么varchar(???)表中当前有多少行有这样一个简单的查询运行速度慢?此机器上有多少其他用户以及它是哪种类型的机器? – 2009-04-08 19:51:05

+0

我必须猜测你的索引没有正确设置。否则,你的表现不会顺应这种趋势。 – 2009-04-08 20:00:33

+0

如果是mysql,它不会从DB中选择消息,其中recipient ='John'LIMIT 1 ??? – 2009-04-08 21:00:24

回答

1

所以问题是,如何设计这样的尺寸?任何提示/提示?

是的,你不想使用关系数据库进行消息排队。你试图做的不是什么关系数据库最好的设计,而你可以做到这一点,它有点像使用螺丝刀钉入钉子。

相反,看看那里的许多开源消息队列之一,SecondLife的人有一个整洁的wiki,他们在那里审查了很多。

http://wiki.secondlife.com/wiki/Message_Queue_Evaluation_Notes

3

数据库设计,这是你的建议很简单。只要用户有更多的信息,它就会花更长的时间,你可以做的只是对结果进行分页。显示第一个10/50/100或其他任何有意义的内容,并且只能提取这些记录。一般来说,除非消息量增加了一个或更多,否则你的时间不应该增加太多。您应该能够在不到一秒的时间内收回1000条短消息。现在可能需要更多时间才能显示该页面,但那些分页应该有所帮助。

我会建议,虽然经历和考虑未来的功能,并建立你的数据库多一点基于此。向软件添加更多功能非常简单,改变数据库相对困难。

+0

我希望我知道为什么在这个问题上的每个人都得到大众投票。这里可能会提出一些有争议的建议,但我不相信这些建议是不准确的。如果您是投票不满,请解释原因,特别是如果您是提问者。如有必要,让我们澄清一下。 – 2009-04-08 19:13:55

+0

我同意,我也赞成这一点。这是Stack Overflow的一个合理问题,它应该有机会得到一个很好的答案。 – 2009-04-08 19:52:25

-3

对于每个用户,您始终只能有一行,并且只需将消息连接成一条长记录。如果您长时间保留邮件,这不是最好的方法,但它可以将您的问题减少到单个查找并在存储时连接,并在检索时查找单个查找。没有更多细节就很难说 - 数据库设计难以实现的部分原因是以一种妥协的方式实现了系统的所有目标。没有所有的细节,它很难给出最佳折中的建议。

编辑:我认为我对此很清楚,但显然不是:除非读取阅读器时排队,否则不会这样做。这就是为什么我提示澄清。

-3

限制您的pull.php在任何时候都会显示的行数。

传输的数据越多,显示页面所需的时间就越长,而不管数据库的性能如何。

您必须将您的数据限制在SQL中,并返回最近的N行。

编辑 把一个索引放在收件人,它会加快它。如果你想获得排名前50的东西,你可能需要另一列来区分行,可能是SendDate或一个自动递增字段。聚集索引会减慢插入,因此在那里使用常规索引。

0

这是一个不可避免的问题 - 更多的消息,更多的时间来找到请求的问题。你可以做的唯一事情就是你已经做了什么 - 添加一个索引,并把O(n)查找时间查看一个完整的表格扫描到O(log(u)+ m)中,查找聚集索引,其中n是数字总消息数量,用户数量,以及每个用户的消息数量。

3
  1. 遵循规范化规则。尝试达到第三范式。进一步这种类型的应用可能不值得。保持你的桌子薄。
  2. 不要实际删除行,只需将它们标记为已删除并带有标记。如果您确实需要删除它们以进行某些类型的维护/清理以减小尺寸。将它们标记为已删除,然后创建清理过程以在低使用时间内归档或删除记录。
  3. 整数值更容易处理SQL Server然后字符值。所以,而不是在哪里收件人='约翰'使用WHERE Recipient_ID = 23当您规范化数据库时,您将获得此类行为。
3

不要使用VARCHAR为您的收件人。最好制作一个收件人表,其中主键是一个整数(如果您期望的数量非常多,则为bigint)。

然后,当你做你的SELECT语句:

SELECT message FROM DB WHERE recipient = 52; 

速度检索行会快很多。

另外,我相信MySQL索引是B-Trees,对大多数情况来说,它是O(log n)。

2

没有索引的数据库表被称为堆,即使使用'where'子句查询堆的结果也会被计算,堆的大O表示为O(n),n是表中的行数。添加一个索引(这实际上取决于数据库引擎的底层方面)会导致O(log(n))的复杂性,以找到表中的匹配行。这是因为索引肯定是以b-tree方式实现的。即使存在索引,向表中添加行也是O(1)操作。

> But for pulling down messages, my pull.php will take longer as the number of rows 
increase! I find the sql select (and delete) will take longer as the rows grow and 
this is true even after I have added an index for the recipient field. 

除非您插入到索引的中间,此时数据库引擎需要将行向下移动以适应。从索引中删除时会发生同样的情况。记住有不止一种索引。请确保您使用的索引不是聚簇索引,因为必须筛选更多数据并通过插入和删除进行移动。

FlySwat为您提供了最好的选择......不要使用RDBMS,因为您的消息在正式意义上不是关系型的。您将从文件系统获得更好的性能。

dbarker也给出了正确的答案。我不知道他为什么被拒绝了三次,但我会投票给他,可能会失分。 dbarker是指“垂直分区”,他的建议是可以接受的,并且好的。这不是火箭手术的人。

我的建议是不要在RDBMS中实现这种功能,如果您确实记得选择,更新,插入或删除表中页面上的所有位置锁定。如果您继续将此功能放入数据库中,那么在您的平台上可以使用nolock锁定提示来运行您的选择,以提高并发性。此外,如果您拥有如此多的并发用户,请按照dbarker的建议垂直分区表,并将这些数据库文件放在单独的驱动器(不仅是卷,而是单独的硬件)上以增加I/O并发性。