这是非正规化的情况吗?

时间:2009-12-03 10:57:45

标签: mysql

我有一个拥有大约30,000名成员的网站,我正在添加一项功能,该功能涉及从40条可能的消息池中发送随机消息。会员永远不会两次收到相同的消息。

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

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

我现在担心的是这个mm表会变得非常大:有30,000名成员和40条消息,我们已经拥有120万行,我们必须搜索这些行以查找尚未发送的消息。< / p>

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

8 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

您还可以将已发送消息的id附加到members-table中的varchar-field。 尽管有礼貌,但这样可以很容易地使用一个语句来获取尚未为特定成员发送的消息。

就像这样(如果你用' - '包围id)

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

规范化可以减少冗余,如果您拥有大量数据,那么您就可以做到这一点。你不需要非规范化。让成员和消息之间有一个M-to-M表。

您可以在M-to-M数据增加时归档旧数据。我甚至没有看到任何冲突,因为您的cron作业每天都会运行此任务,并仅考虑当天的数据。因此,您可以每周归档M-to-M表数据。

我相信如果通过向成员表添加其他颜色来进行非规范化,则会出现维护问题。我不推荐相同的。存档旧数据可以帮助您免于麻烦。

答案 4 :(得分:0)

通过预先分配m-m表中的随机字符串和指向最后一条消息的偏移量的指针,可以实现发送随机消息的效果。

更详细地,创建一个包含列的表MemberMessages memberId, messageIdList char(80)或varchar,
lastMessage int,
主键是memberId

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

ONE。选择成员的下一条消息。如果此成员的MemberMessages中不存在任何行,请转到步骤二。用于选择下一条消息的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 = ?

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

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

答案 5 :(得分:0)

您只能存储可用的(未发送)消息。这意味着当您添加或删除成员或消息类型(没有任何无法通过外键和触发器自动化)时的额外维护,但简化了交付:从每个用户中选择一条随机行,发送消息并删除该行。此外,随着消息的发送,您的数据库将变小; - )

答案 6 :(得分:0)

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

ReceivedMessages

UserId
MessageId

然后:

选择一位成员并选择要发送的邮件:

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

然后将MessageId和UserId插入ReceivedMessages

并在此处发送逻辑

我希望有所帮助。

答案 7 :(得分:0)

有更简单的方法可以做到这一点,具体取决于你想要“随机”的随机性。

考虑在一天开始时,你将一个阵列A,[0..39]混乱,它描述了今天发送给用户的消息的顺序。

另外,请考虑您最多有40个Cron作业,这些作业用于向用户发送消息。给定第N个cron作业,并且ID为所选用户ID,数字,您可以选择M,即要发送的消息的索引:

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

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