Я создаю модель базы данных для использования различными приложениями и различными типами серверов баз данных (хотя сейчас я в основном тестирую на MySQL и SQLite). Это действительно простая модель, которая в основном состоит из одной центральной таблицы совпадений и множества атрибутов таблиц, у которых match_id является их первичным ключом и одно другое поле (само значение атрибута). Другими словами, каждое совпадение имеет ровно по одному атрибуту каждого типа, и каждый атрибут хранится в отдельной таблице. После некоторой довольно низкой производительности во время сортировки и фильтрации по этим атрибутам (FROM matches LEFT JOIN attributes_i_want on primary index
) я решил попытаться улучшить ее. Для этого я добавил индекс для каждого столбца значения атрибута. Производительность сортировки и фильтрации значительно возросла для простых запросов.
Эта простая схема в основном является требованием для приложения, поэтому она может автоматически обнаруживать и использовать атрибуты. Таким образом, чтобы создать более сложные атрибуты, которые на самом деле основаны на других результатах, я решил использовать VIEW, которые превращают одну или несколько других таблиц, которые не обязательно соответствуют атрибутоподобной схеме, в схему атрибутов. Я называю эти мета-атрибуты (они также не редактируются напрямую). Тем не менее, для приложения это все прозрачно, и поэтому оно счастливо включается в VIEW, когда захочет. Проблема: убивает производительность . Когда VIEW присоединяется без сортировки по любому атрибуту, производительность все еще приемлема, но объединение извлечения VIEW с сортировкой недопустимо медленно (порядка 1 с). Даже после прочтения большого количества руководств по индексированию и некоторых вопросов о переполнении стека я не могу помочь.
_ Предпосылки для решения: так или иначе, num_duplicates должны существовать в виде таблицы или представления со столбцами match_id и num_duplicates, чтобы они выглядели как атрибут. Я не могу изменить способ обнаружения и использования атрибутов. Поэтому, если я хочу, чтобы num_duplicates появился в приложении, он должен быть как некое представление или материализованная таблица, которая составляет таблицу num_duplicates ._
Соответствующие части схемы
Основной стол:
CREATE TABLE `matches` (
`match_id` int(11) NOT NULL,
`source_name` text,
`target_name` text,
`transformation` text,
PRIMARY KEY (`match_id`)
) ENGINE=InnoDB;
Пример нормального атрибута (проиндексирован):
CREATE TABLE `error` (
`match_id` int(11) NOT NULL,
`error` double DEFAULT NULL,
PRIMARY KEY (`match_id`),
KEY `error_index` (`error`)
) ENGINE=InnoDB;
(все нормальные атрибуты, такие как error
, в основном одинаковы)
Метаатрибут / ПРОСМОТР:
CREATE VIEW num_duplicates
AS SELECT duplicate AS match_id, COUNT(duplicate) AS num_duplicates
FROM duplicate
GROUP BY duplicate
(это единственный метаатрибут, который я сейчас использую)
Простой запрос с индексацией по столбцам значений атрибута (часть улучшена за счет индексов)
SELECT matches.match_id, source_name, target_name, transformation FROM matches
INNER JOIN error ON matches.match_id = error.match_id
ORDER BY error.error
(производительность по этому запросу сильно возросла из-за индекса ошибки)
(время выполнения этого запроса составляет порядка 0,0001 с)
Чуть более сложные запросы и время их выполнения, включая метаатрибут (все еще плохая часть)
SELECT
matches.match_id, source_name, target_name, transformation, STATUS , volume, error, COMMENT , num_duplicates
FROM matches
INNER JOIN STATUS ON matches.match_id = status.match_id
INNER JOIN error ON matches.match_id = error.match_id
LEFT JOIN num_duplicates ON matches.match_id = num_duplicates.match_id
INNER JOIN volume ON matches.match_id = volume.match_id
INNER JOIN COMMENT ON matches.match_id = comment.match_id
(время выполнения: 0,0263сек) <--- все еще приемлемо </p>
SELECT matches.match_id, source_name, target_name, transformation, STATUS , volume, error, COMMENT , num_duplicates
FROM matches
INNER JOIN STATUS ON matches.match_id = status.match_id
INNER JOIN error ON matches.match_id = error.match_id
LEFT JOIN num_duplicates ON matches.match_id = num_duplicates.match_id
INNER JOIN volume ON matches.match_id = volume.match_id
INNER JOIN COMMENT ON matches.match_id = comment.match_id
ORDER BY error.error
LIMIT 20, 20
(время выполнения: 0,8866 с) <--- недопустимо (скорость запроса с LIMIT точно такая же, как и без LIMIT, <strong>note : если бы я мог получить версию с LIMIT, которая будет быстро, это уже было бы большой победой. Я полагаю, что это должно сканировать всю таблицу, и поэтому лимит не имеет большого значения)
ОБЪЯСНЕНИЕ последнего запроса
Конечно, я пытался решить это сам, прежде чем приехать сюда, но я должен признать, что я не настолько хорош в этих вещах и еще не нашел способа удалить обидного убийцу производительности. Я знаю, что это, скорее всего, использование сортировки файлов, но я не знаю, как от нее избавиться.
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY error index PRIMARY,match_id error_index 9 NULL 53909 Using index; Using temporary; Using filesort
1 PRIMARY COMMENT eq_ref PRIMARY PRIMARY 4 tangbig4.error.match_id 1
1 PRIMARY STATUS eq_ref PRIMARY PRIMARY 4 tangbig4.COMMENT.match_id 1 Using where
1 PRIMARY matches eq_ref PRIMARY PRIMARY 4 tangbig4.COMMENT.match_id 1 Using where
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 2
1 PRIMARY volume eq_ref PRIMARY PRIMARY 4 tangbig4.matches.match_id 1 Using where
2 DERIVED duplicate index NULL duplicate_index 5 NULL 49222 Using index
Кстати, запрос без сортировки, который все еще выполняется приемлемо, выглядит так:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY COMMENT ALL PRIMARY NULL NULL NULL 49610
1 PRIMARY error eq_ref PRIMARY,match_id PRIMARY 4 tangbig4.COMMENT.match_id 1
1 PRIMARY matches eq_ref PRIMARY PRIMARY 4 tangbig4.COMMENT.match_id 1
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 2
1 PRIMARY STATUS eq_ref PRIMARY PRIMARY 4 tangbig4.COMMENT.match_id 1
1 PRIMARY volume eq_ref PRIMARY PRIMARY 4 tangbig4.matches.match_id 1 Using where
2 DERIVED duplicate index NULL duplicate_index 5 NULL 49222 Using index
Вопрос
Итак, мой вопрос: может ли кто-то, кто знает больше о базах данных / MySQL, найти мне способ, которым я могу использовать / исследовать, чтобы повысить производительность моего последнего запроса.
Я довольно много думал о материализованных представлениях, но они изначально не поддерживаются в MySQL, и, поскольку я собираюсь использовать как можно более широкий диапазон SQL-серверов, это может быть не идеей.Я надеюсь, что изменение запросов или представлений может помочь или может привести к дополнительному индексу.
РЕДАКТИРОВАТЬ : Некоторые случайные мысли, которые у меня возникли по поводу запроса:
- ОЧЕНЬ БЫСТРО: объединение всех таблиц, кроме VIEW, сортировка
- ACCEPTABLE: объединение всех таблиц, включая VIEW, no sorting
- DOG SLOW: объединение всех таблиц, включая VIEW, sorting
Но: VIEW никак не влияет на сортировку, ни один из ее атрибутовили даже атрибуты в его составных таблицах используются для сортировки.Почему включение сортировки так сильно влияет на производительность?Можно ли как-то убедить базу данных сначала отсортировать, а затем просто присоединиться к ПРОСМОТРУ?Или я могу убедить его в том, что VIEW не важен для сортировки?
EDIT2 : После предложения @ace о создании VIEW и последующем присоединении не показалосьв помощь:
DROP VIEW IF EXISTS `matches_joined`;
CREATE VIEW `matches_joined` AS (
SELECT matches.match_id, source_name, target_name, transformation, STATUS , volume, error, COMMENT
FROM matches
INNER JOIN STATUS ON matches.match_id = status.match_id
INNER JOIN error ON matches.match_id = error.match_id
INNER JOIN volume ON matches.match_id = volume.match_id
INNER JOIN COMMENT ON matches.match_id = comment.match_id
ORDER BY error.error
);
с последующим:
SELECT matches_joined.*, num_duplicates
FROM matches_joined
LEFT JOIN num_duplicates ON matches_joined.match_id = num_duplicates.match_id
Однако использование LIMIT для представления имело значение:
DROP VIEW IF EXISTS `matches_joined`;
CREATE VIEW `matches_joined` AS (
SELECT matches.match_id, source_name, target_name, transformation, STATUS , volume, error, COMMENT
FROM matches
INNER JOIN STATUS ON matches.match_id = status.match_id
INNER JOIN error ON matches.match_id = error.match_id
INNER JOIN volume ON matches.match_id = volume.match_id
INNER JOIN COMMENT ON matches.match_id = comment.match_id
ORDER BY error.error
LIMIT 0, 20
);
После этого запрос был выполненна приемлемой скорости.Это уже хороший результат.Однако я чувствую, что прыгаю через обручи, чтобы заставить базу данных делать то, что я хочу, и сокращение времени, вероятно, вызвано только тем фактом, что теперь ей нужно только отсортировать 20 строк.Что если у меня будет больше строк?Есть ли другой способ заставить базу данных увидеть, что объединение в num_duplicates
VIEW никак не влияет на сортировку?Могу ли я, возможно, изменить запрос, который делает просмотр немного?