2014-02-15 71 views
1

花了幾天時間來分析我們的分佈式應用程序在MySQL數據庫中使用的各種查詢。我們的應用程序可能會在客戶端數據庫服務器上存儲數百萬條記錄,並且查詢可能有所不同,因此索引的設計並不總是清晰或容易。如果查詢速度足夠快,查詢中的一小部分額外開銷就可以接受。MySQL查詢w /索引匹配較少WHERE列比索引匹配所有列的速度更快

我已經設法縮小了幾乎所有我們最常見的查詢都適用的複合索引。我可以清除下列索引中的某些列,但我需要運行測試來確保。

然而,我的問題:某個查詢實際運行更快當使用包含在目前的條件較少的列的索引。

表結構與當前的複合索引:

CREATE TABLE IF NOT EXISTS `prism_data` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `epoch` int(10) unsigned NOT NULL, 
    `action_id` int(10) unsigned NOT NULL, 
    `player_id` int(10) unsigned NOT NULL, 
    `world_id` int(10) unsigned NOT NULL, 
    `x` int(11) NOT NULL, 
    `y` int(11) NOT NULL, 
    `z` int(11) NOT NULL, 
    `block_id` mediumint(5) DEFAULT NULL, 
    `block_subid` mediumint(5) DEFAULT NULL, 
    `old_block_id` mediumint(5) DEFAULT NULL, 
    `old_block_subid` mediumint(5) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `epoch` (`epoch`), 
    KEY `block` (`block_id`,`action_id`,`player_id`), 
    KEY `location` (`world_id`,`x`,`z`,`y`,`epoch`,`action_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

我有我一直在測試8次常見的查詢,他們都表現出50萬條記錄的數據庫上令人難以置信的性能提升。但是,一個查詢不會。

以下查詢中(9.77秒)返回11088行並使用location索引

SELECT SQL_NO_CACHE id, 
     epoch, 
     action, 
     player, 
     world_id, 
     x, 
     y, 
     z 
FROM prism_data 
INNER JOIN prism_players p ON p.player_id = prism_data.player_id 
INNER JOIN prism_actions a ON a.action_id = prism_data.action_id 
WHERE world_id = 
    (SELECT w.world_id 
    FROM prism_worlds w 
    WHERE w.world = 'world') 
    AND (a.action = 'world-edit') 
    AND (prism_data.x BETWEEN -7220 AND -7020) 
    AND (prism_data.y BETWEEN -22 AND 178) 
    AND (prism_data.z BETWEEN -9002 AND -8802) 
    AND prism_data.epoch >= 1392220467; 
+----+-------------+------------+--------+----------------+----------+---------+--------------------------------+--------+------------------------------------+ 
| id | select_type | table  | type | possible_keys | key  | key_len | ref       | rows | Extra        | 
+----+-------------+------------+--------+----------------+----------+---------+--------------------------------+--------+------------------------------------+ 
| 1 | PRIMARY  | a   | ref | PRIMARY,action | action | 77  | const       |  1 | Using where; Using index   | 
| 1 | PRIMARY  | prism_data | ref | epoch,location | location | 4  | const       | 660432 | Using index condition; Using where | 
| 1 | PRIMARY  | p   | eq_ref | PRIMARY  | PRIMARY | 4  | minecraft.prism_data.player_id |  1 | NULL        | 
| 2 | SUBQUERY | w   | ref | world   | world | 767  | const       |  1 | Using where; Using index   | 
+----+-------------+------------+--------+----------------+----------+---------+--------------------------------+--------+------------------------------------+ 

如果刪除了world條件,這將不再匹配location索引而是使用epoch索引。令人驚訝的是,它返回(0.31秒)11088行

9.77秒與0.31秒太大的差別,不容忽視。我不明白爲什麼我在使用location索引的其他查詢中看不到這樣的性能表現,但更重要的是,我不知道我能做些什麼來解決這個問題。

+0

如果你刪除'w.world ='world'',你不會得到一個子查詢返回多行的錯誤嗎? –

+1

你是說當你沒有使用子查詢時,它返回的結果集更快'(SELECT w.world_id FROM prism_worlds w WHERE w.world ='world')'? – AgRizzo

+2

爲什麼你需要在這裏加入 - '選擇w.world_id從prism_worlds w在哪裏w.world ='world''? – Vishal

回答

1

推測「紀元」索引比「位置」索引更具有選擇性。

請注意,MySQL可能會爲每一行運行一次子查詢。即使有索引,這可能會產生相當大的開銷。做3000萬次索引查找可能需要一點時間。

嘗試這樣做的查詢是這樣的:

SELECT SQL_NO_CACHE id, 
     epoch, 
     action, 
     player, 
     world_id, 
     x, 
     y, 
     z 
FROM prism_data 
INNER JOIN prism_players p ON p.player_id = prism_data.player_id 
INNER JOIN prism_actions a ON a.action_id = prism_data.action_id 
CROSS JOIN (SELECT w.world_id FROM prism_worlds w WHERE w.world = 'world') w 
WHERE world_id = w.world_id 
    AND (a.action = 'world-edit') 
    AND (prism_data.x BETWEEN -7220 AND -7020) 
    AND (prism_data.y BETWEEN -22 AND 178) 
    AND (prism_data.z BETWEEN -9002 AND -8802) 
    AND prism_data.epoch >= 1392220467; 

如果沒有顯示改善,那麼問題是指數的選擇性。 MySQL只是做出錯誤的決定,哪個是最好的索引。如果這確實表現出改進,那麼這是因爲子查詢僅在from子句中執行一次。

編輯:

你的位置索引:

KEY `location` (`world_id`,`x`,`z`,`y`,`epoch`,`action_id`) 

您可以將其更改爲:

KEY `location` (`world_id`, action_id `x`, `z`, `y`, `epoch`) 

這使得where過濾使用action_id以及x。 (只有第一個不平等採用直接索引查找。)

或更好,但一個的這些:

KEY `location` (`world_id`, action_id, epoch, `x`, `z`, `y`) 
KEY `location` (`world_id`, epoch, action_id, `x`, `z`, `y`) 
KEY `location` (epoch, `world_id`, action_id, `x`, `z`, `y`) 

的想法是x因此它將被用於where子句條件之前,移動epoch

+0

我會試試這個。只是爲了提供信息價值,世界子查詢是上一個問題中最有效的建議:http://stackoverflow.com/questions/21763950/improving-mysql-index-efficiency-columns-in-multiple-indexes – helion3

+0

此查詢格式顯示無改善不幸的是,9.71秒 – helion3

+0

然後是劃時代索引是更好的索引的問題。嘗試在查詢中添加'use index(epoch)'。 –