2014-10-28 2 views
4

Я хотел бы задать вопрос о том, как улучшить производительность в большой таблице MySQL с использованием InnoDB двигателя:Повышение производительности в большой таблице MySQL

Там в настоящее время таблицы в моей базе данных с около 200 миллионов строк. В этой таблице периодически хранятся данные, собранные различными датчиками. Структура таблицы выглядит следующим образом:

CREATE TABLE sns_value (
    value_id int(11) NOT NULL AUTO_INCREMENT, 
    sensor_id int(11) NOT NULL, 
    type_id int(11) NOT NULL, 
    date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    value int(11) NOT NULL, 
    PRIMARY KEY (value_id), 
    KEY idx_sensor id (sensor_id), 
    KEY idx_date (date), 
    KEY idx_type_id (type_id)); 

Сначала я подумал о разбиении таблицы в месяцах, но из-за неуклонное добавление новых датчиков он достигнет текущего размера примерно через месяц.

Другим решением, которое я придумал, было разбиение стола на датчики. Однако из-за предела 1024 разделов MySQL это не вариант.

Я считаю, что правильное решение будет использовать таблицу с той же структурой для каждого из датчиков:

sns_value_XXXXX

Таким образом, был бы больше, чем 1.000 столов с предполагаемого размером 30 миллионов строк в год. В то же время эти таблицы могут быть разделены на несколько месяцев для обеспечения быстрого доступа к данным.

Какие проблемы могут возникнуть в результате этого решения? Существует ли более нормализованное решение?

Редактирование с дополнительной информацией

Я считаю, что стол должен быть большим по отношению к серверу:

  • Облако 2xCPU и 8 Гб памяти
  • ЛАМПА (CentOS 6.5 и MySQL 5.1.73)

Каждый датчик может иметь более одного типа переменных (CO, CO2 и т. Д.).

я в основном имеют две медленные запросы:

1) Ежедневное резюме для каждого датчика и типа (ср, макс, мин):

SELECT round(avg(value)) as mean, min(value) as min, max(value) as max, type_id 
FROM sns_value 
WHERE sensor_id=1 AND date BETWEEN '2014-10-29 00:00:00' AND '2014-10-29 12:00:00' 
GROUP BY type_id limit 2000; 

Это занимает более 5 мин.

2) вертикального до горизонтального просмотра и экспорта:

SELECT sns_value.date AS date, 
sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 101)))))) AS one, 
sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 141)))))) AS two, 
sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 151)))))) AS three 
FROM sns_value 
WHERE sns_value.sensor_id=1 AND sns_value.date BETWEEN '2014-10-28 12:28:29' AND '2014-10-29  12:28:29' 
GROUP BY sns_value.sensor_id,sns_value.date LIMIT 4500; 

Это также занимает более 5 мин.

Другие соображения

  1. Timestamps может повторяться из-за вставки характеристик.
  2. Периодические вставки должны сосуществовать с выборами.
  3. Обновления и удаления не выполняются на столе.

Предположение, сделанное в «одну таблицы для каждого датчика» подход

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

Update 02/02/2015

Мы создали новую таблицу для каждого года данных, которые мы также распределяли в ежедневной основе. Каждая таблица имеет около 250 миллионов строк с 365 разделами. Новый индекс используется, как предложил Олли (sensor_id, date, type_id, value), но запрос все еще занимает от 30 секунд до 2 минут. Мы не используем первый запрос (ежедневное резюме), а только второе (вертикальное и горизонтальное).

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

Мы что-то упускаем? Есть ли способ улучшить производительность?

Большое спасибо!

+0

Какая проблема возникает с текущей структурой? –

+0

Большой? Что здесь большое? – TomTom

+0

Какова цель данных teis? Как его читать? – Aret

ответ

0

Создание отдельной таблицы для диапазона датчиков будет идеей.

Не используйте auto_increment для первичного ключа, если вам не нужно. Обычно БД-движок составляет clustering the data своим основным ключом.

Использовать составной ключ вместо этого, зависит от вашей пользовательской последовательности, последовательность столбцов может быть разной.

EDIT: Также добавлен тип в ПК. Учитывая запросы, я бы сделал это так. Выбор имен полей преднамерен, они должны быть описательными и всегда учитывать слова reserverd.

CREATE TABLE snsXX_readings (
    sensor_id int(11) NOT NULL, 
    reading int(11) NOT NULL, 
    reading_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    type_id int(11) NOT NULL, 

    PRIMARY KEY (reading_time, sensor_id, type_id), 
    KEY idx date_idx (date), 
    KEY idx type_id (type_id) 
); 

Кроме того, рассмотрите подведение итогов или группировку их в одно поле.

+1

Вы правы в том, что избегаете разделения таблиц и совершенно неверны в отношении вашего первичного предложения.Во всяком случае, с MySQL и InnoDB PK всегда должен быть auto_increment и почти никогда не составной или составной. Причина этого заключается в том, что вы упомянули о самой кластеризации и о том, как InnoDB использует ПК для кластеризации. –

