у нас есть одна старая база данных под управлением MySQL 5.1. Теперь мы хотим перенести его в MySQL 5.7, но некоторые запросы, которые работали нормально, внезапно стали очень очень медленными (в 60 и более раз медленнее).
В рассматриваемой таблице INNODB (EVENT) среди других столбцов естьCOMPANY_ID (внешний ключ к таблице COMPANY) и EVENT_DATETIME типа DATETIME. Существует индекс COMPANY_ID, EVENT_DATETIME, и для тестирования я добавил один EVENT_DATETIME, COMPANY_ID. В настоящее время в основном все СОБЫТИЯ имеют COMPANY_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;
Результат - около 1 000 000 строки раньше занимало около 1,5 секунд, теперь это занимает до 100 секунд. В то время как запрос в MySQL 5.1 использует индекс для COMPANY_ID и EVENT_DATETIME, этот индекс игнорируется в MySQL 5.7. Кажется, что если MySQL видит, что ему нужно проанализировать слишком много строк, он отказывается от индекса, даже если это поможет. Если я уменьшу окно, например, до 10 месяцев, MySQL 5.7 снова использует индекс.
Так что в MySQL 5.1 индекс COMPANY_ID, EVENT_DATETIME используется в MySQL, он использует только индекс внешнего ключа для COMPANY_ID.
Если я выполняю запрос без указания where на COMPANY_ID
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 Criterias, которые не поддерживают HAVING, поэтому мой обходной путь не будет работать в реальной жизни.
Обновление: MySQL 5.7 Объяснение в течение 12 месяцевзапрос (1050757 строк за 40 секунд)
{
"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-месячный (медленный) запрос не показывает COMPANY_ID в attach_condition, покав течение второго 10-месячного запроса attach_condition имеет проверку COMPANY_ID.
ANALYZE TABLE, как было предложено, ничего не изменило, кажется,
Обновление 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