MySQL MyISAM表性能......痛苦,痛苦地缓慢

时间:2009-05-09 07:06:18

标签: sql mysql performance query-optimization myisam

我有一个表结构,可以总结如下:

pagegroup
* pagegroupid
* name

有3600行

page
* pageid
* pagegroupid
* data

引用pagegroup; 有10000行; 每页组可以有1-700行之间的任何内容; 数据列的类型为mediumtext,该列包含每行100k - 200kbytes的数据

userdata
* userdataid
* pageid
* column1
* column2
* column9

参考页面; 有大约300,000行; 每页可以有大约1-50行

上面的结构是相当直接的转发,问题是从userdata到页面组的连接非常非常慢,即使我已经索引了应该被索引的所有列。运行此类连接的查询所需的时间(userdata inner_join page inner_join pagegroup)超过3分钟。考虑到我根本没有选择数据列这一事实,这非常慢。查询示例耗时太长:

SELECT userdata.column1, pagegroup.name
FROM userdata
INNER JOIN page USING( pageid )
INNER JOIN pagegroup USING( pagegroupid )

请帮助解释为什么需要这么长时间,我该怎么做才能让它更快。

编辑#1

按照胡言乱语说明回复:

id  select_type  table      type    possible_keys        key      key_len  ref                         rows    Extra
1   SIMPLE       userdata   ALL     pageid                                                             372420
1   SIMPLE       page       eq_ref  PRIMARY,pagegroupid  PRIMARY  4        topsecret.userdata.pageid   1
1   SIMPLE       pagegroup  eq_ref  PRIMARY              PRIMARY  4        topsecret.page.pagegroupid  1

编辑#2

SELECT
u.field2, p.pageid
FROM
userdata u
INNER JOIN page p ON u.pageid = p.pageid;
/*
0.07 sec execution, 6.05 sec fecth
*/

id  select_type  table  type    possible_keys  key      key_len  ref                rows     Extra
1   SIMPLE       u      ALL     pageid                                              372420
1   SIMPLE       p      eq_ref  PRIMARY        PRIMARY  4        topsecret.u.pageid 1        Using index

SELECT
p.pageid, g.pagegroupid
FROM
page p
INNER JOIN pagegroup g ON p.pagegroupid = g.pagegroupid;
/*
9.37 sec execution, 60.0 sec fetch
*/

id  select_type  table  type   possible_keys  key          key_len  ref                      rows  Extra
1   SIMPLE       g      index  PRIMARY        PRIMARY      4                                 3646  Using index
1   SIMPLE       p      ref    pagegroupid    pagegroupid  5        topsecret.g.pagegroupid  3     Using where

故事的道德

如果遇到诸如此类的性能问题,请将中/长文本列保留在单独的表中。

6 个答案:

答案 0 :(得分:4)

userdata表中columnX的数据类型和用途是什么?应该注意的是,任何文本数据类型(即排除char,varchar)都会强制在磁盘上创建任何临时表。既然你在没有条件,分组或排序的情况下进行直接连接,它可能不需要任何临时表,除了聚合最终结果。

如果您向我们展示如何创建索引,我认为这也会非常有用。需要记住的一点是,虽然InnoDB将表的主键连接到每个索引,但MyISAM却没有。这意味着,如果您将列 name 编入索引并使用LIKE搜索它,但仍希望获取该页组的 id ;然后查询仍然需要访问该表以获取 id ,而不是能够从索引中检索它。

这意味着,在您的情况下,如果我理解您对 apphacker 的正确评论,就是获取每个用户页面组的名称。查询优化器希望使用索引进行连接,但是对于每个结果,它还需要访问该表以检索页组名称。如果 name 上的数据类型不大于中等varchar,即没有文本,您还可以创建一个索引(id,name),这将使查询能够直接从索引中获取名称。 / p>

作为最后一次尝试,您指出如果mediumtext不在页表中,整个查询可能会更快。

  1. 此列是从您正在运行的查询中排除的吗?
  2. 您还可以尝试将页面数据与页面“配置”分开,即它属于哪个组。那你可能会有类似的东西:
      • PAGEID
      • pageGroupId
    • PageData
      • PAGEID
      • 数据
  3. 这样可以让您更快地加入,因为Pages中的列没有占用太多空间。然后,当您需要显示某个页面时,您可以使用pageId-column上的PageData表连接,以获取显示特定页面所需的数据。

答案 1 :(得分:2)

找出MySQL对您的查询执行的操作的简单方法是让它向您解释查询。运行此命令并查看输出:

EXPLAIN SELECT userdata.column1, pagegroup.name
FROM userdata
INNER JOIN page USING( pageid )
INNER JOIN pagegroup USING( pagegroupid )

MySQL将告诉您它处理查询的顺序以及它使用的索引。您创建索引的事实并不意味着MySQL实际使用它们。

另见Optimizing queries with EXPLAIN

修改

EXPLAIN的输出看起来很好。它在userdata表上执行全表扫描,但这是正常的,因为您要返回其中的所有行。优化此方法的最佳方法是重新考虑您的应用程序。你真的需要返回所有372K行吗?

答案 2 :(得分:2)

我假设userdata表非常大并且不适合内存。 MySQL必须从硬盘读取整个表,即使它只需要两个小列。

您可以尝试通过定义包含查询所需内容的索引来消除扫描整个表的需要。这样,索引不是一种方便搜索主表的方法,但它是表本身的简写版本。 MySQL只需要从磁盘读取速记表。

索引可能如下所示:

column1, pageid

这必须是非群集的,否则它将成为大表的一部分,从而破坏其目的。有关MySQL如何决定群集索引的想法,请参阅this page。最简单的方法似乎是确保在pageid上有一个主键,它将被聚集,因此辅助column1 + pageid索引将是非聚集的。

答案 3 :(得分:1)

一个可能的问题是MySQL每个查询只使用一个索引,也许你没有这些列的单个索引 - 或者MySQL的查询优化器没有选择它。 EXPLAIN SELECT& c告诉你什么?

答案 4 :(得分:1)

我会先打破查询,弄清楚是否有一个慢速和一个快速部分,或两者都很慢(对不起,我不喜欢USING语法,所以我打算用ON):

SELECT 
  u.userdata, p.pageid
FROM
  userdata u
  INNER JOIN page p ON u.pageid = p.pageid

SELECT 
  p.pageid, g.pagegroupid
FROM
  page 
  INNER JOIN pagegroup g ON p.pagegroupid = g.pagegroupid

这给你带来了什么?使用EXPLAIN EXTENDED运行这些将提供其他提示。

答案 5 :(得分:1)

看起来你正在对userdata上的所有行进行连接,然后尝试选择所有内容。这是page pagegroup中的userdata WHERELIMIT条款在哪里?没有userdata,您想要多少结果?为什么不在explain结果的{{1}}行中计算行数,这会加快查询速度。嘿。