我有一个表,其中包含来自用户的消息。该设计将为发送者和接收者保留单个消息副本(每个人都有自己的消息读取/删除标志)。
CREATE TABLE cloob_msg.cl_inbox (
id int(11) NOT NULL AUTO_INCREMENT,
`user` int(11) NOT NULL,
contact int(11) NOT NULL,
sdate timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
body text NOT NULL,
userstatus tinyint(4) NOT NULL DEFAULT 1 COMMENT '0: new, 1:read, 2: deleted',
contactstatus tinyint(4) NOT NULL DEFAULT 0,
class tinyint(4) NOT NULL DEFAULT 0,
attachtype tinyint(4) NOT NULL DEFAULT 0,
attachsrc varchar(255) DEFAULT NULL,
PRIMARY KEY (id),
INDEX i_class (class),
INDEX i_contact_user (contact, user),
INDEX i_contactstatus (contactstatus),
INDEX i_user_contact (user, contact),
INDEX i_userstatus (userstatus)
)
为了列出两个人之间的对话,我们使用以下查询:
select * from cl_inbox
where (user=user1 and contact=user2 and userstatus<>2)
or (user=user2 and contact=user1 and contactstatus<>2)
order by id limit ?,?
现在,如何对表进行分区(我们有数十亿条消息,因此实际上需要对其进行分区)?我应该选择什么唯一键和哪些分区字段?
谢谢。
答案 0 :(得分:2)
为什么要分区?它本质上不会提供任何性能。
要加快查询速度,请将or
更改为union
:
( select * from cl_inbox
where (user=user1 and contact=user2 and userstatus<>2)
order by id limit ?,?
) UNION ALL
( select * from cl_inbox
where (user=user2 and contact=user1 and contactstatus<>2)
order by id limit ?,?
)
现在每个部分都可以独立使用i_contact_user
或i_user_contact
。 (您的版本必须进行全表扫描。)这将运行得更快。顺便说一句,对于该查询,这两个索引同样好。除非同时需要其他两个查询,否则建议删除其中一个。 *status
上的索引(和其他“标志”)可能无用,对于该查询肯定是无用的。
下一个问题:使用OFFSET
进行分页是有问题的。并且当切换到UNION
时,它现在已损坏。
因此,“记住您离开的地方”。既然您说的是order by id
,那么我假设ID符合用户界面所需的顺序?删除OFFSET
并使用id
:
( select * from cl_inbox
where (user=user1 and contact=user2 and userstatus<>2)
AND id < $left_off
ORDER BY id DESC
LIMIT ?
) UNION ALL
( select * from cl_inbox
where (user=user2 and contact=user1 and contactstatus<>2)
AND id < $left_off
ORDER BY id DESC
LIMIT ?
)
ORDER BY id DESC
LIMIT ?
(是的,我故意重复ORDER BY
和LIMIT
。)而且我自由地扭转了局面–您是否不想要 latest 消息首先?
更多讨论:http://mysql.rjweb.org/doc.php/pagination
如果您期望有一个巨大的表,并希望删除“旧”记录,那么我们可以讨论分区以方便旧行。但这是我看到的对该表进行分区的唯一用途。