3

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

2 回答 2

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 
于 2012-04-20T11:25:14.803 回答
2

这是一个糟糕的查询。它从slk表中选择所有 55862 行并将所有 55862 行连接到另外两个表。

大型结果集上的 JOIN 是性能杀手,因为 MySQL 最多只能对主表中的每一行执行查找到详细表中的相应行。如果行太多,MySQL 将决定扫描整个明细表而不是执行如此多的查找更快。

正如 ypercube 建议的那样,在 上创建多列索引node_users: (uid, type)将有助于第二个连接(到 node_users)表。

理想情况下,如果这个查询使用的是 INNER JOINs 而不是 LEFT OUTER JOINs,我们可以通过允许 MySQL 向后遍历它来优化查询,从 ypercube 开始AND node_users.type = 'profile'并按照他建议的顺序给它提供 ypercube 建议的索引。但是,由于它们是左连接,MySQL 仍然希望获取slk表中的所有行,并将从那里开始。

要在不修改此查询的情况下提高此查询的性能,您唯一可以做的额外的事情是避免使用覆盖索引来访问表数据。

这将使用更多的内存,但希望它会更快,因为它可以从索引中读取所有值(在内存中)而不是访问磁盘。这意味着您有足够的 RAM 来支持内存中的所有索引,并且您已配置 MySQL 以使用它。

您已经有一个覆盖索引users(参见Using indexEXPLAIN 结果)。您希望 DERIVED 查询的所有三行都显示Using index在 Extra 列中。

创建附加的以下覆盖索引:

slk: (key_id, uid)

上面已经提到了这个,但我再次将它包括在这里,所以你不要忘记它:

node_users: (uid, type)

你不会在这里获得突破性的表现,因为你仍然需要做所有的 JOIN,但你会得到一些改进。让我们知道它有多快。我猜快两倍。

于 2012-04-20T14:06:24.157 回答