более быстрый способ использовать наборы в MySQL - PullRequest
1 голос
/ 25 мая 2009

У меня есть таблица Myno 5.1 InnoDB (customers) со следующей структурой:

int         record_id (PRIMARY KEY)
int         user_id (ALLOW NULL)
varchar[11] postcode (ALLOW NULL)
varchar[30] region (ALLOW NULL)
..
..
..

В таблице примерно 7 миллионов строк. В настоящее время таблица запрашивается следующим образом:

SELECT * FROM customers WHERE user_id IN (32343, 45676, 12345, 98765, 66010, ...

в текущем запросе, в настоящее время более 560 user_id с находятся в предложении IN. Этот запрос содержит несколько миллионов записей: slow !

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

Я знаю, что SELECT(*) - это плохая вещь, и это будет расширен до полного списка обязательных полей. Однако поля, не перечисленные выше, больше int с и double с. Еще 50 из них возвращаются, но они необходимы для отчета.

Я полагаю, что есть гораздо лучший способ получить доступ к данным для user_id с, но я не могу думать, как это сделать. Моя первая реакция - удалить ALLOW NULL в поле user_id, как я понимаю, NULL обработка замедляет запросы?

Я был бы очень признателен, если бы вы указали мне более эффективное направление, чем использование метода IN ( ).

EDIT Побежал ОБЪЯСНИТЬ, что сказал:

select_type = SIMPLE 
table = customers 
type = range 
possible_keys = userid_idx 
key = userid_idx 
key_len = 5 
ref = (NULL) 
rows = 637640 
Extra = Using where 

это помогает?

Ответы [ 5 ]

3 голосов
/ 25 мая 2009

Сначала проверьте, есть ли индекс на USER_ID и убедитесь, что он используется .

Вы можете сделать это, запустив EXPLAIN.

Во-вторых, создайте временную таблицу и используйте ее в JOIN:

CREATE TABLE temptable (user_id INT NOT NULL)

SELECT  *
FROM    temptable t
JOIN    customers c
ON      c.user_id = t.user_id

В-третьих, как строки могут возвращать ваш запрос?

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

NULL не будет замедлять ваш запрос, поскольку условие IN удовлетворяет только не NULL индексированным значениям.

Обновление:

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

Вам действительно нужно поместить все эти 638,000 строк в отчет?

Надеюсь, это не напечатано: вредно для тропических лесов, глобального потепления и прочего.

Если говорить серьезно, то, похоже, в вашем запросе требуется либо агрегация, либо разбивка на страницы.

2 голосов
/ 26 мая 2009

«Выбрать *» не так плохо, как думают некоторые; Базы данных на основе строк извлекают всю строку, если они выбирают какую-либо из них, поэтому в ситуациях, когда вы не используете индекс покрытия, «SELECT *» по сути не медленнее, чем «SELECT a, b, c» (Примечание: там иногда является исключением, когда у вас большие большие двоичные объекты, но это крайний случай).

Перво-наперво - ваша база данных помещается в ОЗУ? Если нет, получите больше оперативной памяти. Нет, серьезно. Теперь, предположим, что ваша база данных слишком велика, чтобы разумно вписаться в оперативную память (скажем,> 32 ГБ), вам следует попытаться уменьшить количество случайных операций ввода-вывода, поскольку они, вероятно, удерживают вещи.

С этого момента я предполагаю, что вы используете надлежащее оборудование серверного класса с контроллером RAID в RAID1 (или RAID10 и т. Д.) И по крайней мере двумя шпинделями. Если нет, то иди и возьми это.

Вы можете определенно рассмотреть возможность использования кластерного индекса. В MySQL InnoDB вы можете кластеризовать только первичный ключ, что означает, что если что-то еще является первичным ключом, вам придется его изменить. С составными первичными ключами все в порядке, и если вы выполняете много запросов по одному критерию (скажем, user_id), определенное преимущество состоит в том, чтобы сделать его первой частью первичного ключа (вам нужно добавить что-то еще, чтобы сделать его уникальный).

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

Что касается эффективности запросов, то WHERE user_id IN (большой список идентификаторов) почти наверняка является наиболее эффективным способом сделать это из SQL.

НО мои самые большие советы:

  • Имейте в виду цель, решите, что это такое, и когда вы ее достигнете, остановитесь.
  • Не поверь никому на слово - попробуй и посмотри
  • Убедитесь, что ваша система тестирования производительности соответствует спецификации оборудования
  • Убедитесь, что у вашей системы тестирования производительности тот же размер и тип данных, что и у рабочей (та же схема недостаточно хороша!).
  • Используйте синтетические данные, если невозможно использовать производственные данные (копирование производственных данных может быть сложным с логистической точки зрения (помните, что ваша база данных имеет размер> 32 ГБ); это также может нарушать политики безопасности).
  • Если ваш запрос является оптимальным (как это, вероятно, уже есть), попробуйте настроить схему, а затем саму базу данных.
1 голос
/ 25 мая 2009

Это ваш самый важный запрос? Это транзакционная таблица?

Если это так, попробуйте создать кластерный индекс для user_id. Ваш запрос может быть медленным, потому что он все равно должен выполнять случайное чтение с диска для извлечения столбцов (поиск ключей), даже после нахождения соответствующих записей (поиск по индексу user_Id).

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

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

1 голос
/ 25 мая 2009

Они одинаковые ~ 560 идентификаторов каждый раз? Или это разные ~ 500 идентификаторов при разных запусках запросов?

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

0 голосов
/ 25 мая 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...