web-dev-qa-db-ja.com

ORDER BY LIMIT 1がMAXよりもはるかに高速なのはなぜですか?

これが私の2つのクエリです。

select sts_in 
from sta_session 
where sts_user_id=2006 
AND sts_sessid!='0jitkt80gg3avere03tqk4lhi6' 
order by sts_in desc limit 1;

そして

select max(sts_in) 
from sta_session 
where sts_user_id=2006 
AND sts_sessid!='0jitkt80gg3avere03tqk4lhi6';

参考までに、表は次のようになります。

CREATE TABLE `sta_session` (
    `sta_session_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `sts_sessid` varchar(255) NOT NULL COMMENT 'PHP session ID',
    `sts_user_id` int(10) unsigned NOT NULL,
    `sts_in` bigint(20) NOT NULL,
    `sts_out` bigint(20) NOT NULL,
    `sts_ip` varchar(255) NOT NULL,
    `sts_browser_id` int(10) unsigned NOT NULL,
    PRIMARY KEY (`sta_session_id`),
    KEY `sts_sessid` (`sts_sessid`),
    KEY `sts_user_id` (`sts_user_id`),
    KEY `sts_ip` (`sts_ip`),
    KEY `fk_sta_browser_id` (`sts_browser_id`),
    KEY `idx_last_login` (`sts_user_id`,`sts_in`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

両方のクエリでExplainを実行すると、最初のクエリ(order by付き)は、このクエリのために作成した特別なインデックスidx_last_loginを使用しますが、もう1つはsts_user_idを使用します。

MAXクエリnotidx_last_loginを利用できますか?何故なの?

seそのインデックスを強制できるので知っていますが、sts_inの部分を無視してユーザーIDで検索しているだけだと思います。


EXPLAIN、読みやすいJSON形式。

explain select sts_in from sta_session where sts_user_id=2006 AND sts_sessid!='0jitkt80gg3avere03tqk4lhi6' order by sts_in desc limit 1;

[{
  "id": 1,
  "select_type": "SIMPLE",
  "table": "sta_session",
  "type": "ref",
  "possible_keys": "sts_sessid,sts_user_id,idx_last_login",
  "key": "idx_last_login",
  "key_len": "4",
  "ref": "const",
  "rows": 723106,
  "Extra": "Using where"
 }
]


explain select max(sts_in) from sta_session where sts_user_id=2006 AND sts_sessid!='0jitkt80gg3avere03tqk4lhi6';

[{
  "id": 1,
  "select_type": "SIMPLE",
  "table": "sta_session",
  "type": "ref",
  "possible_keys": "sts_sessid,sts_user_id,idx_last_login",
  "key": "sts_user_id",
  "key_len": "4",
  "ref": "const",
  "rows": 723107,
  "Extra": "Using where"
 }
]
7
mpen

それは私には非常に理にかなっています。

MySQL Query Optimizerは、WHERE、_GROUP BY_、および_ORDER BY_句を調べます。

最初のクエリを見てください

_select sts_in 
from sta_session 
where sts_user_id=2006 
AND sts_sessid!='0jitkt80gg3avere03tqk4lhi6' 
order by sts_in desc limit 1;
_

_sta_session_のどのインデックスに、WHERE、_GROUP BY_、および_ORDER BY_句で言及されている列が最も多いですか? _idx_last_login_

MySQLはどのようにしてMAX値を見つけますか?

  • 2006年の_sts_user_id_インデックスの_idx_last_login_をトラバース
  • _sts_in_ 2006の_sts_user_id_の最後のインデックスエントリに移動
  • _sts_sessid!='0jitkt80gg3avere03tqk4lhi6'_まで後方にスクロールします
  • 結果を1行に制限

2番目のクエリを見てください。

_select max(sts_in) 
from sta_session 
where sts_user_id=2006 
AND sts_sessid!='0jitkt80gg3avere03tqk4lhi6';
_

_sta_session_のどのインデックスに、WHERE、_GROUP BY_、および_ORDER BY_句で言及されている列が最も多いですか? _idx_last_login_ではなく、_sts_user_id_です。

MySQLはどのようにしてMAX値を見つけますか?

  • 2006年のフルインデックスレンジスキャン_sts_user_id_
  • テーブルから_sts_in_を集計し、_sts_sessid_のすべての値を比較します<> _'0jitkt80gg3avere03tqk4lhi6'_

可能な回避策

_sts_user_id_と_idx_last_login_は重複したインデックスであるため(最初の列が同じであるため)、次を実行する必要があります。

_ALTER TABLE sta_session DROP INDEX sts_user_id;
_

_idx_last_login_が選択され、_sts_in_を集約する可能性があります。ただし、テーブルを横断するトラバースがまだいくつかあります。

インデックス作成を積極的に行いたい場合は、これを作成してください

_ALTER TABLE sta_session ADD INDEX everything_and_the_kitchen_sink_index
(sts_user_id,sts_in,sts_sessid);
_

すべての列(WHERE、_ORDER BY_、MAX())がインデックス内にあるため、このインデックスが選択され、テーブルに触れる必要がない場合があります。

試してみる !!!

4
RolandoMySQLDBA