MySQL - простое добавление ORDER BY в индексированное поле добавляет 5 минут всего для 52 записей. Когда начать? - PullRequest
1 голос
/ 05 мая 2020

РЕДАКТИРОВАТЬ 2: теперь, когда мы оптимизировали базу данных и сузили ее до MySQL - Почему phpMyAdmin очень медленный с этим запросом, который очень быстр в php / mysqli?

EDIT 1: есть два решения, которые нам помогли. Один на уровне базы данных (конфигурации) и один на уровне запроса. Я, конечно, мог бы принять только один как лучший ответ, но если у вас есть похожие проблемы, посмотрите на оба.

У нас есть база данных, которая отлично работает в течение многих лет. Однако прямо сейчас у нас есть проблема, которую я не понимаю. Это проблема конфигурации mysql / InnoDB? И в настоящее время у нас нет никого для обслуживания системы (я программист).

Табель TitelDaggegevens имеет размер несколько гигов, около 12 000 000 записей, так что ничего экстраординарного.

Если мы:

SELECT * 
  FROM TitelDaggegevens 
 WHERE fondskosten IS NULL 
   AND (datum BETWEEN 20200401 AND 20200430)

он работает нормально, в течение нескольких десятых секунды.

Результат: 52 записи.

Также, если мы добавим ORDER BY datum или сделаем заказ по любому другому неиндексированному полю: все хорошо, та же скорость.

Однако, если я добавлю ORDER BY id (id является первичным ключом), внезапно запрос займет 15 секунд для тех же 52 записей.

И когда я ORDER BY другое индексированное поле, время запроса увеличивается до 4-6 минут . Для заказа 52 записей. В индексированном поле.

Я не знаю понятия не имею , что происходит. EXPLAIN мне не помогает. Я оптимизировал / воссоздал таблицу, проверил ее и перезапустил сервер. Все без толку. Я не являюсь экспертом в настройке MySQL или InnoDB, поэтому понятия не имею, с чего начать поиск.

Я просто надеюсь, что, возможно, кто-то поймет это и сможет указать мне правильное направление.

SHOW TABLE STATUS WHERE Name = 'TitelDaggegevens' Дает мне:

SHOW TABLE STATUS WHERE Name = 'TitelDaggegevens'

Я знаю, что это очень расплывчатая проблема, но я не могу определить ее более конкретно. Я включил ведение журнала для медленных запросов, но таблица slow_log остается пустой. Я потерялся.

Спасибо за любые идеи, где искать.

Это может помочь кому-то, кто что-то знает об этом, но не мне, советнику phpmyadmins:

Advisor

В комментариях и реакции были запрошены выходы EXPLAIN:

1) Без ORDER BY и с ORDER BY datum (который находится в WHERE и имеет индекс):

Without orderby

2) С ORDER BY плюс любое поле, кроме datum (индексированное или нет, поэтому то же самое для быстрых и медленных запросов).

With order by with indexed fields

Структура таблицы:

