编辑:以下问题中的错误解释了观察结果。我可以删除这个问题,但这可能对某人有用。错误的是,当我认为它正在运行SELECT * FROM t
时,服务器上运行的实际查询是SELECT t.* FROM t
(这很愚蠢)(这会产生重大影响)。请参阅tobyobrian的回答及其评论。
在具有架构的情况下,我的查询速度太慢,如下所示。表t
包含由t_id
索引的数据行。 t
通过联结表x
和y
与表t_x
和t_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_x
和t_y
都有大约25 M行。所以这自然会是一个缓慢的查询。
我正在使用MyISAM所以我想我会尝试通过预加载t_x
和t_y
索引加快速度。 t_x.MYI
和t_y.MYI
的总大小约为1.2 M字节,所以我为它们创建了一个专用的密钥缓冲区,将它们的PRIMARY密钥分配给专用缓冲区,并将LOAD INDEX INTO CACHE分配给它们。
但是当我观察运行中的查询时,mysqld使用大约1%的CPU,平均系统IO挂起队列长度大约为5,而mysqld的平均搜索大小在250 k范围内。此外,几乎所有IO都是从t_x.MYI
和t_x.MYD
读取的mysqld。
我不明白:
为什么mysqld正在阅读.MYD
个文件?
为什么mysqld没有使用预先加载的t_x
和t_y
索引?
是否与t_x
和t_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 |
+----+-------------+-------+------+---------------+---------+---------+-----------+----------+-------------+
答案 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
);