我试图在MySQL 8.0.28中使用覆盖索引(带函数索引)来加速一个大型InnoDB表上的分组查询.

创建覆盖索引:

ALTER TABLE `sp_files`
ADD INDEX `ix_site_header_to_cleaned_text2` (`sp_site_foreign_key`, (length(`cleaned_text`)));

只使用覆盖索引中的字段,我希望MySQL使用索引而不是底层表—所以我希望EXPLAIN输出的Extra列显示Using index:

EXPLAIN
SELECT sp_site_foreign_key,
    count(case when length(`cleaned_text`) > 100 then 1 else NULL END) as num1,
    count(1) as num2
FROM sp_files USE INDEX (`ix_site_header_to_cleaned_text2`)
GROUP by sp_site_foreign_key; 
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE sp_files index ix_site_header_to_cleaned_text2 ix_site_header_to_cleaned_text2 208 105249 100

你知道为什么它不使用索引吗? 查询的所有字段都包含在ix_site_header_to_cleaned_text2索引中,所以我希望explainExtra列中显示Using index.

推荐答案

它正在使用索引(因此EXPLAIN报告中显示的type: index连接类型),但它不能获得覆盖索引优化.

MySQL对函数索引的支持有一些限制,除非你仔细阅读手册,否则你可能不会注意到:

https://dev.mysql.com/doc/refman/8.0/en/generated-column-index-optimizations.html

优化器可以使用生成列上的索引来生成执行计划,即使查询中没有按名称直接引用这些列.如果WHEREORDER BYGROUP BY子句引用的表达式与某个索引生成的列的定义相匹配,则会发生这种情况.

这意味着它应该允许您使用与索引的函数部分相同的表达式(在本例中为length(cleaned_text)),并自动从索引中获取该值,因此不必重新计算它.

但是,如果在查询的其他子句中使用该表达式,比如在case中的select—list或in the JOIN clause,这就不起作用了.它没有在手册描述中指定的条款中实现这种优化.

也许它们会在MySQL的future 版本中进一步改进对函数索引的支持.

同时,只有当显式定义了一个虚拟列,并在索引和查询中引用该列时,才可以获得覆盖索引.

演示,在MySQL 8.0.36上测试:

CREATE TABLE sp_files (
  id SERIAL PRIMARY KEY,
  sp_site_foreign_key INT,
  cleaned_text TEXT,
  other INT,
  length_cleaned_text INT AS (length(cleaned_text)) 
);

ALTER TABLE `sp_files`
ADD INDEX `ix_site_header_to_cleaned_text2` (`sp_site_foreign_key`, length_cleaned_text);

EXPLAIN
SELECT sp_site_foreign_key,
    count(case when length_cleaned_text > 100 then 1 else NULL END) as num1,
    count(1) as num2
FROM sp_files USE INDEX (`ix_site_header_to_cleaned_text2`)
GROUP by sp_site_foreign_key\G

输出:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: sp_files
   partitions: NULL
         type: index
possible_keys: ix_site_header_to_cleaned_text2
          key: ix_site_header_to_cleaned_text2
      key_len: 10
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using index

Mysql相关问答推荐

如何在逗号分隔字符串值中使用LEFT函数

MySQL中如何对字符串进行算术运算?

使用 awk 重写 maxscale 过滤器

如何将多个字符串插入变量

Travis 构建失败并出现错误 LOAD DATA LOCAL INFILE 文件请求因访问限制而被拒绝

如何解码存储在带有type*:前缀的base64中的数据?

SQL: Select TEXT 字段的子字符串比整个值快

go&mysql&docker 拒绝连接

sequelize 中最好的更新方法是什么

有人可以帮我优化这个查询吗?

在 MySQL 中使用三个表进行计算

如何使用nodejs从mysql数据库中获取最新的10条记录?

使用 MySQL 流式传输大型结果集

MySQL INSERT IF(自定义 if 语句)

SQL - 如何找到列中的最高数字?

在另一个 where 语句(子查询?)中使用一个 sql 查询的结果

将 MySQL 数据库置于版本控制之下?

MySQL:ALTER IGNORE TABLE 给出违反完整性约束

MySQL - 重复表

在 PHP 中运行 MySQL *.sql 文件