СОВМЕСТНЫЕ против утверждений - PullRequest
6 голосов
/ 12 сентября 2011

В компании, где я работал, они работают с реляционной базой данных PHP / MySQL. Я всегда думал, что если бы мне нужно было извлечь различную информацию из разных таблиц, то я мог бы просто сделать простое объединение, чтобы получить такие данные, как ....

SELECT table_1.id, table_2.id FROM table_1 LEFT JOIN table_2 ON table_1.sub_id = table_2.id

Когда я попал туда, где я сейчас работаю, это то, что они делают.

<?php $query = mysql_query("SELECT sub_id FROM table_1");
while($rs = mysql_fetch_assoc($query)) {
    $query_2 = mysql_fetch_assoc(mysql_query("SELECT * FROM table_2 WHERE id = '{$rs['sub_id']}'"));
    //blah blah blah more queries
?>

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

Я был сбит с толку, поэтому подумал, что выложу это для широкой публики. Это может быть вопросом мнения, но действительно ли быстрее сделать оператор while (один больший запрос, чтобы вытащить много строк, а затем, если хотите, множество маленьких крошечных подзапросов) или выполнить объединение (тянуть один запрос большего размера, чтобы получить все необходимые данные). Пока индексы сделаны правильно, имеет ли это значение? Еще одна вещь, которую следует учитывать, это то, что текущая БД находится в формате InnoDB.

Спасибо!

Обновление 8/28/14

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

Я думаю, что это было немного излишним, потому что я превратил весь отчет (он довольно динамичен в отношении возвращаемых данных) в масштабный фестиваль объединения. Большинство соединений, если не все, объединяют значение с первичным ключом, поэтому все они выполняются очень быстро. Если в отчете было указано, что нужно извлечь 30 столбцов данных, а в 2000 записей, то в каждом поле выполнялся запрос для извлечения данных (поскольку этот фрагмент данных мог находиться в другом поле). 30 x 2000 = 60000 и даже при приятном времени запроса 0,0003 секунды на запрос, это все равно было 18 секундами простого времени запроса (что, как я помню, довольно много). Теперь, когда я перестроил запрос как массивное объединение нескольких первичных ключей (где это возможно), этот же отчет загружался примерно через 2-3 секунды, и большую часть этого времени загружал html. Каждая возвращаемая запись выполняет от 0 до 4 дополнительных запросов в зависимости от необходимых данных (может не потребоваться никаких данных, если они могут извлечь их в объединениях, что происходит в 75% случаев). Таким образом, те же самые записи 2000 будут возвращать дополнительные 0-8000 запросов (намного лучше, чем 60000).

Я бы сказал, что оператор while полезен в некоторых случаях, но, как указано ниже в комментариях, это все, что касается тестирования. В моем случае, объединения были лучшим вариантом, но в других областях моего сайта, инструкция while более полезна. В одном случае у меня есть отчет, в котором клиент может запросить несколько категорий и получить только данные для этих категорий. Случилось так, что у меня был category_id IN(...,...,..,.., etc etc etc) с 50-500 удостоверениями личности, и индекс захлебывался и умирал в моих руках, когда я держал его в последние минуты. Поэтому я разложил идентификаторы по группам по 10 и выполнил один и тот же запрос x / 10 раз, и мои результаты были получены в раз быстрее, чем раньше, потому что индекс любит работать с 10 идентификаторами, а не 500, поэтому Тогда я увидел значительное улучшение в моих запросах благодаря выполнению оператора while.

Ответы [ 5 ]

4 голосов
/ 12 сентября 2011

Если индексы используются правильно, то почти всегда более эффективно использовать JOIN. Акцент добавлен, потому что лучшая эффективность не всегда равна наилучшей производительности.

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

Если бы я столкнулся с таким кодом в существующем проекте, я бы задал ему вопрос: проверить запрос, подумать о различных способах выполнения запроса, убедиться, что эти вещи были рассмотрены, создать научное обоснованное обоснование или против практики. Удостоверьтесь, что первоначальные разработчики проявили должную осмотрительность, так как отсутствие использования JOIN поверхностно указывает на плохую базу данных или дизайн запросов. В конце, однако, результаты говорят громко, и если все оптимизации и исправления по-прежнему приводят к более медленному объединению, чем при использовании фрагментов запроса, тогда преобладает более быстрое решение. Бенчмарк и действовать по результатам эталона; в разработке программного обеспечения не бывает случаев, когда вы должны торговать с низкой производительностью ради соблюдения произвольных правил относительно того, что вы должны или не должны делать. Самый эффективный метод - лучший метод.

2 голосов
/ 12 сентября 2011

Лучше сделать большой запрос, если индексы хорошо расположены.

Логика, стоящая за ним:

  • 1 запрос = 1 вызов сервера БД,который затем обрабатывает запрос (оптимизатор и все) и, наконец, возвращает результат.N запросов означает N обращений к базе данных, включая N обращений к оптимизатору и, в плохом случае, ввод / вывод.
  • В MySQL есть оптимизации, которые работают с соединениями.Эта оптимизация может не сработать, если вы сделаете некоторое время.

Как указано в предыдущих ответах, проверьте с EXPLAIN, есть ли что-то, что не использует индекс, если вы используете JOIN.Кроме того, вы должны проверить память, которая передается в кэш InnoDB, и память, выделенную MySQL для анализа данного запроса.Возможно, из-за этих параметров база данных работает медленнее при выполнении JOIN.

1 голос
/ 12 сентября 2011

Тест для определения фактического ответа.

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

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

Это может вызвать проблемы с производительностью, которые могут быть устранены путем разделения запросов, если:

  1. есть много дочерних элементов к одному из родителей И
  2. вы получаете много данных изparent (много столбцов или больших полей)

По моему опыту, уменьшение количества запросов почти всегда повышает производительность (я оптимизировал, комбинируя запросы гораздо больше, чем выделяя их отдельно).

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

1 голос
/ 12 сентября 2011

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

1 голос
/ 12 сентября 2011

Я бы сказал, что ответ, это зависит. Обычно я бы сказал, что объединение - это ответ, а выполнение нескольких запросов в цикле - плохая практика, однако это полностью зависит от того, что делается.

Это для тебя? Без подробной структуры таблиц и информации об индексах, а также использования внешних ключей и т. Д. Мы не можем сказать наверняка. Лучшая идея, если вы хотите проверить, это попробовать и посмотреть. Получите их запросы, ОБЪЯСНИТЕ их, напишите свой собственный и сделайте ОБЪЯСНЕНИЕ по этому поводу, посмотрите, что более эффективно.

...