2012-06-11 46 views
3

我的查询的对象是从表a中获取所有行,其中性别= f和用户名不存在于表b中campid = xxxx。这里是我成功使用查询:MySQL加速左外部联接/检查空查询

SELECT `id` 
FROM pool 
    LEFT JOIN sent 
    ON pool.username = sent.username 
    AND sent.campid = 'YA1LGfh9' 
WHERE sent.username IS NULL 
    AND pool.gender = 'f' 

的问题是,查询需要在9分钟内完成,池表包含超过1000万行,并且送出表最终要长得比更大那。我为许多列创建了索引,包括用户名和性别。但是,MySQL拒绝为此查询使用我的任何索引。我甚至尝试使用FORCE INDEX。下面是从游泳池我的指标,并说明我的查询的输出:

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| pool |   0 | PRIMARY |   1 | id   | A   |  9326880 |  NULL | NULL |  | BTREE  |   | 
| pool |   1 | username |   1 | username | A   |  9326880 |  NULL | NULL |  | BTREE  |   | 
| pool |   1 | source |   1 | source  | A   |   6 |  NULL | NULL |  | BTREE  |   | 
| pool |   1 | gender |   1 | gender  | A   |   9 |  NULL | NULL |  | BTREE  |   | 
| pool |   1 | location |   1 | location | A   |  59030 |  NULL | NULL |  | BTREE  |   | 
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
6 rows in set (0.00 sec) 

mysql> explain SELECT `id` FROM pool FORCE INDEX (username) LEFT JOIN sent ON pool.username = sent.username AND sent.campid = 'YA1LGfh9' WHERE sent.username IS NULL AND pool.gender = 'f'; 
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra     | 
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------------------+ 
| 1 | SIMPLE  | pool | ALL | NULL   | NULL | NULL | NULL | 9326881 | Using where    | 
| 1 | SIMPLE  | sent | ALL | NULL   | NULL | NULL | NULL |  351 | Using where; Not exists | 
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------------------+ 
2 rows in set (0.00 sec) 

也,这里是我的发送表索引:

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| sent |   0 | PRIMARY |   1 | primary_key | A   |   351 |  NULL | NULL |  | BTREE  |   | 
| sent |   1 | username |   1 | username | A   |   351 |  NULL | NULL |  | BTREE  |   | 
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
2 rows in set (0.00 sec) 

你可以看到,在不使用任何索引所以我的查询需要很长时间。如果任何人有解决方案,涉及重新查询,请给我一个如何使用我的数据结构的例子,以便我不会有任何混淆如何实施和测试。谢谢。

回答

4

首先,您的原始查询在您放置所有东西时都是正确的......包括营地。通过使用从池到发送的LEFT JOIN,然后像前面所建议的那样,将所需的等式(如“CAMP”)拉入WHERE子句中,最终将其转换为INNER JOIN,从而需要双方进入。保持原样。

您已经有发送表上的用户名称的索引,但我会执行以下操作。

在(CampID,UserName)上的“发送”表上建立索引作为组合(即:多键)索引。这样,左连接将针对两个条目进行优化。

在您的“池”表上,尝试3个字段(性别,用户名,ID)的复合索引。

通过这样做,您可以利用不必经历包含您的1000万条记录的所有实际数据页面。由于索引HAS是用于比较的列,因此它不必查找实际记录并查看列,它可以直接使用索引的列。

此外,对于微笑,我添加了关键字“STRAIGHT_JOIN”,它告诉MySQL查询完全符合我的要求,并且不要为我想。很多次,我发现这可以显着提高查询性能......很少有人得到反馈意见,但它没有帮助。

SELECT STRAIGHT_JOIN 
     p.id 
    FROM 
     pool p 
     LEFT JOIN sent s 
      ON s.campid = 'YA1LGfh9' 
      AND p.username = s.username 
    WHERE 
      p.gender = 'f' 
     AND s.username IS NULL 

所有这一切说,你仍然会被返回多少记录了10+万元......如果池中有10+万元,单阵营只有5000元。你仍然会返回几乎整个集合。

+1

我更喜欢'(性别,用户名,id)' –

+0

@ypercube,好点...通过保持用户名位于第二位置将保持该索引不会反弹到发送的表,这也将以适当的顺序。我会改变它。 – DRapp

+0

好的。我已经设置了一切符合你的规范(我认为),但我仍然有性能问题。事实上,它现在所花费的时间比我最初使用索引时的查询时间要长。这是我所做的:http://pastebin.com/BhyPPVqa查询花了将近13分钟完成。也许我做错了什么? – xendi