我有一个聊天消息表,该表已增长到超过 400 万行。
我需要在特定日期范围内接收发送给用户和接收自用户的消息。
一开始这个查询没问题,但以当前表的大小,它花费的时间太长了(>10 秒)。
我试过创建很多索引组合; MySQL 在 messageType+sentDate 上使用复合索引
SELECT `ofMessageArchive`.*
FROM `ofMessageArchive`
WHERE `ofMessageArchive`.`messageType` IN ('Message')
AND (ofMessageArchive.fromJID = 'user@example.com' OR ofMessageArchive.toJID = 'user@example.com')
AND (ofMessageArchive.sentDate > '1462235333109')
ORDER BY ofMessageArchive.sentDate ASC LIMIT 50
我有什么想法可以优化这个查询吗?
[EDIT] EXPLAIN
结果:
+----+-------------+------------------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------+---------+-------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------+---------+-------+--------+-------------+
| 1 | SIMPLE | ofMessageArchive | ref | index_ofMessageArchive_on_fromJID_and_toJID,index_ofMessageArchive_on_sentDate,index_ofMessageArchive_on_messageType,idxMessageTypeSentDate,ofMessageArchive_fromjid_idx,ofMessageArchive_tojid_idx | idxMessageTypeSentDate | 767 | const | 731570 | Using where |
+----+-------------+------------------+------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------+---------+-------+--------+-------------+
最佳答案
您遇到了将传出消息与传入消息区别对待的问题(基本上是规范化问题),因此您不能直接在用户名上使用索引,这可能是您最重要的值(value)。
如果您还没有它们,请创建索引 ofMessageArchive(fromJID, messageType, sentDate)
和
ofMessageArchive(toJID, messageType, sentDate)
(可能顺序不同,具体取决于您对每一列有多少不同的值以及您使用的其他查询,但从这个顺序开始)。
要使用这些索引,请使用union
:
select * from
( (SELECT `ofMessageArchive`.*
FROM `ofMessageArchive`
WHERE (ofMessageArchive.fromJID = 'user@example.com')
AND `ofMessageArchive`.`messageType` IN ('Message')
AND (ofMessageArchive.sentDate > '1462235333109')
ORDER BY ofMessageArchive.sentDate ASC LIMIT 50
) union
(SELECT `ofMessageArchive`.*
FROM `ofMessageArchive`
WHERE (ofMessageArchive.toJID = 'user@example.com')
AND `ofMessageArchive`.`messageType` IN ('Message')
AND (ofMessageArchive.sentDate > '1462235333109')
ORDER BY ofMessageArchive.sentDate ASC LIMIT 50
)
) s
order by s.sentDate ASC LIMIT 50
关于mysql - 优化大表上的日期范围搜索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39244124/