CREATE TABLE `TitelDaggegevens` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `isbn` decimal(13,0) NOT NULL,
 `datum` date NOT NULL,
 `volgendeDatum` date DEFAULT NULL,
 `prijs` decimal(8,2) DEFAULT NULL,
 `prijsExclLaag` decimal(8,2) DEFAULT NULL,
 `prijsExclHoog` decimal(8,2) DEFAULT NULL,
 `stadiumDienstverlening` char(2) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
 `stadiumLevenscyclus` char(1) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
 `gewicht` double(7,3) DEFAULT NULL,
 `volume` double(7,3) DEFAULT NULL,
 `24uurs` tinyint(1) DEFAULT NULL,
 `UitgeverCode` varchar(4) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
 `imprintId` int(11) DEFAULT NULL,
 `distributievormId` tinyint(4) DEFAULT NULL,
 `boeksoort` char(1) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
 `publishingStatus` tinyint(4) DEFAULT NULL,
 `productAvailability` tinyint(4) DEFAULT NULL,
 `voorraadAlles` mediumint(8) unsigned DEFAULT NULL,
 `voorraadBeschikbaar` mediumint(8) unsigned DEFAULT NULL,
 `voorraadGeblokkeerdEigenaar` smallint(5) unsigned DEFAULT NULL,
 `voorraadGeblokkeerdCB` smallint(5) unsigned DEFAULT NULL,
 `voorraadGereserveerd` smallint(5) unsigned DEFAULT NULL,
 `fondskosten` enum('depot leverbaar','depot onleverbaar','POD','BOV','eBoek','geen') COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `ISBN+datum` (`isbn`,`datum`) USING BTREE,
 KEY `UitgeverCode` (`UitgeverCode`),
 KEY `Imprint` (`imprintId`),
 KEY `VolgendeDatum` (`volgendeDatum`),
 KEY `Index op voorraad om maxima snel te vinden` (`isbn`,`voorraadAlles`) USING BTREE,
 KEY `fondskosten` (`fondskosten`),
 KEY `Datum+isbn+fondskosten` (`datum`,`isbn`,`fondskosten`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=16519430 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci 

Ответы [ 3 ]

1 голос
/ 20 мая 2020
  1. Это должно полностью обрабатывать WHERE:

    INDEX(fondskosten, Datum)
    

Примечание: сначала =, затем диапазон.

Получить *. Примечание. Если есть большие столбцы TEXT или BLOB, которые вам не нужны, составьте список SELECT, чтобы избежать их. Они могут храниться «вне записи», поэтому их получение может занять больше времени.

Необязательный ORDER BY. Если он на Datum, то лишних усилий нет. Если он находится в любом другом столбце, то сортировка будет. Но вроде 52 строк будет довольно быстро (миллисекунды).

Примечания:

  • Если у вас нет fondskosten IS NULL или у вас есть другой тест, тогда все ставки отключены. При разработке оптимального составного индекса нужно начинать заново.
  • USE/FORCE INDEX - используйте это в крайнем случае.
  • Всегда указывайте SHOW CREATE TABLE, когда нужно обсудить запрос.
  • В Advisor есть кое-что хорошее, но без каких-либо подсказок о том, что «слишком велико», это довольно бесполезно.
  • Я подозреваю все другие обсуждения не смогли поймите, что для заданного диапазона Datum гораздо больше 52 строк. То есть fondskosten IS NULL действительно является частью проблемы и решения.
1 голос
/ 21 мая 2020

Для людей, ищущих настройки в аналогичных случаях, это настройки, которые специалист внес в базу данных, которые значительно ускорили ее (заметьте, это для базы данных с сотнями таблиц и МНОГИМИ очень сложными и большими запросами, иногда объединяющими более 15 таблиц, но не сверхмассивное количество записей. Размер базы данных составляет всего 37 гигабайт.

[mysqld]
innodb_buffer_pool_size=2G
innodb_buffer_pool_instances=4
innodb_flush_log_at_trx_commit=2

tmp_table_size=64M
max_heap_table_size=64M

join_buffer_size=4M
sort_buffer_size=8M

optimizer_search_depth=5

optimizer_search_depth УМЕНЬШЕН, чтобы минимизировать время, необходимое оптимизатору для сложных запросов.

После перезапуска сервер, (регулярно) запускайте все запросы, которые являются результатом выполнения этого запроса:

SELECT CONCAT('OPTIMIZE TABLE `', TABLE_SCHEMA , '`.`', TABLE_NAME ,'`;') AS query
FROM INFORMATION_SCHEMA.TABLES
WHERE DATA_FREE/DATA_LENGTH > 2 AND DATA_LENGTH > 4*1024*1024

(Этот первый лучше, когда сервер отключен или мало используется, если у вас большие таблицы. Он перестраивает и таким образом оптимизирует таблицы, которые в нем нуждаются.)

И затем:

SELECT CONCAT('ANALYZE TABLE `', TABLE_SCHEMA , '`.`', TABLE_NAME ,'`;') AS query
FROM INFORMATION_SCHEMA.TABLES
WHERE DATA_FREE/DATA_LENGTH > 2 AND DATA_LENGTH > 1*1024*1024

(Эта вторая серия запросов ie намного легче и меньше нарушает авторские права, но все же может помочь ускорить некоторые запросы путем пересчета стратегии запросов сервером.)

0 голосов
/ 05 мая 2020

Похоже, что ORDER BY использует 3 разных плана оптимизации

  1. ORDER BY id - Дополнительно: Using index condition; Using where; Using filesort. MySQL использует filesort для разрешения ORDER BY. Но строки уже отсортированы. Итак, это занимает 15 секунд.
  2. ORDER BY Datum или другое неиндексированное поле - Extra: Using index condition; Using where. MySQL использует индекс Datum для разрешения ORDER BY. Это займет несколько секунд.
  3. ORDER BY index_field - Дополнительно: Using index condition; Using where; Using filesort. MySQL использует filesort для разрешения ORDER BY. Строки не отсортированы. Это займет несколько минут.

Это мое предложение. Только EXPLAIN может сказать, что происходит

Влияние на оптимизацию ORDER BY

UPD: Не могли бы вы проверить этот запрос с каждым предложением ORDER BY ?

SELECT * 
  FROM TitelDaggegevens USE INDEX FOR ORDER BY (Datum)
 WHERE fondskosten IS NULL 
   AND (Datum BETWEEN 20200401 AND 20200430)

Также вы можете попробовать увеличить sort_buffer_size

Если вы видите много Sort_merge_passes в секунду на выходе SHOW GLOBAL STATUS, вы можете рассмотрите возможность увеличения значения sort_buffer_size, чтобы ускорить операции ORDER BY или GROUP BY, которые нельзя улучшить с помощью оптимизации запросов или улучшенного индексирования. В Linux есть пороговые значения в 256 КБ и 2 МБ, где большие значения могут значительно замедлить выделение памяти, поэтому вам следует подумать о том, чтобы оставаться ниже одного из этих значений.

...