Накладные расходы для MySQL SELECTS — лучше использовать один или несколько последовательно

Есть ли заметная разница в производительности между одним запросом SELECT foo, bar, FROM users, который возвращает 500 строк, и 500 запросами SELECT foo, bar, FROM users WHERE id = x, поступающими одновременно?

В PHP-приложении, которое я пишу, я пытаюсь выбрать между написанием ясного, читаемого участка кода, который выдаст около 500 операторов SELECT; или написать его неясным, сложным способом, который будет использовать только один SELECT, возвращающий 500 строк.

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

Справочная информация, если она уместна: 1) Это модуль Drupal, закодированный на PHP 2) Рассматриваемые таблицы получают очень мало INSERT и UPDATE и редко блокируются 3) СОЕДИНЕНИЯ SQL невозможны по причинам, не относящимся к вопрос

Спасибо!


person anschauung    schedule 05.12.2009    source источник


Ответы (3)


Почти всегда быстрее выполнить один большой пакет SELECT и проанализировать результаты в коде приложения, чем выполнять огромное количество SELECT для одной строки. Я бы порекомендовал вам реализовать оба и профилировать их. Всегда стремитесь свести к минимуму количество предположений, которые вы должны сделать.

person Emil H    schedule 05.12.2009
comment
Спасибо! Я так и подозревал. Не могли бы вы объяснить, почему это лучше, например, из-за накладных расходов на соединение? Я хотел бы лучше понять, как именно один из способов лучше. - person anschauung; 05.12.2009
comment
ЦП и память всегда менее затратны, чем любая форма ввода-вывода, включая сетевой ввод-вывод. Подумайте об этом так: определенно дешевле отправить один заказ на 500 книг и получить их одной огромной посылкой по почте, чем отправить 500 заказов и получить все книги в отдельных упаковках. :) - person Emil H; 06.12.2009

Я бы не стал слишком беспокоиться о накладных расходах на запросы mysql, особенно если вы не закрываете соединение между каждым запросом. Учтите, что если ваш запрос создает временную таблицу, вы уже потратили на запрос больше времени, чем потребовались накладные расходы запроса.

Лично мне нравится выполнять сложные SQL-запросы, но я обнаружил, что размер таблиц, кеш запросов mysql и производительность запросов, которые должны выполнять проверку диапазона (даже по индексу), имеют значение.

Я предлагаю это:

1) Установите простой и правильный базовый уровень. Я подозреваю, что это подход с миллионом запросов. Это не неправильно и, скорее всего, чертовски правильно. Запустите его несколько раз и посмотрите на кеш запросов и производительность приложения. Возможность поддерживать ваше приложение в сопровождении очень важна, особенно если вы работаете с другими специалистами по сопровождению кода. Кроме того, если вы запрашиваете действительно большие таблицы, небольшие запросы сохранят масштабируемость.

2) Закодируйте сложный запрос. Сравните результаты на точность, а затем на время. Затем используйте EXPECT в запросе, чтобы увидеть, какие строки отсканированы. Я часто обнаруживал, что если у меня есть JOIN, или WHERE x != y, или условие, создающее временную таблицу, производительность запроса может сильно ухудшиться, особенно если я нахожусь в таблице, которая постоянно обновляется. Однако я также обнаружил, что сложный запрос может быть неправильным, а также что сложный запрос может легко сломаться по мере роста приложения. Сложные запросы обычно сканируют большие наборы строк, часто создают временные таблицы и вызывают using where сканирование. Чем больше стол, тем дороже они получаются. Кроме того, у вас могут быть групповые соображения, когда сложные запросы не соответствуют сильным сторонам вашей команды.

3) Поделитесь результатами со своей командой.

Сложные запросы с меньшей вероятностью попадут в кеш запросов mysql, и если они достаточно велики, не кэшируйте их. (Вы хотите сохранить кеш запросов mysql для часто попадающихся запросов.) Кроме того, запрос, где предикаты, которые должны сканировать индекс, также не будут работать. (х != у, х > у, х ‹ у). Такие запросы, как SELECT foo, bar FROM users WHERE foo != 'g' and mumble < '360', заканчиваются сканированием. (Стоимость накладных расходов на запросы в этом случае может быть незначительной.)

Небольшие запросы часто можно выполнять без создания временных таблиц, просто получая все значения из индекса, если поля, которые вы выбираете и на которых основываетесь, индексируются. Таким образом, производительность запросов SELECT foo, bar FROM users WHERE id = x действительно велика (особенно, если столбцы foo и bar индексируются, например, alter table users add index ix_a ( foo, bar );.)

Другими хорошими способами повышения производительности вашего приложения могут быть кэширование этих небольших результатов запроса в приложении (если это уместно) или выполнение пакетных заданий запроса материализованного представления. Кроме того, рассмотрите memcached или некоторые функции, имеющиеся в XCache.

person memnoch_proxy    schedule 06.12.2009
comment
Отличный совет. Я только что проверил альтернативные запросы, в которых меньшая партия сопоставимых запросов выполнялась в 5 раз быстрее, чем большой сложный запрос. - person kingjeffrey; 04.02.2011
comment
Хотя общий совет здесь хорош, говорить, что не нужно беспокоиться о накладных расходах на соединение при использовании php с mysql, абсолютно неверно. Попробуйте протестировать описанную ситуацию, и вы увидите, что выполнение 500 маленьких запросов по сравнению с 1 большим будет иметь огромную разницу. Способ с одним запросом может быть примерно в 50 раз быстрее, чем способ с несколькими запросами. - person Zaptree; 25.03.2012

Кажется, вы знаете, что такое 500 значений id, так почему бы не сделать что-то вроде этого:

// Assuming you have already validated that this array contains only integers
// so there is not risk of SQl injection

$ids = join(',' $arrayOfIds);

$sql = "SELECT `foo`, `bar` FROM `users` WHERE `id` IN ($ids)";
person Doug Neiner    schedule 05.12.2009
comment
Я не знаю идентификаторов в этом случае, но это изящная техника. Я должен запомнить это. - person anschauung; 05.12.2009
comment
Если у вас нет идентификаторов, как вы можете запустить SELECT foo, bar, FROM users WHERE id = x? - person Doug Neiner; 05.12.2009
comment
Я понимаю что ты имеешь ввиду. Я хотел написать, что я могу получить идентификаторы, но самый простой способ сделать это — получить их по одному. У меня нет их всех в одной удобной упаковке. - person anschauung; 05.12.2009
comment
Значит ли это, что вы выполняете запросы 500x2? 1 поиск id и 1 SELECT с id ? - person Doug Neiner; 05.12.2009
comment
В корне да. На практике я подключаюсь к более крупной системе, в которой данные расположены в формате, отличном от того, который мне нужен для этой конкретной операции. Более крупное приложение все равно искало бы идентификатор, поэтому я не считаю это вторым запросом для узких целей этого вопроса. - person anschauung; 05.12.2009
comment
Список из 500 id не очень большой. Я создал предикаты where id in ( $list ) с десятками тысяч элементов в списке. - person memnoch_proxy; 06.12.2009