我正在设计一个非常简单(在功能方面)但很难(在可扩展性方面)系统,用户可以互相发送消息。可以把它想象成一个非常简单的聊天服务。用户可以通过php页面插入消息。邮件很短,并且有一个收件人姓名。
在另一个php页面上,用户可以查看一次发送给他的所有消息,然后在数据库中删除它们。而已。这就是该系统所需的所有功能。我应该如何设计它(从数据库/ php的角度来看)?
到目前为止,我有这样的表:
现在对于sql insert,我发现无论数据库中的行数是多少,它所花费的时间都是常量。所以我的send.php将保证返回时间很好。
但是为了减少消息,我的pull.php会随着行数的增加而花费更长的时间!我发现随着行的增长,sql select(和delete)将花费更长的时间,即使我已经为收件人字段添加了索引,也是如此。
现在,如果仅仅是用户必须等待更长时间才能在他们的消息被提取到php之前那么它就可以了。但我担心的是,当每个pull.php服务时间花费很长时间时,php服务器将开始拒绝与某些请求的连接。或者更糟糕的是服务器可能会死
所以问题是,如何设计这样可以扩展?任何提示/提示?
PS。有人估计数字:
到目前为止阅读答案的更新:
我只想澄清一下,通过从pull.php中删除较少的消息无济于事。当桌子很大时,即使只拉一条消息也需要很长时间。这是因为该表包含所有消息,因此您必须执行以下选择:
select message from DB where recipient = 'John'
即使您将其更改为此也无济于事
select top 1 message from DB where recipient = 'John'
从答案到目前为止,似乎表越长,选择越慢O(n)或稍微好一点,没有办法绕过它。如果是这种情况,我应该如何从php端处理这个?我不希望php页面在http上失败,因为用户会感到困惑并且最终会像疯了一样刷新,这会让它变得更糟。
答案 0 :(得分:3)
根据您的建议,数据库设计很简单。一旦用户有更多消息需要更长的时间,你可以做的就是对结果进行分页。显示第一个10/50/100或任何有意义的东西,只显示那些记录。一般来说,除非消息量增加一个或更多,否则你的时间不应该增加太多。您应该可以在不到一秒的时间内收回1000条短消息。现在页面可能需要更长的时间才能显示,但分页应该有帮助。
我建议虽然经历并考虑未来的功能,并基于此更多地构建您的数据库。为软件添加更多功能很容易,更改数据库相对比较困难。
答案 1 :(得分:3)
答案 2 :(得分:3)
请勿为收件人使用 VARCHAR 。最好制作一个带有主键的收件人表,这是一个整数(如果您期望的人数非常多,则为bigint)。
然后当你选择陈述时:
SELECT message FROM DB WHERE recipient = 52;
速度检索行会快得多。
另外,我认为MySQL索引是B-Trees,大多数情况下都是O(log n)。
答案 3 :(得分:2)
没有索引的数据库表称为堆,查询堆会导致正在计算的表的每一行,即使使用'where'子句,堆的big-o表示法也是O(n),其中n为表中的行数。添加索引(这实际上取决于数据库引擎的底层方面)会导致O(log(n))的复杂性,以便在表中找到匹配的行。这是因为索引最肯定是以b树的方式实现的。即使存在索引,向表中添加行也是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也给出了正确的答案。我不知道他为什么被投了3次,但是我会冒险投票让他失去分数。 dbarker指的是“垂直分区”,他的建议既可接受又好。这不是火箭手术的人。
我的建议是在你的RDBMS中不实现这种功能,如果你确实记得选择,更新,插入,删除表格中页面上的所有地方锁定。如果您确实将此功能放入数据库中,那么如果您的平台上有可用的nolock锁定提示来运行您的选择以增加并发性。此外,如果您有这么多并发用户,请按照dbarker的建议垂直对表进行分区,并将这些数据库文件放在不同的驱动器上(不仅仅是卷而是单独的硬件)以增加I / O并发性。
答案 4 :(得分:1)
所以问题是,如何设计这样可以扩展?任何提示/提示?
是的,您不希望使用关系数据库进行消息队列。你要做的不是关系数据库最适合的设计,而你可以做到这一点,有点像用螺丝刀钉钉子。
相反,看看那里的众多开源消息队列之一,SecondLife的人们有一个整洁的维基,他们在那里审查了很多。
http://wiki.secondlife.com/wiki/Message_Queue_Evaluation_Notes
答案 5 :(得分:0)
这是一个不可避免的问题 - 更多消息,更多时间找到所请求的消息。你唯一能做的就是你已经做了 - 添加一个索引并将完整的表扫描的O(n)查找时间转换成O(log(u)+ m)以查找聚簇索引,其中n是数字总消息量,用户数量,以及每个用户的消息数量。
答案 6 :(得分:-3)
每个用户只能拥有一行,只需将消息连接成一个长记录。如果您长时间保留消息,这不是最好的方法,但它可以将您的问题减少到单个查找并在存储时连接并在检索时单个查找。如果没有更多细节,很难说 - 数据库设计难以实现的一部分是以一种妥协的方式满足系统的所有目标。没有所有细节,很难就最佳妥协提出建议。
编辑:我认为我对此非常清楚,但显然不是:你不会这样做,除非你在阅读时弄清楚读者的队列。这就是我提示澄清的原因。答案 7 :(得分:-3)
限制pull.php在任何时候显示的行数。
您传输的数据越多,显示页面所需的时间就越长,无论您的数据库有多棒。
您必须限制SQL中的数据,返回最近的N行。
修改强> 在收件人上放一个索引,它会加快速度。如果你想要获得前50或其他东西,可能需要另一列来区分行,可能是SendDate或自动递增字段。聚集索引会减慢插入速度,因此请在那里使用常规索引。