MySQL 上理论上相等查询的不同速度

Different speed of theoretically equal queries on MySQL

当 运行 在两个不同的列上 date_fromdate_to.

时,我发现我的一个 MySQL 查询存在奇怪的速度问题

table结构如下:

create table if not exists table1 (
    id unsigned int,
    field2 int,
    field3 varchar(32),
    date_from date not null,
    date_to date not null,
    field6 text
);

create unique index idx_uniq_table1 on table1 (id, field2, field3, date_from);
create index idx_table1_id on table1 (id);
create index idx_table1_field2 on table1 (field2);
create index idx_table1_field3 on table1 (field3);
create index idx_table1_date_from on table1 (date_from);
create index idx_table1_date_to on table1 (date_to);

当我运行这个查询使用date_from时,执行时间是1.487秒:

select field3, min(date_from) from table1 group by field3;

当我 运行 这个其他查询使用 date_to 时,执行时间是 13.804 秒,几乎慢了 10 倍:

select field3, max(date_to) from table1 group by field3;

两列都是NOT NULL,所以没有空值。

table 有 ~7M 行。

我在这两列之间看到的唯一区别是 date_from 出现在唯一索引中,但据我所知,如果不按所有四列进行过滤,那应该不会有什么不同索引。

我错过了什么吗?

这是date_from列的解释:

{
    "query_block": {
        "select_id": 1,
        "cost_info": {
        "query_cost": "889148.90"
        },
        "grouping_operation": {
            "using_filesort": false,
            "table": {
                "table_name": "table1",
                "access_type": "index",
                "possible_keys": [
                    "idx_uniq_table1",
                    "idx_table1_id",
                    "idx_table1_field2",
                    "idx_table1_field3",
                    "idx_table1_date_from",
                    "idx_table1_date_to"
                ],
                "key": "idx_table1_field3",
                "used_key_parts": [
                    "field3"
                ],
                "key_length": "130",
                "rows_examined_per_scan": 5952609,
                "rows_produced_per_join": 5952609,
                "filtered": "100.00",
                "using_index": true,
                "cost_info": {
                    "read_cost": "293888.00",
                    "eval_cost": "595260.90",
                    "prefix_cost": "889148.90",
                    "data_read_per_join": "908M"
                },
                "used_columns": [
                    "id",
                    "field2",
                    "field3",
                    "date_from"
                ]
            }
        }
    }
}

这是date_to列的解释:

{
    "query_block": {
        "select_id": 1,
        "cost_info": {
            "query_cost": "889148.90"
        },
        "grouping_operation": {
            "using_filesort": false,
            "table": {
                "table_name": "table1",
                "access_type": "index",
                "possible_keys": [
                    "idx_uniq_table1",
                    "idx_table1_id",
                    "idx_table1_field2",
                    "idx_table1_field3",
                    "idx_table1_date_from",
                    "idx_table1_date_to"
                ],
                "key": "idx_table1_field3",
                "used_key_parts": [
                    "field3"
                ],
                "key_length": "130",
                "rows_examined_per_scan": 5952609,
                "rows_produced_per_join": 5952609,
                "filtered": "100.00",
                "cost_info": {
                    "read_cost": "293888.00",
                    "eval_cost": "595260.90",
                    "prefix_cost": "889148.90",
                    "data_read_per_join": "908M"
                },
                "used_columns": [
                    "id",
                    "field2",
                    "field3",
                    "date_from",
                    "date_to"
                ]
            }
        }
    }
}

我看到的唯一区别是 used_columns,最后,其中一个包含 date_to 而另一个不包含。

调皮。没有PRIMARY KEY.

由于“已使用的列”似乎与查询不符,我不想尝试解释时间差异。

field3 上的索引替换为以下两个:

INDEX(field3, date_from)
INDEX(field3, date_to)

这些将加快您的两次选择。

除了 Rick 根据您的标准回答的关于正确索引的问题之外...速度差异的原因是同时具有 field3 和 date_from 的索引,引擎能够使用实际索引中的数据,而不必转到包含整个记录的原始数据页。原来只有date_to的索引还是要到每条原始数据记录中去获取field3,比较费时间。

这就是您可以利用覆盖索引的原因。拥有您正在寻找的数据的每个组成部分以优化查询。并不是说您想要一个包含 20 列的索引,但在这种情况下,过滤的通用标准正是您这样做的原因。