此查询在VPS上运行12秒以上。它加入了3个表。只有第一个" topcics"有大约70k行,其他大约是20行和" post_cc"大约1500。
SELECT topics.*, employee.username, accounts.ac_name, accounts.ac_mail
FROM topics
INNER JOIN employee ON employee.id_user = topics.id_owner
INNER JOIN accounts ON accounts.id_account = topics.id_account
WHERE topics.status IN ('1','3') AND ( topics.id_owner IN (12, 5) OR topics.id_post IN
(SELECT DISTINCT(id_post) FROM post_cc WHERE id_employee IN (12, 5) ) )
ORDER BY topics.creationdate DESC LIMIT 0,25
我已经尝试过(没有任何改进)删除子查询和第一个"员工"加入。如果我删除"帐户" join,查询在0.1秒内运行,但在分页期间需要所有表数据进行排序。
说明:
+----+--------------------+------------+-----------------+-----------------------+---------+---------+-----------------+-------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------+-----------------+-----------------------+---------+---------+-----------------+-------+----------------------------------------------+
| 1 | PRIMARY | topics | ALL | id_owner,id_account | NULL | NULL | NULL | 75069 | Using where; Using temporary; Using filesort |
| 1 | PRIMARY | accounts | ALL | PRIMARY | NULL | NULL | NULL | 5 | Using where; Using join buffer |
| 1 | PRIMARY | employee | eq_ref | PRIMARY | PRIMARY | 3 | topics.st_owner | 1 | Using where |
| 2 | DEPENDENT SUBQUERY | post_cc | unique_subquery | PRIMARY | PRIMARY | 8 | func,const | 1 | Using index; Using where |
+----+--------------------+------------+-----------------+-----------------------+---------+---------+-----------------+-------+----------------------------------------------+
我已将建议的密钥添加为索引,它将时间提高了2秒,但它仍然太慢。
缩短的表格:
topics
+--------------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+---------------------+------+-----+---------+----------------+
| id_post | int(10) unsigned | NO | PRI | NULL | auto_increment |
| id_account | int(10) unsigned | YES | MUL | 0 | |
| mail | varchar(256) | YES | MUL | NULL | |
| from_name | varchar(512) | YES | | NULL | |
| title | varchar(512) | YES | | NULL | |
| content | text | YES | | NULL | |
| id_owner | int(10) unsigned | YES | MUL | NULL | |
| creationdate | datetime | YES | | NULL | |
+--------------------+---------------------+------+-----+---------+----------------+
employee
+---------------------+-----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+-----------------------+------+-----+---------+----------------+
| id_employee | mediumint(8) unsigned | NO | PRI | NULL | auto_increment |
| id_user | mediumint(8) unsigned | NO | | NULL | |
| id_owner | tinyint(1) | YES | | 0 | |
| active | tinyint(1) | YES | | 1 | |
| username | varchar(64) | YES | | NULL | |
| email | varchar(128) | YES | | NULL | |
+---------------------+-----------------------+------+-----+---------+----------------+
accounts
+----------------------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------------+---------------------+------+-----+---------+----------------+
| id_account | int(10) unsigned | NO | PRI | NULL | auto_increment |
| ac_mail | int(10) unsigned | YES | UNI | NULL | |
| ac_name | varchar(512) | YES | | NULL | |
| last_sync_time | datetime | YES | | NULL | |
+----------------------------+---------------------+------+-----+---------+----------------+
post_cc
+------------------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+---------------------+------+-----+---------+-------+
| id_post | int(10) unsigned | NO | PRI | NULL | |
| id_employee | int(10) unsigned | NO | PRI | NULL | |
| notifications | tinyint(3) unsigned | YES | | 1 | |
+------------------------+---------------------+------+-----+---------+-------+
答案 0 :(得分:2)
一个可能怀疑的是依赖性提示。
MySQL正在处理外部查询返回的每一行的子查询(它已经被其他谓词过滤掉了。
要提高性能,请考虑将其重写为JOIN操作或EXISTS谓词。
要用JOIN操作替换它,因为谓词中的OR
,所以需要是OUTER JOIN(而不是INNER JOIN)。
作为一种方法的例子:
SELECT topics.*
, employee.username
, accounts.ac_name
, accounts.ac_mail
FROM topics
JOIN employee ON employee.id_user = topics.id_owner
JOIN accounts ON accounts.id_account = topics.id_account
LEFT
JOIN ( SELECT DISTINCT q.id_post
FROM post_cc q
WHERE q.id_employee IN (12, 5)
) p
ON p.id_post = topics.id_post
WHERE topics.status IN ('1','3')
AND ( topics.id_owner IN (12, 5)
OR p.id_post IS NOT NULL
)
ORDER BY topics.creationdate DESC LIMIT 0,25
我建议你对它运行一个EXPLAIN,看看它是如何运作的。
另一种选择是考虑EXISTS谓词。在偶然的情况下,我们可以让它表现得更好,但往往不是。
SELECT topics.*
, employee.username
, accounts.ac_name
, accounts.ac_mail
FROM topics
JOIN employee ON employee.id_user = topics.id_owner
JOIN accounts ON accounts.id_account = topics.id_account
WHERE topics.status IN ('1','3')
AND ( topics.id_owner IN (12, 5)
OR EXISTS ( SELECT 1
FROM post_cc q
WHERE q.id_employee IN (12, 5)
AND q.id_post = topics.id_post
)
)
ORDER BY topics.creationdate DESC LIMIT 0,25
对于性能,这几乎需要EXISTS子句中子查询的合适覆盖索引,例如:
ON post_cc (id_post, id_employee)
你可以尝试运行一个EXPLAIN,看看它的表现如何。
我们发现MySQL并没有在topics
表上使用索引。
我们可能会让MySQL避免昂贵的"使用filesort"如果我们的索引的前导列为creationdate
。
部分问题可能是谓词中的 OR 。我们可能会尝试将该查询重写为两个单独的查询,并将它们与UNION ALL
集合操作相结合。但是,如果我们这样做,我们真的希望看到使用topic
上的索引(我们可能通过招致两次扫描70,000行来提高性能。
SELECT topics.*
, employee.username
, accounts.ac_name
, accounts.ac_mail
FROM topics
JOIN employee ON employee.id_user = topics.id_owner
JOIN accounts ON accounts.id_account = topics.id_account
WHERE topics.status IN ('1','3')
AND topics.id_owner IN (12, 5)
UNION ALL
SELECT topics.*
, employee.username
, accounts.ac_name
, accounts.ac_mail
FROM topics
JOIN employee ON employee.id_user = topics.id_owner
JOIN accounts ON accounts.id_account = topics.id_account
JOIN ( SELECT DISTINCT q.id_post
FROM post_cc q
WHERE q.id_employee IN (12, 5)
) p
ON p.id_post = topics.id_post
WHERE topics.status IN ('1','3')
AND ( topics.id_owner NOT IN (12, 5) OR topics.id_owner IS NULL )
ORDER BY 8 DESC LIMIT 0,25
通过查询该表单,我们更有可能让MySQL在主题表上使用合适的索引,
... ON topics (id_owner, status)
... ON topics (id_post, status, id_owner)
答案 1 :(得分:0)
为什么不在post_cc表中使用左连接,然后使用条件?
像这样。
SELECT topics.*,
employee.username,
accounts.ac_name,
accounts.ac_mail
FROM topics
INNER JOIN employee ON employee.id_user = topics.id_owner
INNER JOIN accounts ON accounts.id_account = topics.id_account
LEFT JOIN post_cc ON id_employee IN (12, 5)
WHERE topics.status IN ('1','3')
AND ( topics.id_owner IN (12, 5) OR topics.id_post IN (post_cc.post_id)
ORDER BY topics.creationdate DESC LIMIT 0,25;
答案 2 :(得分:0)
罪魁祸首:
75069(行)|用在哪里;使用临时;使用filesort
这就是我们需要摆脱的东西。
可能的解决方案:在topics.creationdate
上添加索引。
作为旁注,该查询还具有id_post
,st_owner
和status
的条件,因此是topics(creationdate, id_post, st_owner, status)
上的综合索引(或最后一个的排列)三列 - 使用您的数据集进行测试)可能会有所帮助。但是,您的查询似乎无论如何都会拉动您的大部分表,因此我希望简单的索引就足够了。