MySQL 上理论上相等查询的不同速度
Different speed of theoretically equal queries on MySQL
当 运行 在两个不同的列上 date_from
与 date_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 列的索引,但在这种情况下,过滤的通用标准正是您这样做的原因。
当 运行 在两个不同的列上 date_from
与 date_to
.
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 列的索引,但在这种情况下,过滤的通用标准正是您这样做的原因。