我有一个Web服务器,在该服务器上实现了自己的消息传递系统。 我现在需要创建一个API,以检查用户是否有新消息。
我的数据库表很简单:
ID - Auto Increment, Primary Key (Bigint)
Sender - Varchar (32) // Foreign Key to UserID hash from Users DB Table
Recipient - Varchar (32) // Foreign Key to UserID hash from Users DB Table
Message - Varchar (256) //UTF8 BIN
我正在考虑制作一个API,用于估计给定用户是否有新消息。我正在考虑使用以下方法之一:
A)选择发件人或收件人是我的邮件中的count(*)
。
(如果该号码>先前的号码,我有新消息)
B)选择发件人或收件人是我的邮件中的max(ID)
。
(如果max(ID)>比以前的数字大,我会收到一条新消息)
我的问题是:我可以以某种方式计算出哪种方法会消耗更少的服务器资源吗?还是有一些文章?也许我没有提到另一种方法?
答案 0 :(得分:16)
在MySQL InnoDB中,SELECT COUNT(*) WHERE secondary_index = ?
是一项昂贵的操作,并且当用户有很多消息时,此查询可能需要很长时间。即使使用索引,引擎仍需要对所有匹配记录进行计数。随着消息总数的增加,性能将下降。
另一方面,SELECT MAX(id) WHERE secondary_index = ?
通过执行所谓的松散索引扫描可以非常有效地传递该索引中的最高id。性能将几乎保持不变。
如果您想了解原因,请考虑查找InnoDB用于组织其数据的B+Tree数据结构。
如果要求仅检查是否有新条消息(而不是计数),我建议您选择SELECT MAX(id)
。
此外,如果您依赖消息计数,则可能会在竞争条件上留下空白。如果用户删除了一条消息并在两个轮询间隔之间收到了一条新消息,该怎么办?
答案 1 :(得分:4)
要获得某人有新消息的信息,请执行此操作。当系统中记录了新消息时,更新users
表中的字段(我假设这是名称)。您只需要收件人的ID。您可以创建一个after insert
触发器(假设:有users2messages
表),该触发器使用指示有消息的布尔标志来更新users表。
这种方法远远快于计数索引(无论是主索引还是次索引)。用户执行操作时,可以在收到新消息时用users
更新has_messages = 0
表,而用has_messages = 1
更新表。它简单,有效,可伸缩,并使用触发器进行维护使其变得轻松,无缝。
我敢肯定会有反对者不喜欢触发器,您可以在将用户与新消息相关联时手动进行。
答案 2 :(得分:-1)
如果您需要知道新消息的数量,请使用
Select count(*) from Messages where user_id in (sender, recipient) and id > last_seen_id
是您的最佳选择。
我喜欢尽可能使用exists
,因此如果要确定是否有新消息,我的查询将是Select exists(Select 1 from Messages where user_id in (sender, recipient) and id > last_seen_id)
。存在的好处是,一旦找到1条记录,它就会返回true
。
编辑:为了避免在阅读此答案时造成任何混淆,这两个查询还将包括对other_user_id in (sender, recipient)
的检查,以便仅返回2个特定用户之间的消息。
答案 3 :(得分:-2)
@FeHora您谈论的是不使用键来节省数据库空间。该表设计浪费了更多的数据库空间。
ID - Auto Increment, Primary Key (Bigint)
bigint
真的有必要吗?让我们假设,一条消息每秒发送一次。 int unsigned
足够使用126年。而且,如果您有太多消息,则必须输入密钥。
Sender - Varchar (32) // Foreign Key to UserID hash from Users DB Table
Recipient - Varchar (32) // Foreign Key to UserID hash from Users DB Table
为什么不使用UserID
(通常是int unsigned
)。
然后我将添加一个可见标志。顺便说一句,您可以为所有提交的属性添加not null
。
seen tinyint not NULL.
最后,我特别推荐@Mjh的变体:在用户记录中定义标志has_messages
或new_messages
,或两者都定义。通常,用户记录已加载,因此它不是附加的数据库查询。