Пагинация - два принципа. Недостатки?

Placido

Наблюдаю
Разбираюсь с вопросом пагинации.

Общие принципы, как я понимаю, могут быть такими.

1. Подсчитывается количество записей, которые нужно вывести (SELECT COUNT() ...), определяется оффсет (зависит от номера страницы), количество рядов (сколько записей нужно вывести на странице) и количество страниц, потом запрос к базе с "SELECT ... LIMIT оффсет, кол-во рядов". После этого вывод.

2. Выборка всех записей (без LIMIT). Все остальные операции производятся уже с массивом - count(), определить оффсет, количество записей, страниц, array_slice(), вывод.

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

Хочу использовать второй метод, так как работаю с Nested Sets, и выборка из базы уже реализована в классе для работы с деревьями. Хочу узнать мнения по этому поводу.
 

c0dex

web.dev 2002-...
Команда форума
Партнер клуба
В поиске смотрел? Это было уже тут раз по 5 точно. Второй вариант не очень - в первую очередь с точки зрения производительности на больших обьемах данных.

Почему вы все так боитесь сделать один лишний запрос? Болезнь прям какая-то.
 
  • Like
Реакции: AmdY

Placido

Наблюдаю
О SQL_CALC_FOUND_ROWS читал, что простой select count() работает гораздо быстрее.

Читал, скачал, смотрел. Опять же, там работает первый принцип.

У меня просто была мысль такая. Есть свой класс работы с деревьями (Nested Sets). Выбираю наследников узла (скажем, узел - сообщение, наследники - комментарии). Хочу организовать постраничный вывод комментариев к сообщению. Пишу класс Pager. Далее создаю объект, передаю в него массив (комментарии), который нужно вывести постранично, и параметры пагинации (количество комментариев на страницу, вид строки с номерами страниц, базовую ссылку и т.п.), можно даже в качестве аргументов в конструктор. После этого получаю набор свойств с html-кодом (номера страниц со ссылками) и обрезанный массив. Что-то вроде такого:
PHP:
$pager = new Pager($messages_array, $parameters);
$pager->messages //здесь готовый к выводу обрезанный массив
$pager->pages //здесь html-строка с номерами страниц
После этого $pager->messages и $pager->pages вывожу в шаблон.

Я понимаю, что есть готовые решения, но я сейчас учусь и пытаюсь разобраться с самими принципами. Написал класс для работы с древовидными структурами - разобрался с PDO, транзакциями, перегрузкой свойств и методов. Написал класс регистрации - разобрался с сессиями, куками, почтой. Сейчас вот пагинация. Разобравшись с этим вопросом, возможно, тоже вынесу что-то полезное.
 

c0dex

web.dev 2002-...
Команда форума
Партнер клуба
Nested Sets мягко скажем вообще хреново подходят под хранение часто обновляемых наборов данных, типа комментариев. Так как при вставке узла там идут множественные перерасчеты листьев дерева. Тебе сейчас не надо думать о быстродействии, иначе это у тебя превратится в болезнь, когда ты еще вместо двойных кавычек везде начнешь вставлять одинарные и прочее. Плюс структуру дерева я бы хранил отдельно от данных, в твоем случае не фатально, а вот в случае магазина какого нибудь или еще чего-то подобного, вместе хранить не стоит.
 

Placido

Наблюдаю
Спасибо за совет. Комментарии обновляться будут реже, чем выбираться. Да и вряд ли, что у какого-то сообщения будет сотня тысяч комментариев.
Второй вариант не очень - в первую очередь с точки зрения производительности на больших обьемах данных..
Запрос сделать не страшно. Но вот по поводу производительности... Ведь при выборке идет запрос вида SELECT ... ORDER BY ... LIMIT. И его производительность будет даже ниже такого же запроса,но без LIMIT, так как сортировка осуществляется до LIMIT. В чем выигрыш?
 

A1x

