Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/62.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 这是非规范化的情况吗?_Mysql - Fatal编程技术网

Mysql 这是非规范化的情况吗?

Mysql 这是非规范化的情况吗?,mysql,Mysql,我有一个拥有大约30000名成员的网站,我正在添加一项功能,包括从40条可能的消息池中随机发送消息。成员不能两次收到同一消息 一个表包含40条消息,另一个表映射消息和成员之间的多对多关系 cron脚本每天运行,从30000中选择一个成员,从40中选择一条消息,然后检查此消息以前是否发送给此用户。如果没有,则发送消息。如果是,它将再次运行查询,直到找到此成员尚未收到的消息 我现在担心的是,这个m-m表将变得非常大:在30000个成员和40条消息中,我们已经有120万行,我们必须通过这些行进行搜索,

我有一个拥有大约30000名成员的网站,我正在添加一项功能,包括从40条可能的消息池中随机发送消息。成员不能两次收到同一消息

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

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

我现在担心的是,这个m-m表将变得非常大:在30000个成员和40条消息中,我们已经有120万行,我们必须通过这些行进行搜索,以找到尚未发送的消息

这是非规范化的情况吗?在members表中,我可以添加40列message_1、message_2。。。消息_40,其中每次发送消息时添加1标志。如果我没有弄错的话,这将使cron脚本中的查询运行得更快


标准化减少了冗余,如果您有大量数据,您将这样做,这似乎是您的情况。你不需要去规范化。让成员和消息之间有一个M-to-M表

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


我相信,如果您通过向members表中添加额外的列来取消规范化,将会出现维护问题。我不推荐同样的。对旧数据进行归档可以避免麻烦。

标准化可以减少冗余,而且如果您拥有大量数据,您也可以这样做。你不需要去规范化。让成员和消息之间有一个M-to-M表

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


我相信,如果您通过向members表中添加额外的列来取消规范化,将会出现维护问题。我不推荐同样的。存档旧数据可以避免麻烦。

您还可以将发送消息的id附加到members表中的varchar字段。 尽管有良好的礼貌,这将使使用一个声明来获取尚未发送给特定成员的消息成为可能

就像这样,如果你在ID周围加上“-”

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

您还可以将已发送消息的id附加到members表中的varchar字段。 尽管有良好的礼貌,这将使使用一个声明来获取尚未发送给特定成员的消息成为可能

就像这样,如果你在ID周围加上“-”

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

我知道这并不能回答您最初的问题,但如果您选择了所有尚未发送给用户的消息,然后随机选择其中一条,不是会更快吗

请参见此处的伪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

我知道这并不能回答您最初的问题,但如果您选择了所有尚未发送给用户的消息,然后随机选择其中一条,不是会更快吗

请参见此处的伪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

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

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

cron作业的伪代码如下所示

一个。为成员选择下一条消息。如果此成员的MemberMessages中不存在任何行,请转至步骤2。选择下一条消息的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 = ?
两个。创建一个随机的MessageID列表,如2117390740。。。这是作为80个字符字符串的消息ID的随机列表。为您的成员id向MemberMessages插入一行,将message\u id\u列表设置为80个字符的字符串,并将last\u message设置为1


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

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

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

cron作业的伪代码如下所示

一个。选择下一条消息 r成员。如果此成员的MemberMessages中不存在任何行,请转至步骤2。选择下一条消息的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 = ?
两个。创建一个随机的MessageID列表,如2117390740。。。这是作为80个字符字符串的消息ID的随机列表。为您的成员id向MemberMessages插入一行,将message\u id\u列表设置为80个字符的字符串,并将last\u message设置为1


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

您只能存储可用的未发送消息。当您添加或删除成员或消息类型时,这意味着额外的维护没有任何东西不能用外键和触发器自动执行,但可以简化传递:从每个用户中随机选取一行,发送消息并删除该行。而且,随着消息的发送,您的数据库将变得更小-

您只能存储可用的未发送邮件。当您添加或删除成员或消息类型时,这意味着额外的维护没有任何东西不能用外键和触发器自动执行,但可以简化传递:从每个用户中随机选取一行,发送消息并删除该行。而且,随着消息的发送,您的数据库将变得更小-

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

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

你可以创建一种队列/堆

接收消息

UserId
MessageId
然后:

选择一个成员并选择要发送的消息:

SELECT * FROM Messages WHERE MessageId NOT IN (SELECT MessageId FROM ReceivedMessages WHERE UserId = @UserId) LIMIT 1
然后插入MessageId和UserId以接收消息

UserId
MessageId
把逻辑发送到这里


我希望这有帮助

您可以创建一种队列/堆

接收消息

UserId
MessageId
然后:

选择一个成员并选择要发送的消息:

SELECT * FROM Messages WHERE MessageId NOT IN (SELECT MessageId FROM ReceivedMessages WHERE UserId = @UserId) LIMIT 1
然后插入MessageId和UserId以接收消息

UserId
MessageId
把逻辑发送到这里


我希望这有帮助

有可能更容易做到这一点,这取决于您希望随机的程度

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

也可以考虑,最多有40个CRON作业,用于向用户发送消息。给定第n个cron作业ID和所选用户ID numeric,您可以选择M,即要发送的消息的索引:

SELECT * FROM Messages WHERE MessageId NOT IN (SELECT MessageId FROM ReceivedMessages WHERE UserId = @UserId) LIMIT 1
M=A[N]+ID%40


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

有可能更容易做到这一点,这取决于你想要随机的程度

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

也可以考虑,最多有40个CRON作业,用于向用户发送消息。给定第n个cron作业ID和所选用户ID numeric,您可以选择M,即要发送的消息的索引:

SELECT * FROM Messages WHERE MessageId NOT IN (SELECT MessageId FROM ReceivedMessages WHERE UserId = @UserId) LIMIT 1
M=A[N]+ID%40


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

我同意,我已经在下面详细阐述了这一点。你能澄清一下你在第一个select语句中使用CONCAT_WS做什么吗?我几乎用逗号分隔符将所有未发送的消息ID连接在一起。这允许在一个SELECT中返回每个用户的ID列表。我同意,我在下面对此进行了详细阐述。您是否可以澄清您在第一个SELECT语句中使用CONCAT_WS执行的操作?我几乎使用逗号分隔符将所有未发送的消息ID连接在一起。这允许在一次选择中返回每个用户的ID列表。通过存档旧数据,您的意思是删除已收到全部40条消息的用户的记录?将数据转移到另一个不太频繁访问的数据库中,以便您的主应用程序能够很好地处理仅包含所需数据的M-2-M表,并且仍将旧数据保留在存档表中。通过存档旧数据,您的意思是删除已收到全部40条消息的用户的记录?将数据转移到另一个不太频繁访问的数据库中,以便您的主应用程序能够很好地处理只包含所需数据的M-2-M表,并且仍将旧数据保留在存档表中。