2012-06-11 74 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