2009-12-03 76 views
1

我有一个拥有大约30,000个成员的网站,我添加了一个功能,包括从40个可能的消息池中发送一条随机消息。成员永远不会收到相同的消息两次。这是否是非正规化的情况?

一个表包含40条消息,另一个表映射消息和成员之间的多对多关系。

cron脚本每天运行,从30,000中选择一个成员,从40中选择一条消息,然后检查此消息是否已经发送给此用户。如果不是,则发送消息。如果是,它会再次运行查询,直到找到尚未被此成员接收的消息。

我现在担心的是这个m-m表格将变得非常大:在30,000个成员和40个消息中,我们已经有了120万行,通过它我们必须搜索才能找到尚未发送的消息。

这是反例化的情况吗?在成员表中,我可以添加40列(message_1,message_2 ... message_40),每次发送消息时都会添加1个标志。如果我没有弄错,这会使cron脚本中的查询运行得更快

回答

4

我知道,不回答你原来的问题,但如果你选择了所有的消息不会是这样快还没有发送给用户,然后随机选择其中之一?

看到这个伪mysql的位置:

SELECT 
    CONCAT_WS(',', messages.ids) unsent_messages, 
    user.id user 
FROM 
    messages, 
    user 
WHERE 
    messages.id NOT IN (
     SELECT 
      id 
     FROM 
      sent_messages 
     WHERE 
      user.id = sent_messages.user 
    ) 
GROUP BY ids 
+0

我同意,我在这里详细阐述了一下 – 2009-12-03 11:14:01

+0

你能否在CONCAT_WS的第一条语句中说明你正在做什么? – stef 2009-12-04 09:04:25

+0

嗯,我使用逗号分隔符几乎连接了所有未发送的消息的ID。这允许在一个SELECT中为每个用户返回一个id列表。 – 2009-12-06 14:02:12

0

规范化可以减少冗余,而且如果您有大量数据似乎是您的情况,那就是您要做的事。你不需要反规范化。让成员和消息之间有一个M到M的表。

随着M-to-M数据的增加,您可以归档旧数据。我甚至没有看到任何冲突,因为您的cron作业每天为此任务运行,并仅为当天的数据计算。因此,您可以每周归档M到M表格数据。

我相信如果通过向成员表添加额外的色彩来反规范化,将会有维护问题。我不推荐相同的。归档旧数据可以让您免于麻烦。

+0

归档旧数据,是指删除收到所有40条消息的用户的记录? – stef 2009-12-03 14:23:40

+0

将数据转移到另一个未被频繁访问的数据库中,这样您的主应用程序就可以很好地处理仅具有所需数据的M-2-M表格,并仍将旧数据保留在归档表格中。 – 2009-12-03 16:14:39

1

您也可以将发送的消息的id附加到members-table中的varchar字段。 尽管有良好的礼貌,这将使它可以很容易地使用一条语句来获取尚未发送给特定成员的消息。

就像这样(如果你与周围的ID“ - ”)

SELECT message.id 
FROM member, message 
WHERE member.id = 2321 
AND member.sentmessages NOT LIKE '%-' && id && '-%' 
0

可以实现通过预先分配在你的M-M表中的随机字符串和指针发出的最后一条消息的偏移发送随机消息的影响。

更详细地,创建表MemberMessages与列
memberId, messageIdList炭(80)或varchar,
lastMessage INT,
主键是memberId

cron作业的伪代码看起来像这样...

ONE。为会员选择下一条消息。如果此成员的MemberMessages中不存在行,请转至步骤TWO。选择一条消息在SQL看起来像

select substr(messageIdList, 2*lastMessage + 1, 2) as nextMessageId 
from MemberMessages 
where member_id = ? 

发送由nextMessageId

确定再更新lastMessage递增1,除非你已经达到39在这种情况下归零的消息。

update MemberMessages 
set lastMessage = MOD(lastMessage + 1, 40) 
where member_id = ? 

二。创建一个随机的messageIds列表,如2117390740...这样的对联字符串。这是您的随机消息ID列表,作为80个字符串。向MemberMessages插入一行,将您的member_id设置message_id_list添加到您的80个字符串中,并将last_message设置为1.

将由列表中第一个联号标识的消息发送给成员。

0

您可以只存储可用(未发送)的消息。这意味着当您添加或删除成员或消息类型(不能使用外键和触发器自动执行的任何操作)而是简化了交付时,需要额外的维护:从每个用户中随机选择一行,发送消息并删除该行。此外,随着消息发送,数据库会变小;-)

1

1.2每行8字节(+开销)的M行不是很多。这是如此之小,我甚至不打赌它需要索引(但当然你应该这样做)。

0

您可以创建一种队列/堆。

ReceivedMessages

UserId 
MessageId 

然后:

拿起一个构件和选择消息发送:

SELECT * FROM Messages WHERE MessageId NOT IN (SELECT MessageId FROM ReceivedMessages WHERE UserId = @UserId) LIMIT 1 

然后插入邮件ID以及用户ID到ReceivedMessages

和不发送逻辑这里

我希望有所帮助。

0

有可能更简单的方法来做到这一点,这取决于你想要“随机”的随机程度。

请考虑在一天的开始时,您将洗牌数组A,[0..39],它描述了今天要发送给用户的消息的顺序。

此外,请考虑您最多有40个Cron作业,用于向用户发送消息。给定的第N个计划作业,和ID所选择的用户ID,数字,可以选择M,则消息的索引来发送:

M =(A [N] + ID)%40.

这方式,给定的ID在同一天不会收到相同的消息两次(因为A [N]会不同),并且两个随机选择的用户有1/40的机会接收相同的消息。如果你想要更多的“随机性”,你可以使用多个数组。