为什么 ORDER BY LIMIT 1 比 MAX 快这么多?

mpe*_*pen 10 mysql innodb performance aggregate order-by

这是我的两个查询:

select sts_in 
from sta_session 
where sts_user_id=2006 
AND sts_sessid!='0jitkt80gg3avere03tqk4lhi6' 
order by sts_in desc limit 1;
Run Code Online (Sandbox Code Playgroud)

select max(sts_in) 
from sta_session 
where sts_user_id=2006 
AND sts_sessid!='0jitkt80gg3avere03tqk4lhi6';
Run Code Online (Sandbox Code Playgroud)

作为参考,该表如下所示:

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;
Run Code Online (Sandbox Code Playgroud)

如果我对两个查询都运行解释,第一个(按顺序)使用我专门为此查询创建的特殊索引idx_last_login,但另一个使用sts_user_id.

MAX 查询不能利用idx_last_login吗?为什么不?

我知道它可以使用该索引,因为我可以强制使用它,但我怀疑它忽略了它的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"
 }
]
Run Code Online (Sandbox Code Playgroud)

Rol*_*DBA 6

这对我来说很有意义。

MySQL 查询优化器查看WHERE,GROUP BYORDER BY子句。

看第一个查询

select sts_in 
from sta_session 
where sts_user_id=2006 
AND sts_sessid!='0jitkt80gg3avere03tqk4lhi6' 
order by sts_in desc limit 1;
Run Code Online (Sandbox Code Playgroud)

这指数sta_session在提到最栏WHEREGROUP BYORDER BY条款?idx_last_login

MySQL如何定位MAX值?

  • 遍历sts_user_ididx_last_login指数2006年
  • 转到最后一个索引项的sts_insts_user_id2006年
  • 向后滚动直到 sts_sessid!='0jitkt80gg3avere03tqk4lhi6'
  • 将结果限制为 1 行

看第二个查询

select max(sts_in) 
from sta_session 
where sts_user_id=2006 
AND sts_sessid!='0jitkt80gg3avere03tqk4lhi6';
Run Code Online (Sandbox Code Playgroud)

这指数sta_session在提到最栏WHEREGROUP BYORDER BY条款?不是idx_last_login,但是sts_user_id

MySQL如何定位MAX值?

  • sts_user_id2006 年全索引范围扫描
  • sts_in从表中聚合,比较sts_sessid<>的所有值'0jitkt80gg3avere03tqk4lhi6'

可能的解决方法

由于sts_user_ididx_last_login是重复索引(因为它们具有相同的第一列),您应该运行

ALTER TABLE sta_session DROP INDEX sts_user_id;
Run Code Online (Sandbox Code Playgroud)

idx_last_login被选择和聚合的可能性sts_in。但是,仍然有一些遍历表。

如果你真的想积极使用索引,创建这个

ALTER TABLE sta_session ADD INDEX everything_and_the_kitchen_sink_index
(sts_user_id,sts_in,sts_sessid);
Run Code Online (Sandbox Code Playgroud)

此索引可能会被选中并且永远不必接触表,因为所有列 ( WHERE, ORDER BY, MAX()) 都在索引中。

试一试 !!!