加快这个大加入

时间:2010-11-25 16:13:54

标签: sql mysql optimization query-optimization

编辑:以下问题中的错误解释了观察结果。我可以删除这个问题,但这可能对某人有用。错误的是,当我认为它正在运行SELECT * FROM t时,服务器上运行的实际查询是SELECT t.* FROM t(这很愚蠢)(这会产生重大影响)。请参阅tobyobrian的回答及其评论。


在具有架构的情况下,我的查询速度太慢,如下所示。表t包含由t_id索引的数据行。 t通过联结表xy与表t_xt_y相邻,每个表只包含JOIN所需的foreigns键:

CREATE TABLE t (
  t_id INT NOT NULL PRIMARY KEY,
  data columns...
);
CREATE TABLE t_x (
  t_id INT NOT NULL,
  x_id INT NOT NULL,
  PRIMARY KEY (t_id, x_id),
  KEY (x_id)
);
CREATE TABLE t_y (
  t_id INT NOT NULL,
  y_id INT NOT NULL,
  PRIMARY KEY (t_id, y_id),
  KEY (y_id)
);

我需要导出t中的 stray 行,即未在任何联结表中引用的行。

SELECT t.* FROM t
LEFT JOIN t_x ON t_x.t_id=t.t_id
LEFT JOIN t_y ON t_y.t_id=t.t_id
WHERE t_x.t_id IS NULL OR t_y.t_id IS NULL
INTO OUTFILE ...;

t有21 M行,而t_xt_y都有大约25 M行。所以这自然会是一个缓慢的查询。

我正在使用MyISAM所以我想我会尝试通过预加载t_xt_y索引加快速度。 t_x.MYIt_y.MYI的总大小约为1.2 M字节,所以我为它们创建了一个专用的密钥缓冲区,将它们的PRIMARY密钥分配给专用缓冲区,并将LOAD INDEX INTO CACHE分配给它们。

但是当我观察运行中的查询时,mysqld使用大约1%的CPU,平均系统IO挂起队列长度大约为5,而mysqld的平均搜索大小在250 k范围内。此外,几乎所有IO都是从t_x.MYIt_x.MYD读取的mysqld。

我不明白:

  1. 为什么mysqld正在阅读.MYD个文件?

  2. 为什么mysqld没有使用预先加载的t_xt_y索引?

  3. 是否与t_xt_y PRIMARY键超过两列有关?

    编辑:查询解释了:

    | id | select_type | table | type | possible_keys | key     | key_len | ref       | rows     | Extra       |
    +----+-------------+-------+------+---------------+---------+---------+-----------+----------+-------------+
    |  1 | SIMPLE      | t     | ALL  | NULL          | NULL    | NULL    | NULL      | 20980052 |             | 
    |  1 | SIMPLE      | t_x   | ref  | PRIMARY       | PRIMARY | 4       | db.t.t_id |   235849 | Using index | 
    |  1 | SIMPLE      | t_y   | ref  | PRIMARY       | PRIMARY | 4       | db.t.t_id |   207947 | Using where | 
    +----+-------------+-------+------+---------------+---------+---------+-----------+----------+-------------+
    

3 个答案:

答案 0 :(得分:2)

使用不存在 - 这将是最快的 - 在这种情况下比'加入'或使用'不在'要好得多。

SELECT t.* FROM t a
Where not exists (select 1 from t_x b
                  where b.t_id = a.t_id)
or not exists (select 1 from t_y c
                where c.t_id = a.t_id);

答案 1 :(得分:1)

我可以回答您问题的第1部分,如果您发布EXPLAIN的输出,我可能会或可能不会回答第2部分:

为了选择t。*,它需要查看MYD文件 - 只有主键在索引中,才能获取您请求的其他列需要的数据列。

也就是说,您的查询可能很快就会过滤结果,它只是在努力复制您想要的所有数据。

另请注意,您的输出中可能会有重复项 - 如果一行在t_x中没有引用,而在x_y中没有引用,那么t。*将重复3次。鉴于我们认为where子句足够高效,并且花费了大量时间来读取实际数据,这很可能是您的问题的根源。尝试更改为select distinct,看看这是否有助于提高效率

答案 2 :(得分:0)

这可能会更有效率:

SELECT * 
FROM t
WHERE t.id NOT IN (
  SELECT DISTINCT t_id
  FROM t_x
  UNION
  SELECT DISTINCT t_id
  FROM t_y
);
相关问题