MySQL 5.7中INNODB表计数查询的MySQL忽略索引

MySQL 5.7中INNODB表计数查询的MySQL忽略索引,mysql,hibernate,indexing,mysql-5.7,Mysql,Hibernate,Indexing,Mysql 5.7,我们有一个运行MySQL 5.1的旧数据库。我们现在想将其迁移到MySQL 5.7,但一些正常工作的查询突然变得非常慢,慢了60倍或更多 有问题的INNODB表事件在其他列中有一个COMPANY_ID外键,指向COMPANY表,以及DATETIME类型的EVENT_DATETIME。公司ID、事件日期时间上有一个索引,为了测试,我添加了一个事件日期时间、公司ID。 目前,基本上所有活动都有公司ID 1,但这将发生变化 我们有一个计数查询来查询去年的事件数: select count(distin

我们有一个运行MySQL 5.1的旧数据库。我们现在想将其迁移到MySQL 5.7,但一些正常工作的查询突然变得非常慢,慢了60倍或更多

有问题的INNODB表事件在其他列中有一个COMPANY_ID外键,指向COMPANY表,以及DATETIME类型的EVENT_DATETIME。公司ID、事件日期时间上有一个索引,为了测试,我添加了一个事件日期时间、公司ID。 目前,基本上所有活动都有公司ID 1,但这将发生变化

我们有一个计数查询来查询去年的事件数:

select count(distinct this_.EVENT_ID) as y0_ from EVENT this_
       where this_.EVENT_DATETIME>='2018-10-22 00:00:00'
         and this_.EVENT_DATETIME<='2019-11-21 00:00:00'
         and this_.COMPANY_ID = 1;
结果是大约1000000行,过去需要1.5秒,现在需要100秒。虽然MySQL 5.1上的查询使用公司ID和事件日期时间的索引,但MySQL 5.7上的索引被忽略。看起来,如果MySQL发现它必须解析太多的行,那么它会在索引上放弃,即使这会有所帮助。如果我将窗口缩小到10个月,MySQL 5.7会再次使用索引

因此,在MySQL 5.1上,使用了索引COMPANY\u ID、EVENT\u DATETIME 在MySQL上,它只对公司ID使用外键索引

如果我在没有公司ID上的where的情况下运行查询

select count(distinct this_.EVENT_ID) as y0_ from EVENT this_ 
       where this_.EVENT_DATETIME>='2018-10-22 00:00:00'
         and this_.EVENT_DATETIME<='2019-11-21 00:00:00';
查询速度要快得多

有没有办法强迫MySQL 5.7使用某个索引

如果我将查询重写为:

select count(distinct this_.EVENT_ID) as y0_ from EVENT this_
     where this_.EVENT_DATETIME>='2018-10-22 00:00:00'
       and this_.EVENT_DATETIME<='2019-11-21 00:00:00'
     GROUP BY COMPANY_ID HAVING COMPANY_ID = 1;
它又回到了大约1到1.5秒。问题是我们可能有不止一个这样的查询,而这些查询是由Hibernate标准生成的,这些标准不支持这样的查询,因此我的解决方案在现实生活中无法工作

更新: MySQL 5.7 在40秒内解释12个月查询1050757行

{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "673838.60"
    },
    "table": {
      "table_name": "this_",
      "access_type": "ref",
      "possible_keys": [
      "PRIMARY",
      "FK_EVENT_COMPANY",
      "IX_REFERENCE",
      "IX_DATE_TIME",
      "EVENT_DATETIME",
      "IDX_CE_COMPANY_TYPE",
      "IDX_CE_COMPANY_DATE",
      "IDX_CE_DATE_COMPANY"
      ],
      "key": "FK_EVENT_COMPANY",
      "used_key_parts": [
        "COMPANY_ID"
      ],
      "key_length": "4",
      "ref": [
        "const"
      ],
      "rows_examined_per_scan": 2698153,
      "rows_produced_per_join": 1135826,
      "filtered": "42.10",
      "cost_info": {
        "read_cost": "134208.00",
        "eval_cost": "227165.40",
        "prefix_cost": "673838.60",
        "data_read_per_join": "1G"
      },
      "used_columns": [
        "EVENT_ID",
        "COMPANY_ID",
        "EVENT_DATETIME"
      ],
      "attached_condition": "((`test`.`this_`.`EVENT_DATETIME` >= '2018-10-22 00:00:00') and (`test`.`this_`.`EVENT_DATETIME` <= '2019-11-21 00:00:00'))"
    }
  }
}
解释10个月的查询

   {
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "634047.16"
    },
    "table": {
      "table_name": "this_",
      "access_type": "range",
      "possible_keys": [
        "PRIMARY",
        "FK_EVENT_COMPANY",
        "IX_REFERENCE",
        "IX_DATE_TIME",
        "EVENT_DATETIME",
        "IDX_CE_COMPANY_TYPE",
        "IDX_CE_COMPANY_DATE",
        "IDX_CE_DATE_COMPANY"
      ],
      "key": "IDX_CE_DATE_COMPANY",
      "used_key_parts": [
        "EVENT_DATETIME"
      ],
      "key_length": "9",
      "rows_examined_per_scan": 1578860,
      "rows_produced_per_join": 789430,
      "filtered": "50.00",
      "using_index": true,
      "cost_info": {
        "read_cost": "476161.16",
        "eval_cost": "157886.00",
        "prefix_cost": "634047.16",
        "data_read_per_join": "1G"
      },
      "used_columns": [
        "EVENT_ID",
        "COMPANY_ID",
        "EVENT_DATETIME"
      ],
      "attached_condition": "((`test`.`this_`.`COMPANY_ID` = 1) and (`test`.`this_`.`EVENT_DATETIME` >= '2019-01-22 00:00:00') and (`test`.`this_`.`EVENT_DATETIME` <= '2019-11-21 00:00:00'))"
    }
  }
}
有趣的是,第一个12个月的慢速查询在附加的\u条件中不显示公司\u ID,而第二个10个月的查询附加的\u条件检查公司\u ID