+0

Спасибо за исправление меня по поводу первичного ключа, можете ли вы обратиться к некоторым материалам об использовании InnoDB и PK? – Aret

+1

В Percona есть много интересных статей. Я не могу дать вам несколько ссылок, содержащих все это, но [это pdf] (http://www.percona.com/files/presentations/percona-live/london-2011/PLUK2011-b-tree- indexes-and-innodb.pdf) должны пролить свет на этот вопрос. Лично я читал статьи и занимался исходным кодом InnoDB, чтобы понять, что он делает. Чтобы выразить это простым языком, следующее значение PK должно быть всегда больше, чтобы избежать перебалансировки b-дерева. В принципе, auto_increment делает это так, что это почти всегда идеальный кандидат. –

1

Edited на основе изменений в вопросе

Один стол на датчик есть, с уважением, очень плохая идея действительно. Для этого есть несколько причин:

  1. Серверы MySQL в обычных операционных системах имеют трудности с тысячами таблиц. Большинство ОС не могут одновременно обрабатывать многие одновременные обращения к файлам.
  2. Вам нужно создавать таблицы каждый раз, когда вы добавляете (или удаляете) датчики.
  3. Запросы, содержащие данные с нескольких датчиков, будут медленными и запутанными.

Моя предыдущая версия этого ответа предложила разбиение диапазонов на временную метку. Но это не будет работать с вашим основным ключом value_id. Однако, с запрошенными вами запросами и правильной индексацией вашей таблицы, разметка, вероятно, не понадобится.

(Избежать имя столбца date, если вы можете: это зарезервированное слово, и вы будете иметь много проблем написания запросов Вместо этого я предлагаю вам использовать ts, что означает временную метку.).

Остерегайтесь: int(11) значения не являются достаточно большими для вашей колонки value_id. У вас кончились иды. Используйте bigint(20) для этой колонки.

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

SELECT round(avg(value)) as mean, min(value) as min, max(value) as max, 
     type_id 
    FROM sns_value 
WHERE sensor_id=1 
    AND date BETWEEN '2014-10-29 00:00:00' AND '2014-10-29 12:00:00' 
GROUP BY type_id limit 2000; 

Для этого запроса, вы первый глядя sensor_id используя константу, то вы отрываясь диапазон date значений, то вы агрегирование по type_id. Наконец, вы извлекаете столбец value. Поэтому так называемый compound covering index на (sensor_id, date, type_id, value) сможет удовлетворить ваш запрос непосредственно при сканировании индекса. Это должно быть очень быстро для вас - конечно, быстрее, чем за 5 минут даже при большом столе.

В вашем втором запросе будет работать аналогичная стратегия индексирования.

SELECT sns_value.date AS date, 
     sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 101)))))) AS one, 
     sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 141)))))) AS two, 
     sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 151)))))) AS three 
    FROM sns_value 
WHERE sns_value.sensor_id=1 
    AND sns_value.date BETWEEN '2014-10-28 12:28:29' AND '2014-10-29 12:28:29' 
GROUP BY sns_value.sensor_id,sns_value.date 
LIMIT 4500; 

Опять же, вы начинаете с постоянным значением sensor_id, а затем использовать date диапазон. Затем вы извлекаете и type_id, и value. Это означает, что один и тот же индекс из четырех столбцов, который я упомянул, должен работать на вас.

CREATE TABLE sns_value (
    value_id bigint(20) NOT NULL AUTO_INCREMENT, 
    sensor_id int(11) NOT NULL, 
    type_id int(11) NOT NULL, 
    ts  timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    value int(11) NOT NULL, 
    PRIMARY KEY  (value_id), 
    INDEX query_opt (sensor_id, ts, type_id, value) 
); 
+0

@DRapp Причина индексов с заменой строк связана с тем, как MySQL использует составные индексы для удовлетворения запросов. Когда я предложил эти два индекса, первоначальный вопросник еще не раскрыл используемые запросы. –

+0

Понял, спасибо – DRapp

+0

Зачем добавлять значение в индекс? – Aret

0

Вы можете попробовать получить рандомизации сводные данные

У меня аналогичная таблица. таблица двигателя myisam (наименьший размер таблицы), запись 10 м, индекс на мой стол, потому что бесполезен (проверен). Получите весь диапазон для всех данных. результат: 10sn этот запрос.

SELECT * FROM (
     SELECT sensor_id, value, date 
     FROM sns_value l 
     WHERE l.sensor_id= 123 AND 
     (l.date BETWEEN '2013-10-29 12:28:29' AND '2015-10-29 12:28:29') 
     ORDER BY RAND() LIMIT 2000 
    ) as tmp 
    ORDER BY tmp.date; 

Этот запрос на первом этапе получить между датами и сортировкой рандомизации первые 2k данных, на данных второго шага сортировки. запрос каждый раз получает результат 2k для разных данных.

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