MySQL在LEFT JOIN查询中拆分左表以提高性能

时间:2012-06-12 10:08:46

标签: mysql performance join split

我有以下MySQL查询:

SELECT pool.username
FROM pool
LEFT JOIN sent ON pool.username = sent.username
AND sent.campid = 'YA1LGfh9'
WHERE sent.username IS NULL
AND pool.gender = 'f'
AND (`location` = 'united states' OR `location` = 'us' OR `location` = 'usa');

问题是池表包含数百万行,此查询需要12分钟才能完成。我意识到在这个查询中,正在扫描整个左表(池)。池表具有自动递增的id行。

我想将此查询拆分为多个查询,以便不是扫描整个池表,而是一次扫描1000行,在下一个查询中,我会从中断的地方继续查询(1000-2000,2000-3000) )等等使用id列来跟踪。

如何在查询中指定此内容?如果您知道答案,请显示示例。谢谢。

这是我的索引,如果它有帮助:

mysql> show index from main.pool;
| 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         |     9275039 |     NULL | NULL   |      | BTREE      |         |
| pool  |          1 | username |            1 | username    | A         |     9275039 |     NULL | NULL   |      | BTREE      |         |
| pool  |          1 | source   |            1 | source      | A         |           1 |     NULL | NULL   |      | BTREE      |         |
| pool  |          1 | location |            1 | location    | A         |       38168 |     NULL | NULL   |      | BTREE      |         |
| pool  |          1 | pdex     |            1 | gender      | A         |           2 |     NULL | NULL   |      | BTREE      |         |
| pool  |          1 | pdex     |            2 | username    | A         |     9275039 |     NULL | NULL   |      | BTREE      |         |
| pool  |          1 | pdex     |            3 | id          | A         |     9275039 |     NULL | NULL   |      | BTREE      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
8 rows in set (0.00 sec)

mysql> show index from main.sent;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| 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         |         175 |     NULL | NULL   |      | BTREE      |         |
| sent  |          1 | sdex     |            1 | campid      | A         |           7 |     NULL | NULL   |      | BTREE      |         |
| sent  |          1 | sdex     |            2 | username    | A         |         351 |     NULL | NULL   |      | BTREE      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

以下是我的查询说明:

----------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref   | rows    | Extra                                |
+----+-------------+-------+-------+---------------+------+---------+-------+---------+--------------------------------------+
|  1 | SIMPLE      | pool  | ref   | location,pdex | pdex | 5       | const | 6084332 | Using where                          |
|  1 | SIMPLE      | sent  | index | sdex          | sdex | 309     | NULL  |     351 | Using where; Using index; Not exists |
+----+-------------+-------+-------+---------------+------+---------+-------+---------+--------------------------------------+

这是池表的结构:

| pool  | CREATE TABLE `pool` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) CHARACTER SET utf8 NOT NULL,
`source` varchar(10) CHARACTER SET utf8 NOT NULL,
`gender` varchar(1) CHARACTER SET utf8 NOT NULL,
`location` varchar(50) CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (`id`),
KEY `username` (`username`),
KEY `source` (`source`),
KEY `location` (`location`),
KEY `pdex` (`gender`,`username`,`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9327026 DEFAULT CHARSET=latin1 |

这是发送表的结构:

| sent  | CREATE TABLE `sent` (
`primary_key` int(50) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`from` varchar(50) NOT NULL,
`campid` varchar(255) NOT NULL,
`timestamp` int(20) NOT NULL,
PRIMARY KEY (`primary_key`),
KEY `username` (`username`),
KEY `sdex` (`campid`,`username`)
) ENGINE=MyISAM AUTO_INCREMENT=352 DEFAULT CHARSET=latin1 |

这会产生语法错误,但开头的这个WHERE子句就是后面的内容:

SELECT pool.username
FROM pool
WHERE id < 1000
LEFT JOIN sent ON pool.username = sent.username
AND sent.campid = 'YA1LGfh9'
WHERE sent.username IS NULL
AND pool.gender = 'f'
AND (location = 'united states' OR location = 'us' OR location = 'usa');

2 个答案:

答案 0 :(得分:0)

看起来像是在使用pool.location 可以尝试添加性别指数,虽然可能没有太多帮助。 将位置合理化为数据中的国家/地区代码,并对可能有用的索引进行索引。

但是第一个添加的索引看起来像是对我而言,这可能会严重削弱它必须测试的记录数量。

答案 1 :(得分:0)

拆分您的查询听起来不是正确的做法。

更好的方法是从现有查询中获取一些记录,发送消息然后继续提取。


您的查询可能会受益于

上的其他复合索引
pool( location, gender, username )

这应该允许从sdex和您的新索引运行完整的查询。


如果您真的想要拆分查询,可以采用一种简单的方法

SELECT MIN(id), MAX(id) FROM pool

然后以1000的步长从最小值循环到最大值,并将id >= r AND id < r+1000添加到您的查询中。

如果您有间隙,这可能会返回0行,但它一次不会返回超过1000行。 pool上的不同复合索引(包括idlocationgenderusername)可能有助于此查询。