Новичок
LIMIT дает выигрыш в кол-ве данных передаваемых между базой и пхп
 

MiksIr

miksir@home:~$
count вообще штука медленная... особо на транзакционных моделях. Ну было по крайней мере, там все какие-то оптимизации рождали рождали... давно не вникал. Но я к чему... При огромных объемах данных мало кого волнует count. Если уж очень хочется, у каждой базы есть оценочные способы получения данных. А если забыть про count, то можно попробовать такое решение - выбираете столько страниц, сколько рисуете на пейджере... ну или на одну больше, что бы точно знать - есть ли там еще данные, для отрисовки пейджера.
 

С.

Продвинутый новичок
О SQL_CALC_FOUND_ROWS читал, что простой select count() работает гораздо быстрее.
"простой select count()" рабоатет супер быстро дял подсчета обчего количества записей. Если же есть WHERE, то никакого выигрыша по сравнению с SQL_CALC_FOUND_ROWS нет. Наоборот SQL_CALC_FOUND_ROWS экономнее.
 

С.

Продвинутый новичок
Если иметь представление, что БД работает не на святом духе, а по определенным принципам, то все следует из здравого смысла. Которое из трех мной высказанных утверждений тебя смутило?
 

Фанат

oncle terrible
Команда форума
Хочу использовать второй метод, так как выборка из базы уже реализована
Во всем этом адовом топике вот это - самая жесть.

"Чем лучше есть суп - ложкой или вилкой? Я думаю - вилкой, она у меня от компота осталась. А ложку еще искать надо"

А если серьёзно, то второй метод как "общий" рассматриваться не может. Как исключение, когда заранее известно, что данных будет немного - максимум десяток страниц - ну можно его применять. Вопрос в этом случае только один - зачем автору вообще нужна датабаза, не проще было все в текстовом файле хранить?

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

Для комментариев, кстати, отлично работает materialized path. У меня форум с 25 000 сообщений нормально показывает деревце. Рубит страницы прямо посередине обсуждения.
 

MiksIr

miksir@home:~$
Если иметь представление, что БД работает не на святом духе, а по определенным принципам, то все следует из здравого смысла. Которое из трех мной высказанных утверждений тебя смутило?
Проблема в том, что нужно не только иметь представление о том как работает, но и знать как работает. По крайней мере для утверждений без цифр.
Вот вам цифры
count(*) по всей таблице - 2.94 sec
count(*) where по полю без индекса - 3.72 sec
count(*) и тот же where по полю с индексом - 1.51 sec
SQL_CALC_FOUND_ROWS * where по полю без индекса limit 1 - 4.44 sec
SQL_CALC_FOUND_ROWS * и тот же where по полю c индексом limit 1 - 4.80 sec
Беретесь своим здравым смыслом объяснить и сопоставить с вашими же утверждениями?
 

С.

Продвинутый новичок
count(*) по всей таблице просто прочитает количество записей из служебнай информации базы. По сути это 0 сек.

count(*) where переберет k записей, удовлетворающих where. В данном контексте совершенно не важно с индексом или без.

select where limit m,n переберет m+n записей. Опять так и без относительно индекса.

select SQL_CALC_FOUND_ROWS where limit m,n переберет k записей.

Теперь считаем, что больше k или k+m+n?
 

MiksIr

miksir@home:~$
count(*) по всей таблице просто прочитает количество записей из служебнай информации базы. По сути это 0 сек.
Вот я и говорю, что если иметь представление, что mysql != myisam, то внезапно смысл оказывается не очень здравым.
Т.е. реальные цифры объяснять не беретесь?
 

Placido

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

AmdY

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

fixxxer

К.О.
Партнер клуба
С.
Вот что ты с умным видом лезешь с заявлениями про никому не нужный муисам, который вообще за базу данных считать сложно? mysql == innodb сейчас. Почитай про версионные РСУБД и как они работают.
 
Сверху