0

次の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');
4

2 に答える 2

0

pool.location を使用しているように見えますが、性別にインデックスを追加してみてください。データの国コードへの場所の合理化と、おそらく役立つインデックス作成。

しかし、追加する最初のインデックスは私には野心的に見えます。これは、テストする必要があるレコードの数に深刻な影響を与える可能性があります.

于 2012-06-12T10:34:08.960 に答える
0

クエリを分割することは、正しいアプローチとは思えません。

より良い方法は、既存のクエリからいくつかのレコードをフェッチし、メッセージを送信してからフェッチを続けることです。


あなたのクエリは、別の複合インデックスの恩恵を受ける可能性があります

pool( location, gender, username )

sdexこれにより、新しいインデックスから完全なクエリを実行できるようになります。


本当にクエリを分割したい場合、簡単な方法は

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

のステップで最小から最大までループし、クエリ1000に追加id >= r AND id < r+1000します。

ギャップがある場合、これにより行が返される可能性0がありますが、一度に 1000 行を超える行が返されることはありません。( 、、および多分)poolを含む別の複合インデックスは、このクエリに役立つ可能性があります。idlocationgenderusername

于 2012-06-12T10:41:39.877 に答える