编辑:以下问题有一个错误,可以解释这些发现.我可以删除问题,但这可能对某人仍然有用.错误是,当我认为服务器上正在运行的SELECT t.* FROM t(这很不一样)时,服务器上运行的实际查询是SELECT * FROM t(这很愚蠢).请参阅tobyobrian的答案和对此的评论.
在具有以下架构的情况下,我的查询速度太慢.表t具有以t_id索引的数据行. t通过连接表t_x和t_y与表x和y邻接,每个连接表仅包含JOIN所需的外键:
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中,即任何一个联结表中都未引用的行.
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具有2100万行,而t_x和t_y均具有2500万行.因此,这自然将是一个缓慢的查询.
我正在使用MyISAM,所以我想我会尝试通过预加载t_x和t_y索引来加快速度. t_x.MYI和t_y.MYI的总大小约为1.2 M字节,因此我为它们创建了专用密钥缓冲区,将其PRIMARY密钥分配给了专用缓冲区,并进行了加载索引以进行缓存.
但是,当我观看正在运行的查询时,mysqld使用的CPU约为1%,平均系统IO待处理队列长度约为5,而mysqld的平均查找大小在250 k范围内.而且,几乎所有的IO都是mysqld从t_x.MYI和t_x.MYD中读取.
我不明白:
>为什么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 |
+----+-------------+-------+------+---------------+---------+---------+-----------+----------+-------------+
解决方法:
我可以回答您问题的第一部分,如果您发布EXPLAIN的输出,则我可能无法回答第二部分:
为了选择t.*,它需要在MYD文件中查找-只有主键在索引中,才能获取您请求的数据列,它需要其余的列.
也就是说,您的查询很有可能会非常快速地过滤结果,而这只是在努力复制所需的所有数据.
还要注意,您的输出中可能会有重复项-如果一行在t_x中没有引用,但在x_y中有3个引用,则您将重复3次相同的t.*.鉴于我们认为where子句足够有效,并且花费大量时间读取实际数据,因此很可能是问题的根源.尝试更改以选择不同的内容,看看是否有助于提高效率