【发布时间】:2014-05-15 23:09:10
【问题描述】:
我在 MySQL db 中有 >20 000 000 行的表,下面的查询在少量行上执行得很好,但如果有更多行则需要 2-3 秒。我该如何优化它以使其至少运行
SELECT sql_no_cache users.id AS uid,
name,
avatar,
avatar_date,
driver,
msg,
DATE,
messages.removed,
from_id = 528798 AS outbox ,
!(0 IN
(SELECT read_state
FROM messages AS msgs FORCE KEY(user_id_2)
WHERE (msgs.from_id = messages.from_id
OR msgs.from_id = messages.user_id)
AND msgs.user_id = 528798
AND removed = 0
)) AS read_state
FROM dialog,
messages,
users
WHERE messages.id = mid
AND ((uid1 = 528798
AND users.id = uid2)
OR (uid2 = 528798
AND users.id = uid1))
ORDER BY DATE DESC;
显示消息中的索引;
+----------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| messages | 0 | PRIMARY | 1 | id | A | 27531939 | NULL | NULL | | BTREE | | |
| messages | 1 | to_number | 1 | to_number | A | 22 | NULL | NULL | | BTREE | | |
| messages | 1 | from_id | 1 | from_id | A | 529460 | NULL | NULL | | BTREE | | |
| messages | 1 | from_id | 2 | to_number | A | 529460 | NULL | NULL | | BTREE | | |
| messages | 1 | user_id_2 | 1 | user_id | A | 655522 | NULL | NULL | | BTREE | | |
| messages | 1 | user_id_2 | 2 | read_state | A | 917731 | NULL | NULL | | BTREE | | |
| messages | 1 | user_id_2 | 3 | removed | A | 949377 | NULL | NULL | | BTREE | | |
| messages | 1 | idx_user_id | 1 | user_id | A | 809762 | NULL | NULL | | BTREE | | |
| messages | 1 | idx_from_id | 1 | from_id | A | 302548 | NULL | NULL | | BTREE | | |
+----------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
描述消息;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| from_id | int(11) | NO | MUL | NULL | |
| user_id | int(11) | NO | MUL | NULL | |
| group_id | int(11) | NO | | NULL | |
| to_number | varchar(30) | NO | MUL | NULL | |
| msg | text | NO | | NULL | |
| image | varchar(20) | NO | | NULL | |
| date | bigint(20) | NO | | NULL | |
| read_state | tinyint(1) | NO | | 0 | |
| removed | tinyint(1) | NO | | NULL | |
+------------+-------------+------+-----+---------+----------------+
解释扩展:
+----+--------------------+----------+-------------+---------------+-----------+---------+--------------------+--------+----------+---------------------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------------+----------+-------------+---------------+-----------+---------+--------------------+--------+----------+---------------------------------------------------------------------------+
| 1 | PRIMARY | dialog | index_merge | uid1,uid2 | uid1,uid2 | 4,4 | NULL | 1707 | 100.00 | Using sort_union(uid1,uid2); Using where; Using temporary; Using filesort |
| 1 | PRIMARY | users | ALL | PRIMARY | NULL | NULL | NULL | 608993 | 100.00 | Range checked for each record (index map: 0x1) |
| 1 | PRIMARY | messages | eq_ref | PRIMARY | PRIMARY | 4 | numbers.dialog.mid | 1 | 100.00 | |
| 2 | DEPENDENT SUBQUERY | msgs | ref | user_id_2 | user_id_2 | 6 | const,const,const | 2607 | 100.00 | Using where |
+----+--------------------+----------+-------------+---------------+-----------+---------+--------------------+--------+----------+---------------------------------------------------------------------------+
【问题讨论】:
-
您应该能够将该子查询作为 JOIN 放入。然后,您可以将其作为非相关子查询来执行,这应该更有效。但是,混合连接语法(在隐式连接和显式连接之间)有点混乱,并且您没有提供表声明来整理连接(即,您加入
messages.id = mid,但没有说明来自哪个表中间)。如果您可以添加声明,它将有所帮助。 -
从你的解释我猜这个问题更多的是用户表而不是消息表?你也有关于那张桌子的信息吗?
-
只是一个边缘信息:在我看来,您可以删除索引 idx_user_id(将使用 user_id_2)和 idx_from_id(将使用 from_id)以节省硬盘空间并加快数据操作操作(插入、更新、删除)。
-
您能否快速返回查询并进行编辑,以便所有列都表示为 table.column(甚至是 alias.column),以便我们知道各个表的列的来源。
-
当然你不应该从一开始就使用隐式语法,非常糟糕的编程技术和 SQL 反模式。