2017-02-17 5 views
0

У меня есть таблица мониторинга со следующей структурой:Высокий дорожный стол, оптимальные индексы?

CREATE TABLE `monitor_data` (
    `monitor_id` INT(10) UNSIGNED NOT NULL, 
    `monitor_data_time` INT(10) UNSIGNED NOT NULL, 
    `monitor_data_value` INT(10) NULL DEFAULT NULL, 
    INDEX `monitor_id_data_time` (`monitor_id`, `monitor_data_time`), 
    INDEX `monitor_data_time` (`monitor_data_time`) 
) 
COLLATE='utf8_general_ci' 
ENGINE=InnoDB; 

Это очень высокий стол трафика с потенциально тысячами строк каждой минуты. Каждая строка относится к монитору и содержит значение и время (UNIX_TIMESTAMP)

У меня есть три вопроса:

1. Внезапно, после нескольких месяцев в разработчике, стол вдруг стало очень медленно. Запросы, которые ранее выполнялись под вторым, теперь могут занимать до минуты. Я использую стандартные настройки в my.cnf, так как это dev-машина, но поведение было действительно очень странным для меня.

2. Я не уверен, что у меня есть оптимальные показатели. «Нормальный» запрос выглядит следующим образом:

SELECT DISTINCT(md.monitor_data_time), monitor_data_value 
FROM monitor_data md 
WHERE md.monitor_id = 165 
    AND md.monitor_data_time >= 1484076760 
    AND md.monitor_data_time <= 1487271199 
ORDER BY md.monitor_data_time ASC; 

EXPLAIN на запрос выше выглядит следующим образом:

id;select_type;table;type;possible_keys;key;key_len;ref;rows;Extra 
1;SIMPLE;md;range;monitor_id_data_time,monitor_data_time;monitor_id_data_time;8;\N;149799;Using index condition; Using temporary; Using filesort 

Что вы думаете об индексах?

3. Если я не укажу DISTINCT в запросе выше, я получаю дубликаты строк, даже если в таблице нет повторяющихся строк. Любое объяснение этому поведению?

Любой вход очень ценится!

UPDATE 1:

Новое предложение по структуре таблицы:

CREATE TABLE `monitor_data_test` (
`monitor_id` INT UNSIGNED NOT NULL, 
`monitor_data_time` INT UNSIGNED NOT NULL, 
`monitor_data_value` INT UNSIGNED NULL DEFAULT NULL, 
PRIMARY KEY (`monitor_data_time`, `monitor_id`), 
INDEX `monitor_data_time` (`monitor_data_time`) 
) COLLATE='utf8_general_ci' ENGINE=InnoDB; 
+0

Примечание об обновлении 1: вторичный индекс избыточен с предлагаемым первичным ключом; добавив, что это отходы. Для предикатов запроса, заданного в вопросе, мы предпочли бы, чтобы индекс с 'monitor_id' был ведущим столбцом (как я предложил в своем ответе). Если есть какая-то причина, кластерный ключ не имеет' (monitor_id , monitor_data_time) 'как ведущие столбцы, тогда нам нужен индекс * cover * как вторичный индекс' (monitor_Id, monitor_data_time, monitor_data_value) '. Есть определенные причины для моих рекомендаций; мы не просто бросаем вещи на стену, чтобы увидеть, какие палки. – spencer7593

ответ

2

Что вы думаете об индексах?

Индекс (monitor_id,monitor_data_time) представляется подходящим для запроса. Это подходит для операции сканирования диапазона индексов, очень быстро устраняя лодку строк, которые необходимо изучить.

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

И даже лучше, если ключ кластера InnoDB должен быть PRIMARY KEY или UNIQUE KEY на столбцах, вместо того, чтобы нести накладные расходы на синтетический идентификатор строки, который создает InnoDB, когда соответствующий индекс не определен.

Если бы я не разрешал дублировать (monitor_id, monitor_data_time) кортежей, то я бы определил таблицу с индексом UNIQUE для тех столбцов, которые не являются допустимыми.

CREATE TABLE `monitor_data` 
(`monitor_id`   INT(10) UNSIGNED NOT NULL 
, `monitor_data_time` INT(10) UNSIGNED NOT NULL 
, `monitor_data_value` INT(10) NULL DEFAULT NULL 
, UNIQUE KEY `monitor_id_data_time` (`monitor_id`, `monitor_data_time`) 
) ENGINE=InnoDB 

или эквивалент, укажите ГЛАВНЫЙ вместо UNIQUE и удалить идентификатор

CREATE TABLE `monitor_data` 
(`monitor_id`   INT(10) UNSIGNED NOT NULL 
, `monitor_data_time` INT(10) UNSIGNED NOT NULL 
, `monitor_data_value` INT(10) NULL DEFAULT NULL 
, PRIMARY KEY (`monitor_id`, `monitor_data_time`) 
) ENGINE=InnoDB 

Любое объяснение такого поведения?

Если запрос (как показано в этом вопросе) возвращает разное количество строк с DISTINCT ключевое слово, то должен быть дубликатом (monitor_id,monitor_data_time,monitor_data_value) кортежей в таблице. В определении таблицы ничего нет, что гарантирует, что дубликатов нет.

Есть несколько других возможных объяснений, но эти объяснения связаны с добавлением/изменением строк и запросами, которые видят разные моментальные снимки, уровни изоляции транзакций, yada, yada. Если данные не изменяются, то есть повторяющиеся строки.