分析表的建议并没有改变它看起来的任何东西

更新2: 解释MySQL 5.1不支持JSON格式需要1.3秒

1 SIMPLE this_ range FK_EVENT_COMPANY,IX_DATE_TIME,EVENT_DATETIME,IDX_CE_COMPANY_TYPE,IDX_CE_COMPANY_DATE IDX_CE_COMPANY_DATE 16 NULL 2018704 Using where; Using index
查询计划器可能根据可用的统计信息做出错误的决策。您可以尝试运行ANALYZE来重建统计数据,并为计划人员提供更好的数据。请注意,ANALYZE在快速运行时会阻塞表

更新

在阅读MySQL文档时,我发现以下段落:

在MySQL 5.7.18之前,InnoDB通过扫描聚集索引处理SELECT COUNT*语句。从MySQL 5.7.18开始,InnoDB通过遍历最小的可用辅助索引来处理SELECT COUNT*语句,除非索引或优化器提示指示优化器使用不同的索引。如果不存在次索引,则扫描聚集索引

参考:

这意味着计数行为在您使用的版本上发生了完全的变化。这或许可以解释这种差异

最佳指标为

INDEX(COMPANY_ID, EVENT_DATETIME, EVENT_ID)  -- in this order
我觉得您的日期范围是1年+一天+1秒。这是故意的吗


如果EVENT_ID是主键,请提供SHOW CREATE TABLE,那么COUNTDISTINCT EVENT_ID可以是COUNT*。但查询似乎是由hibernate生成的,不确定是否可以添加索引提示。在这两个版本上运行explain并将结果发布在此处。解释将显示查询计划,您将能够比较它们。我指的是MySQL 5.1和5.7中的解释。。因此,我们可以比较两个版本的查询计划器的决定。请提供SHOW CREATE TABLE,而不是试图用散文来描述它。5.7的哪个版本?谢谢您的建议。我尝试过分析表,但这似乎没有改变任何东西。5.7.18 changelog:SELECT COUNT*性能在某些情况下会下降,因为MySQL 5.7.2中引入了一个修改,导致InnoDB通过遍历聚集索引而不是较小的二级索引来计算行数。修改被恢复。错误23046302,错误80580