Задавайте вопросы, мы ответим
Вы не зашли.
Страниц: 1 2
И если да, то в чем это состоит?
Неактивен
Если оптимизированный запрос с лимитом (т.е. останавливается выбрав нужное кол-во сток), то при SQL_CALC_FOUND_ROWS он сделает всю выборку, а потом отбросит лишнее. В общем, плохо.
Неактивен
Порой отдельный запрос с count(*) может быть намного быстрее, если удастся использовать покрывающий индекс.
Неактивен
А для каких целей ты его хочешь использовать?
Неактивен
Порой отдельный запрос с count(*) может быть намного быстрее, если удастся использовать покрывающий индекс.
То есть? Ты хочешь сказать, что один запрос
As soon as MySQL has sent the required number of rows to the client, it aborts the query unless you are using SQL_CALC_FOUND_ROWS.
Засчет того, что запрос от LIMIT и без SQL_CALC_FOUND_ROWS становится легче, а простой COUNT(*) хорошо ляжет на индексы, два запроса могут действительно оказаться быстрее одного. Пожалуй, ты прав.
А для каких целей ты его хочешь использовать?
Для решения одной из самых часто встречающихся в веб-приложениях задач - узнать общее количество записей, подохдящее под какие-либо условия, когда нужны данные лишь части из них. Характерный пример - разбивка списка на страницы.
Заковыка тут вот в чем.
Обычно удобно иметь некую функцию/метод, которая принимает какие-то параметры-ограничения будущего списка, а в ответ возвращает часть этого списка (обычно не больше какого-то фиксированного числа строк). Тут все просто. Вот только бывает нужно еще и узнать, а сколько строк всего, помимо этих выбранных. И вот тогда начинаются неудобства: где это число передать? Пихать в результат работы функции неудобно, потому что тогда надо возвращать не просто список строк, а массив, где в одном элементе - список, в другом - общее количество. В результате вместо простого и понятного выражения вида
Неактивен
Случай, когда SQL_CALC_FOUND_ROWS влияет на производительность не редкий. Дело в том, что запросы, о которых идет речь обычно тяжелые, содержат одновремененно where и order by, иногда JOIN, результат которого используется в ORDER BY. Чаще всего LIMIT начинается от небольшого значения (первую страницу смотрят на порядок чаще, чем 20-ую), поэтому естественная оптимизация - сортировка по индексу. И это как раз случай, когда он прекратит исполнять запрос, набрав достаточно значений. При подсчете count(*) сортировать не нужно, а иногда не нужно и джойнить.
Неактивен
Имхо, SQL_CALC_FOUND_ROWS это зло и то, что люди считают это традиционным средством - опасное заблуждение.
Вот представь, что ты выводишь список статей с разбивкой на страницы. В случае SQL_CALC_FOUND_ROWS будет выбраны текстовые поля (анотация, название, автор) для всей сотни страниц.
На практике запросы с лимит идут с сортировкой и как правило при добавлении SQL_CALC_FOUND_ROWS будут сильно ухудшены, т.е. ситуация распространенная.
Что же касается конкретно разбивки на страницы, то можно делать выборку limit N+1 и две ссылки (следующая и последняя). При этом не обязательно знать сколько всего записей.
Неактивен
Случай, когда SQL_CALC_FOUND_ROWS влияет на производительность не редкий.
Он не редкий по сравнению с общим количеством запросов, нуждающихся в оптимизации.
По сравнению же с количеством всех запросов, которые обычно делает приложение, он весьма редкий
В общем, архитектурно возможность такая есть. Описывать ее в коде как-то пока нет настроения. Я ее допишу, когда понадобится кому-то, кто будет пользоваться.
Неактивен
Вот представь, что ты выводишь список статей с разбивкой на страницы. В случае SQL_CALC_FOUND_ROWS будет выбраны текстовые поля (анотация, название, автор) для всей сотни страниц.
Нет, потому что я предписываю сначала выбирать нужные id, а потом для них данные.
На практике запросы с лимит идут с сортировкой и как правило при добавлении SQL_CALC_FOUND_ROWS будут сильно ухудшены, т.е. ситуация распространенная.
Это если, убрав SQL_CALC_FOUND_ROWS, ты получишь хороший запрос по ключу. В противном же случае последующий COUNT(*) будет не лучше, чем SQL_CALC_FOUND_ROWS.
Все же я считаю это редким случаем (см. мой ответ выше на сообщение rgbeast).
Что же касается конкретно разбивки на страницы, то можно делать выборку limit N+1 и две ссылки (следующая и последняя). При этом не обязательно знать сколько всего записей.
Вот это вообще не понял.
Мне нужно разбить записи по 20 штук на страницу. Как я узнаю, сколько мне ссылок выводить на страницы, если не знаю, сколько у меня всего записей и, таким образом, не знаю, сколько они занимают страниц?
Неактивен
LazY написал:
Что же касается конкретно разбивки на страницы, то можно делать выборку limit N+1 и две ссылки (следующая и последняя). При этом не обязательно знать сколько всего записей.
Вот это вообще не понял.
Мне нужно разбить записи по 20 штук на страницу. Как я узнаю, сколько мне ссылок выводить на страницы, если не знаю, сколько у меня всего записей и, таким образом, не знаю, сколько они занимают страниц?
Можно выводить не список всех страниц, а ссылки вида "следующая", "последняя". В этом случае не нужно знать точное число, а достаточно сделать limit 21 (или limit 41, чтобы быть уверенным, что ссылки "следующая" и "последняя" не приведут на одну страницу).
Неактивен
LazY написал:
Он не редкий по сравнению с общим количеством запросов, нуждающихся в оптимизации.
По сравнению же с количеством всех запросов, которые обычно делает приложение, он весьма редкий
Среди погибших в ДТП многие переходили на красный свет, но среди общего числа переходящих на красный свет мало кто гибнет. То есть ты с не очень высокой вероятностью создаешь запрос, который может убить всю производительность. Иначе говоря - если у кого-то из твоих заказчиков будут проблемы производительности, то скорее всего из-за этого (запрос будет лидировать по числу обращений в службу поддержки).
Добавление SQL_CALC_FOUND_ROWS делает невозможным оптимизацию этого запроса, если он станет медленным. Конечно нужно избегать преждевременной оптимизации, но тут не такой случай, использование SQL_CALC_FOUND_ROWS - само по себе является оптимизацией числа выполняемых запросов.
Неактивен
vasya написал:
Можно выводить не список всех страниц, а ссылки вида "следующая", "последняя"
Когда можно, а когда - и нет.
Интерфейсные решения не должны в такой степени зависеть от проблем серверных механизмов, поэтому гарантированно избавиться от необходимости знать общее количество строк нельзя.
Неактивен
rgbeast написал:
...ты с не очень высокой вероятностью создаешь запрос, который может убить всю производительность. Иначе говоря - если у кого-то из твоих заказчиков будут проблемы производительности, то скорее всего из-за этого (запрос будет лидировать по числу обращений в службу поддержки).
Добавление SQL_CALC_FOUND_ROWS делает невозможным оптимизацию этого запроса, если он станет медленным.
Если так случится - дополню функцию так, чтобы была вместо SQL_CALC_FOUND_ROWS использовать COUNT (о чем я писал выше), это несложно.
rgbeast написал:
Конечно нужно избегать преждевременной оптимизации, но тут не такой случай, использование SQL_CALC_FOUND_ROWS - само по себе является оптимизацией числа выполняемых запросов.
Вот это я совсем не понял. Само использование SQL_CALC_FOUND_ROWS является преждевременной оптимизацией? Или отказ от SQL_CALC_FOUND_ROWS является преждевременной оптимизацией?
Неактивен
LazY написал:
Интерфейсные решения не должны в такой степени зависеть от проблем серверных механизмов, поэтому гарантированно избавиться от необходимости знать общее количество строк нельзя.
Если речь о высоконагруженных системах, то технические требования накладывают очень серьезные требования на интерфейс.
LazY написал:
Вот это я совсем не понял. Само использование SQL_CALC_FOUND_ROWS является преждевременной оптимизацией? Или отказ от SQL_CALC_FOUND_ROWS является преждевременной оптимизацией?
Использование SQL_CALC_FOUND_ROWS - это замена двух запросов на один изощренный. Оправдание в том, что якобы иногда это экономит немного времени. Поэтому - преждевременная оптимизация, так как по факту получается, что есть риски.
Неактивен
rgbeast написал:
Если речь о высоконагруженных системах, то технические требования накладывают очень серьезные требования на интерфейс.
Если уже после утверждения бюджета -то да
rgbeast написал:
Использование SQL_CALC_FOUND_ROWS - это замена двух запросов на один изощренный. Оправдание в том, что якобы иногда это экономит немного времени.
Т.е. ты считаешь, что по умолчанию нужно использовать COUNT(*), а не SQL_CALC_FOUND_ROWS?
Я подчеркиваю, что речь сейчас обо всех случаях, а не только о высоконагруженных системах (поскольку я пишу инструмент, который можно использовать и там, и там).
Неактивен
LazY написал:
Если уже после утверждения бюджета -то да
Бывает, что ограничения не зависят от бюджета. Например, в онлайн-играх (MMORPG) есть интерфейсные вещи, которые вообще нельзя реализовать.
LazY написал:
Т.е. ты считаешь, что по умолчанию нужно использовать COUNT(*), а не SQL_CALC_FOUND_ROWS?
Я подчеркиваю, что речь сейчас обо всех случаях, а не только о высоконагруженных системах (поскольку я пишу инструмент, который можно использовать и там, и там).
Если речь не идет о производительности, то я бы делал два запроса. Логически это оправдано, а оптимизацию доверим серверу. Если потребуется оптимизация, то нужно иначе рассматривать вопрос.
Неактивен
rgbeast написал:
Бывает, что ограничения не зависят от бюджета. Например, в онлайн-играх (MMORPG) есть интерфейсные вещи, которые вообще нельзя реализовать.
Ну, это конечно.
Я исключительно веб-приложения имел в виду.
rgbeast написал:
Если речь не идет о производительности, то я бы делал два запроса. Логически это оправдано, а оптимизацию доверим серверу.
Тогда уж лучше наоборот сделать через SQL_CALC_FOUND_ROWS, а оптимизацию доверить серверу , как это и закладывалось разработчиками MySQL (иначе SQL_CALC_FOUND_ROWS не было бы как такового и не было бы практики его использования).
Так получается удобней с точки зрения клиентского приложения: у тебя один запрос, и WHERE и пр. локализованы в одном месте, отдельно остается только вызвать FOUND_ROWS(). В случае же с COUNT(*) запрос нужно очищать от ORDER BY и LIMIT и выполнять отдельно в другом месте в измененном виде. Такой код менее удобно поддерживать, чем написанный на основе SQL_CALC_FOUND_ROWS.
Неактивен
Между прочим, в прошлом мы сами советовали пользоваться SQL_CALC_FOUND_ROWS
http://sqlinfo.ru/forum/viewtopic.php?id=992
Теперь, получается, не советуем? Что изменилось?
Неактивен
За пять лет мы набрались опыта и стали мудрее
Неактивен
К сожалению у MySQL на один запрос только один план. И сказать ему SQL_CALC_FOUND_ROWS - значит сказать решить две разные задачи одним и тем же методом. Становится понятно, что реализация от MySQL сделана настолько грубо, что рекомендовать эту фичу в продакшене нельзя. Тут, видимо, какая-то взаимная практика - люди не используют, а вендор не развивает.
Неактивен
rgbeast написал:
Становится понятно, что реализация от MySQL либо сделана настолько грубо, что рекомендовать эту фичу в продакшене нельзя.
Прямо уж так и нельзя? Я этим пользуюсь много лет и проблем не знаю (и не только я). Это факт.
Наверное, могут быть проблемы с производительностью в каких-то случаях, но в остальных случаях (которых больше) отказ от более удобного с точки зрения API метода в пользу теоретически более производительных, но громоздких вариантов выглядит как яркий пример преждевременной оптимизации.
Примечание: само наличие SQL_CALC_FOUND_ROWS не стоит рассматривать как преждевременную оптимизацию, потому что это, в первую очередь, интерфейсный инструмент, а не оптимизация как таковая. Да и, в любом случае, это часть MySQL. Разработчик клиентского приложения с точки зрения преждевременной оптимизации рассматривает свой код, а не механизм базы данных.
rgbeast написал:
люди не используют, а вендор не развивает
Ну как же не используют? Набери SQL_CALC_FOUND_ROWS в поисковике - увидишь, сколько упоминаний.
А вендор не развивает, возможно, потому, что не такая уж частая проблема (именно проблема, а не случай использования). Оптимизации подзапросов в SELECT вон сколько дожидались, а проблема была более очевидная.
Неактивен
На мой взгляд SQL_CALC_FOUND_ROWS - оптимизация, так как это не стандарт SQL, а фича именно MySQL. Его использование делает приложение непереносимым.
Неактивен
Ох, какой вы флейм развели, давайте я в нем тоже поучаствую
1. Миш, ты гордишься тем, что ты программист. Посмотри на эту штуку не с точки зрения
«написали, давайте заюзаем», а с точки зрения здравого смысла. CALC_FOUND_ROWS
работает так: считает количество строк, которое выведет этот запрос без LIMIT. Т.е. берет
первую строку, считает, берет вторую строку, считает, берет третью строку, считает, и т.д.
2. SELECT COUNT(*) подвержен некоторым оптимизациям. Например, в случае с MyISAM,
он сразу знает ответ. В случае с InnoDB он вынужден бежать по PK, но он никогда не
заглядывает в листья за данными.
3. Наконец, в самом худшем случае, когда и тот, и другой алгоритмы вынуждены просма-
тривать и считать строки, случай с COUNT(*) не требует передачи самих данных в считалку
(а COUNT_FOUND_ROWS требует, т.к. иначе не умеет).
По сути, COUNT(*) отдельно работает всегда не медленнее, чем вариант «в лоб».
Неактивен
paulus написал:
По сути, COUNT(*) отдельно работает всегда не медленнее, чем вариант «в лоб».
Другими словами, SQL_CALC_FOUND_ROWS использовать не нужно вообще никогда?
Так, коллеги. Давайте ближе к делу.
Сейчас интерфейсный вопрос решается. Потом результатом предстоит пользоваться постоянно, так что давайте подумаем.
Я пытаюсь понять, как сделать лучше.
Вариант первый. По умолчанию используется SQL_CALC_FOUND_ROWS (как планировалось изначально, поскольку я считал этот вариант стандартным). В этом случае варианты такие:
Неактивен
Вариант по умолчанию должен, разумеется, не считать count вообще. Сигнализировать
о том, как считать количество, можно в том же параметре, который передает количество
строк, которое нужно передать. Ну то есть я, если честно, не понимаю, почему мы это
обсуждаем на форуме о MySQL
getlist(array('user_id' => 17), array('limit' => 100, 'count' => 1))
Миш, ты пишешь ORM, возьми готовый какой-то.
Неактивен
Страниц: 1 2