Mysql在发送数据时需要更多时间

时间:2012-04-20 11:03:18

标签: mysql sql drupal-views cpu-usage

我有这个特殊的查询需要很长时间才能执行,同一个表上的其他查询执行得非常快。在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是(uidtype)的索引。

查询缓存正在运行,以下是统计信息。

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    | 

2 个答案:

答案 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,但你会得到一些改进。让我们知道它的速度有多快。我猜的速度快了两倍。