我有这个特殊的查询需要很长时间才能执行,同一个表上的其他查询执行得非常快。在mysql中启用了Querycache,但是下面的查询每次都需要超过80秒,并且CPU超过了100%的利用率。
我无法修改查询,因为它是由Drupal生成的。我还能做些什么来改善表现吗?
查询是:
select count(*)
from (
SELECT slk.key_id AS key_id
FROM slk slk
LEFT JOIN users users ON slk.uid = users.uid
LEFT JOIN node node_users ON users.uid = node_users.uid
AND node_users.type = 'profile'
) count_alias;
以下是个人资料信息:
+--------------------------------+-----------+
| Status | Duration |
+--------------------------------+-----------+
| starting | 0.000029 |
| checking query cache for query | 0.000093 |
| Opening tables | 0.000210 |
| System lock | 0.000007 |
| Table lock | 0.000075 |
| optimizing | 0.000008 |
| statistics | 0.000113 |
| preparing | 0.000027 |
| executing | 0.000004 |
| Sending data | 66.086903 |
| init | 0.000027 |
| optimizing | 0.000009 |
| executing | 0.000018 |
| end | 0.000003 |
| query end | 0.000004 |
| freeing items | 0.000049 |
| storing result in query cache | 0.000116 |
| removing tmp table | 0.033162 |
| closing tables | 0.000106 |
| logging slow query | 0.000003 |
| logging slow query | 0.000085 |
| cleaning up | 0.000007 |
+--------------------------------+-----------+
关于查询的解释给出了:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | PRIMARY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
| 2 | DERIVED | slk | ALL | NULL | NULL | NULL | NULL | 55862 | |
| 2 | DERIVED | users | eq_ref | PRIMARY | PRIMARY | 4 | gscom.slk.uid | 1 | Using index |
| 2 | DERIVED | node_users | ref | node_type,uid,idx_ctp | uid | 4 | gscom.users.uid | 3 | |
idx_ctp
是(uid
,type
)的索引。
查询缓存正在运行,以下是统计信息。
show variables like '%query_cache%';
:
| Variable_name | Value |
| have_query_cache | YES |
| query_cache_limit | 2097152 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 52428800 |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
mysql> show status like '%Qcache%';
:
| Variable_name | Value |
| Qcache_free_blocks | 1255 |
| Qcache_free_memory | 22902848 |
| Qcache_hits | 1484908 |
| Qcache_inserts | 1036344 |
| Qcache_lowmem_prunes | 95086 |
| Qcache_not_cached | 3975 |
| Qcache_queries_in_cache | 14271 |
| Qcache_total_blocks | 30117 |
答案 0 :(得分:2)
您需要索引:
slk
:(uid)
node_users
:(type, uid)
可以在没有子查询的情况下重写查询,如:
SELECT COUNT(*)
FROM slk
LEFT JOIN users
ON slk.uid = users.uid
LEFT JOIN node node_users
ON users.uid = node_users.uid
AND node_users.type = 'profile'
我真的不确定你为什么使用LEFT JOIN
。您可以使用INNER JOIN
并获得相同的结果。或者只使用简单的:
SELECT COUNT(*)
FROM slk
答案 1 :(得分:2)
这是一个糟糕的查询。它从slk
表中选择所有55862行,并将所有55862行连接到另外两个表。
大型结果集上的JOIN是性能杀手,因为MySQL必须充其量地对主表中的每一行执行查找到详细信息表中的相应行。如果有太多行,MySQL将决定扫描整个详细信息表而不是执行如此多的搜索更快。
在ypercube建议的node_users: (uid, type)
上创建多列索引将有助于第二次连接(到node_users)表。
理想情况下,如果此查询使用INNER JOIN而不是LEFT OUTER JOIN,我们可以通过允许MySQL向后遍历它来优化查询,从AND node_users.type = 'profile'
开始并给它ypercube建议的索引,在命令他建议。但是,由于它们是左连接,MySQL仍然希望获取slk
表中的所有行,并从那里开始。
在不修改此查询的情况下,您可以做的唯一额外的事情就是避免使用覆盖索引来访问表数据。
这将使用更多内存,但希望它会更快,因为它可以读取索引中的所有值(在内存中)而不是命中磁盘。这意味着你有足够的RAM来支持在内存中拥有所有索引,并且你已经配置了MySQL来使用它。
您已在users
上有覆盖索引(请参阅EXPLAIN结果中的Using index
)。您希望DERIVED查询的所有三行在Extra列中显示Using index
。
创建额外的以下覆盖索引:
slk: (key_id, uid)
上面已经提到了这个,但我再次把它包括在内,所以你不要忘记它:
node_users: (uid, type)
你不会在这里获得突破性的表现,因为你仍然需要做所有的JOIN,但你会得到一些改进。让我们知道它的速度有多快。我猜的速度快了两倍。