ограничение первичного ключа (или UNIQUE KEY ограничений Ненулевых столбцы) гарантировало бы нам уникальность.

Обратите внимание, что DISTINCT является ключевым словом в списке SELECT. Это не функция. Ключевое слово DISTINCT применяется к всем выражениям в списке SELECT. Параны вокруг md.monitor_date_time являются излишними.

Оставив ключевое слово DISTINCT, исключение необходимости в работе «Использование файлового управления». И это может быть дорогостоящим для больших наборов, особенно когда набор слишком велик для сортировки в памяти, и сортировка должна разливаться на диск.

Было бы гораздо эффективнее иметь гарантированную уникальность, опустить ключевое слово DISTINCT и возвращать строки по порядку по индексу, предпочтительно к кластерному ключу.

Кроме того, вторичный индекс monitor_data_time не подходит для этого запроса. (Там могут быть и другие вопросы, которые могут сделать эффективное использование индекса, хотя один подозревает, что эти запросы будут также эффективно использовать композитный индекс, который был monitor_data_time в качестве ведущей колонки.

+0

@gregoff: Похоже, вы задали более ранний вопрос здесь: http://stackoverflow.com/questions/32794612/mysql-table-structure-do-i-need-a-primary-key, должна ли эта таблица иметь ПЕРВИЧНЫЙ КЛЮЧ. Если кортеж '(monitor_id, monitor_data_time) уникален, да, это должен быть составной кластерный ключ таблицы. Этот кортеж можно определить как ПЕРВИЧНЫЙ КЛЮЧ таблицы. Или, учитывая, что эти два столбца определены NOT NULL, объявление UNIQUE KEY даст тот же эффект. Для этой таблицы нет необходимости определять дополнительный столбец, который будет служить суррогатным ключом. – spencer7593

+0

Да, но я создал новый вопрос, так как этот вопрос был более подробным с дополнительной информацией. Благодарю вас за ваш вклад! Похоже, я должен сделать ставку на создание комбинированного ПК, так как в любом случае первые два столбца должны быть уникальными. Обновленный мой вопрос выше с предложением новой структуры (ОБНОВЛЕНИЕ 1). Будет ли это работать лучше? Я действительно ищу лучшее долгосрочное решение, так как эта таблица потенциально будет содержать много миллионов строк. Должен ли я также добавить один индекс в столбце monitor_data_value? – gregoff

+0

Я одобрительно опубликовал редактирование на вашем посту, когда я должен был сделать это сам. Пожалуйста, не обращайте внимания. – gregoff

2
SELECT DISTINCT(md.monitor_data_time), monitor_data_value 

такая же, как

SELECT DISTINCT md.monitor_data_time, monitor_data_value 

То есть, пара отличается. это делает не DeDup только time. это то, что вы хотите?

Если вы пытаетесь DeDup толькоtime, то сделать что-то вроде

SELECT time, AVG(value) 
    ... 
    GROUP BY time; 

Для оптимальной работы

WHERE md.monitor_id = 165 
AND md.monitor_data_time >= 14840767604 ... 

вам нужно

PRIMARY KEY (monitor_id, monitor_data_time) 

и должны быть в таком порядке.Противоположный порядок много менее полезно. Руководящий принцип: Начните с «=», затем переходите к «диапазону». Другие обсуждения here.

У вас есть 4 миллиарда monitor_id значений? INT занимает 4 байта; рассмотрите возможность использования меньшего типа данных.

У вас есть другие вопросы, требующие оптимизации? Лучше спроектировать индекс (ы) после сбора всех важных запросов.

Почему PK

В InnoDB, то PRIMARY KEY является "гнездовым" с данными. То есть данные представляют собой упорядоченный список троек: (id, time, value), хранящихся в дереве B +. Расположение id = 165 AND time = 1484076760 - это базовая операция BTree. И это очень быстро. Затем сканирование вперед (это «+» часть «B + Tree»), пока time = 1487271199 не будет очень быстрой операцией «следующей строки» в этом упорядоченном списке. Кроме того, поскольку value находится прямо там с id и time, нет никаких дополнительных усилий для получения значений.

Вы не можете быстрее сканировать запрошенные строки. Но для этого требуется PRIMARY KEY. (OK, UNIQUE(id, time) будет «способствовать», чтобы быть PK, но давайте не будем путать этот вопрос.)

Contrast ... Учитывая индекс (time, id), он будет делать сканирование над датами мелких, но она должна была бы пропустите любые записи, где id != 165 Но ему нужно было бы прочитать все эти строки, чтобы обнаружить, что они не применяются. Намного больше усилий.

Поскольку неясно, что вы предназначеноDISTINCT, я не могу продолжить подробное обсуждение того, как это происходит. Достаточно сказать: возможные строки найдены; теперь для прохождения DISTINCT необходим какой-то вторичный проход. (Возможно, это даже не нужно делать.)

+0

Спасибо за ваш ввод! Зачем нужен первичный ключ именно в этом порядке? Просто потому, что этот monitor_id, вероятно, уже, чем monitor_data_time? – gregoff

+0

Нет. Существует один идентификатор «id», и записи для этого «id» будут смежными «временем». Подумайте о кабинете дантиста (который все еще использует бумажные файлы). Чтобы найти _your_ записи для _last year_, это лучше иметь папку с хронологически отсортированными записями, чем собирать записи всех, а затем искать в прошлом году, глядя только ваши записи. –

+0

Хорошее объяснение! Благодаря! – gregoff

Смежные вопросы