Mysql улучшить скорость SELECT - PullRequest
10 голосов
/ 23 января 2011

В настоящее время я пытаюсь улучшить скорость SELECTS для таблицы MySQL и буду признателен за любые предложения о способах ее улучшения.

У нас более 300 миллионов записей в таблице, а в таблице есть тег структуры, дата, значение.Первичный ключ - это комбинированный ключ тега и даты.Таблица содержит информацию о 600 уникальных тегах, большинство из которых содержат в среднем около 400 000 строк, но могут варьироваться от 2000 до более 11 миллионов строк.

Запросы, выполняемые к таблице:

  SELECT date,
         value 
    FROM table 
   WHERE tag = "a" 
     AND date BETWEEN 'x' and 'y' 
ORDER BY date

.... и их очень мало, если вообще есть ВСТАВКИ.

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

Ответы [ 8 ]

4 голосов
/ 11 марта 2011

найдите время, чтобы прочитать мой ответ здесь: (имеет такие же объемы, как ваш)

500 миллионов строк, 15 миллионов сканирования диапазона строк за 0,02 секунды.

MySQL и NoSQL: помогите выбрать правильный

затем измените ваш движок таблицы на innodb следующим образом:

create table tag_date_value
(
tag_id smallint unsigned not null, -- i prefer ints to chars
tag_date datetime not null, -- can we make this date vs datetime ?
value int unsigned not null default 0, -- or whatever datatype you require
primary key (tag_id, tag_date) -- clustered composite PK
)
engine=innodb;

вместо первичного ключа вы можете рассмотреть следующее:

primary key (tag_id, tag_date, value) -- added value save some I/O

но только если значением не является какой-то БОЛЬШОЙ тип varchar!

запрос, как и раньше:

select
 tag_date, 
 value
from
 tag_date_value
where
 tag_id = 1 and
 tag_date between 'x' and 'y'
order by
 tag_date;

надеюсь, это поможет:)

EDIT

о, забыл упомянуть - не используйте alter table, чтобы изменить тип движка с mysiam на innodb, а скорее выведите данные в файлы csv и повторно импортируйте их во вновь созданную пустую таблицу innodb.

обратите внимание, я упорядочиваю данные во время процесса экспорта - кластерные индексы - это КЛЮЧ!

Экспорт

select * into outfile 'tag_dat_value_001.dat' 
fields terminated by '|' optionally enclosed by '"'
lines terminated by '\r\n'
from
 tag_date_value
where
 tag_id between 1 and 50
order by
 tag_id, tag_date;

select * into outfile 'tag_dat_value_002.dat' 
fields terminated by '|' optionally enclosed by '"'
lines terminated by '\r\n'
from
 tag_date_value
where
 tag_id between 51 and 100
order by
 tag_id, tag_date;

-- etc...

Импорт

импорт обратно в таблицу в правильном порядке!

start transaction;

load data infile 'tag_dat_value_001.dat' 
into table tag_date_value
fields terminated by '|' optionally enclosed by '"'
lines terminated by '\r\n'
(
tag_id,
tag_date,
value
);

commit;

-- etc...
1 голос
/ 09 марта 2011

Я хотел бы сделать две вещи - сначала добавить несколько индексов вокруг тега и даты, как указано выше:

alter table table add index (tag, date);

Далее разбейте ваш запрос на основной запрос и выберите, в котором вы сужаете результаты, когда входите в основной запрос:

SELECT date, value
FROM table
WHERE date BETWEEN 'x' and 'y'
AND tag IN ( SELECT tag FROM table WHERE tag = 'a' )
ORDER BY date
1 голос
/ 11 марта 2011

Ваш запрос требует нескольких вещей - и с таким большим количеством строк внешний вид данных может изменить оптимальный подход.

   SELECT date, value 
   FROM table 
   WHERE tag = "a" 
     AND date BETWEEN 'x' and 'y' 
   ORDER BY date

Есть несколько вещей, которые могут замедлитьвниз по этому запросу выбора.

  1. Очень большой набор результатов, который необходимо отсортировать (упорядочить по)
  2. Очень большой набор результатов.Если тег и дата присутствуют в индексе (и давайте предположим, что это так же хорошо, как получается), каждая строка результата должна будет покинуть индекс, чтобы найти поле значения.Думайте об этом как о необходимости первого предложения каждой главы книги.Если вам нужно было только знать названия глав, просто - вы можете получить их из оглавления, но, поскольку вам нужно первое предложение, вам нужно перейти к самой главе.В некоторых случаях оптимизатор может выбрать просто пролистать всю книгу (просмотр таблицы в жаргоне плана запроса), чтобы получить эти первые предложения.
  3. Фильтрация по неправильному предложению where.Если индекс находится в теге order, date ..., тег должен (для большинства ваших запросов) быть более строгим из двух столбцов.Таким образом, в основном, если у вас нет больше тегов, чем дат (или, может быть, чем даты в типичном диапазоне дат), тогда даты должны быть первым из двух столбцов в вашем индексе.

Несколько рекомендаций:

  1. Подумайте, возможно ли обрезать некоторые из этих данных, если они слишком стары, чтобы заботиться о них большую часть времени.
  2. Попробуйте поиграть с вашим текущим индексом - то есть изменить порядок элементов в нем.
  3. Не используйте текущий индекс и замените его индексом покрытия (содержит все 3 поля)
  4. Запустите некоторые EXPLAIN и убедитесь, что он вообще использует ваш индекс.
  5. Переключитесь на другое хранилище данных (mongo db?) Или иным образом убедитесь, что эта таблица монстров хранится в памяти как можно больше.
1 голос
/ 23 января 2011

Какова мощность поля даты (то есть, сколько разных значений появляется в этом поле)?Если дата МЕЖДУ 'x' И 'y' является более ограничивающей, чем тег = 'a' в предложении WHERE, попробуйте сделать ваш первичный ключ (date, tag) вместо (tag, date), разрешив использовать dateв качестве индексированного значения.

Кроме того, будьте осторожны при указании «x» и «y» в предложении WHERE.В некоторых случаях MySQL будет преобразовывать каждое поле даты в соответствие с не подразумеваемым типом значений, с которыми вы сравниваете.

0 голосов
/ 09 февраля 2011

Попробуйте вставить только нужные даты во временную таблицу и завершите выбор во временной таблице для тегов и порядка.

CREATE temporary table foo
SELECT date, value 
FROM table 
WHERE date BETWEEN 'x' and 'y' ;

ALTER TABLE foo ADD INDEX index( tag );

SELECT date, value 
FROM foo 
WHERE tag = "a" 
ORDER BY date;

, если это не сработает, попробуйте создать foo из тегавыбор вместо.

CREATE temporary table foo
SELECT date, value 
FROM table 
WHERE tag = "a";    

ALTER TABLE foo ADD INDEX index( date );

SELECT date, value 
FROM foo 
WHERE date BETWEEN 'x' and 'y' 
ORDER BY date;
0 голосов
/ 23 января 2011

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

Я буду аргументировать мои подозрения в отношении столбца value тем фактом, что разбиение фактически не сокращает время выполнения запроса. Можете ли вы выполнить запрос без value и дать нам еще какие-нибудь результаты, а также ОБЪЯСНИТЬ? Вы действительно нуждаетесь в этом для каждой строки и какой это столбец?

Ура!

0 голосов
/ 23 января 2011

Я думаю, что добавление индекса на (tag, date) поможет:

alter table table add index (tag, date);

Пожалуйста, опубликуйте результат объяснения по этому запросу (EXPLAIN SELECT date, value ОТ ......)

0 голосов
/ 23 января 2011

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

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

...