parserALT
Страницы форума: ← Назад | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 600 | Дальше →

Стандартная ситуация. Насколько можно упростить?

#1Ильяс
04.09.11 13:35 / 13:36
www.parser.ru → | ответить → | в избранное →

Стандартная ситуация. Насколько можно упростить?

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

В некоторых местах нарочно привел методы "в лоб", не включая свой взгляд на оптимизацию кода.


@content[]

#условия запросов данных, например "dlina > $form:dlina AND ves = $form:ves AND cena < $form:cena"
$zapros[условия запроса]

#вычисляем количество строк, согласно запросу
$tovarCount[^int:sql{SELECT COUNT(id) FROM tovar WHERE $zapros}]

#выводим верхнюю постраничную навигацию
^pager[$tovarCount;$limit_;page]

#выбираем строки, согласно запросу
$tovar[^table::sql{SELECT id FROM tovar WHERE $zapros LIMIT ^eval($limit_*($page_-1)), $limit_}]

^tovar.menu{
	^show_tovar[$tovar.id]
}[<br>]

#выводим нижнюю постраничную навигацию
^pager[$tovarCount;$limit_;page]


@show_tovar[_id]
$_tovar[^table::sql{SELECT * FROM tovar WHERE id = $_id}]
ID: $_tovar.id<br>
Название: $_tovar.name<br>
Длина: $_tovar.dlina<br>
Вес: $_tovar.ves<br>
Цена: $_tovar.cena<br>

#2Webnode
→ Ильяс [#1] | 04.09.11 13:46 / 13:55
www.parser.ru → | ответить → | в избранное →

Можно и нужно меньше мучить базу.

Начиная с
#выбираем строки, согласно запросу

переписать так, чтобы сразу всё получить, а не дергать ^show_tovar по каждому id
Типа так:
$_tovar[^table::sql{SELECT id,name,dlina,ves,cena FROM tovar WHERE $zapros LIMIT ^eval($limit_*($page_-1)), $limit_}]
^_tovar.menu{
ID: $_tovar.id<br>
Название: $_tovar.name<br>
Длина: $_tovar.dlina<br>
Вес: $_tovar.ves<br>
Цена: $_tovar.cena<br>	
}[<br>]


И конечно, читать рекомендации в документации ;)
Не использовать select *
Моветон это.
#3Misha v.3
→ Ильяс [#1] | 04.09.11 14:13 / 14:18
www.parser.ru → | ответить → | в избранное →

«Сравнение особенностей LIKE '%string%' в MySQL 3.x и MySQL 4.x»

+ вместо pager можно посмотреть на класс для построения постраничной навигации из примеров, там сразу будут для вас рассчитаны значения для limit и offset и не надо будет такие страшные конструкции выносить в часто-читаемое место.

P.S. ух какая древность там выложена. надо будет обновить при случае.
#4Ильяс
→ Webnode [#2] | 04.09.11 14:31
www.parser.ru → | ответить → | в избранное →
В реальности функции подобные @show_tovar[] гораздо массивнее в объеме, и часто оспользуются из разных мест. Отдельно вынесено не зря.

Про Select * согласен, принято.
#5Ильяс
→ Misha v.3 [#3] | 04.09.11 15:05
www.parser.ru → | ответить → | в избранное →
На самом деле ^pager это и есть scroller, указанный вами. Я сократил его немного в примере.

А про SELECT FOUND_ROWS() не знал, учтем и это.
#6Ильяс
→ Ильяс [#1] | 04.09.11 15:12
www.parser.ru → | ответить → | в избранное →

Теперь так

На данный момент упростили до следующего

@content[]

#условия запросов данных, например "dlina > $form:dlina AND ves = $form:ves AND cena < $form:cena"
$zapros[условия запроса]

#выбираем строки, согласно запросу
$tovar[^table::sql{SELECT SQL_CALC_FOUND_ROWS id FROM tovar WHERE $zapros LIMIT ^eval($limit_*($page_-1)), $limit_}]

#вычисляем общее количество строк, согласно запросу
$tovarCount[^int:sql{SELECT FOUND_ROWS()}]

#выводим верхнюю постраничную навигацию
^pager[$tovarCount;$limit_;page]

^tovar.menu{
	^show_tovar[$tovar.id]
}[<br>]

#выводим нижнюю постраничную навигацию
^pager[$tovarCount;$limit_;page]


@show_tovar[_id]
$_tovar[^table::sql{SELECT id, name, dlina, ves, cena, text FROM tovar WHERE id = $_id}]
ID: $_tovar.id<br>
Название: $_tovar.name<br>
Длина: $_tovar.dlina<br>
Вес: $_tovar.ves<br>
Цена: $_tovar.cena<br>
#7Vint
→ Ильяс [#4] | 04.09.11 15:14
www.parser.ru → | ответить → | в избранное →
Подход верный, но можно добавить кеширование для каждого товара.
Тогда на страницах со списком товаров надо получать список id (как сейчас) и доставать инфу о товаре по одному: из кеша, если есть; из базы, если нет в кеше + тут же класть в кеш.

На страницах с товаром тоже самое только для одного id (товара).
Т.е. товар -- объект, который писан в базе, но информация о нём может быть атомарно закеширована.

Важно учесть, что скорость работы с кешем (диском) может быть меньше, чем запрос в базу данных. А в случае с интернет магазином и большим количеством товаров кеш будет занимать много места на диске и, по сути, быть избыточным звеном.
#8Ильяс
→ Vint [#7] | 04.09.11 15:24
www.parser.ru → | ответить → | в избранное →

Верное замечание

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

Каждый товар в отдельности закешировать можно, только как определить ту грань, когда кешировать уже нужно?
И не даст ли дополнительную нагрузку в .menu{} проверка условия ^if(def /cache/$tovar.id), если этих файлов тысячи?

Данные и из базы и из кеша достаются не большие, но часто.
#9Vint
→ Ильяс [#8] | 04.09.11 15:37
www.parser.ru → | ответить → | в избранное →
У парсера есть встроенный класс кеширования, который сам проверяет наличие файлов, их валидность и время жизни.

Грань определять не надо. Запрос данных происходит при обращении к объекту (товару), а будут они из кеша или из базы знает сам объект.

В menu оно, действительно, будет перебирать файлы. Но тут надо определиться, что сложнее и дороже по ресурсам в вашей системе -- тяжёлый запрос из базы или чтение кеша с диска.

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

Опять же, можно в разных местах доставать инфу о товаре по-разному: если список, то одним запросом для всех товаров (такой вариант уже предлагали), если товар -- один запрос на товар.
Но мне такая схема не нравится, они не гибкая, отсутствует объектная модель.
#10Ильяс
→ Vint [#9] | 04.09.11 17:52
www.parser.ru → | ответить → | в избранное →

Объектная модель

Как бы объектная модель выглядела на данном небольшом примере?
#11max_rip
→ Ильяс [#8] | 04.09.11 20:12
www.parser.ru → | ответить → | в избранное →

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

#12G_Z
→ Ильяс [#6] | 04.09.11 20:24 / 20:26
www.parser.ru → | ответить → | в избранное →
Не совсем про навигацию, но: @show_tovar (б-р-р название) можно переписать так, чтобы он учитывал тип переданного параметра и ожидал объект или строку/число.

Тогда ^show_tovar(123) сходит в базу и достанет товар по ID, а ^show_tovar[$table.fields] для строки уже имеющейся таблицы просто выведет значения.

Это универсальный, но не красивый подход.

Лучше так:
@getGoods[…] — выбирает список товаров, возвращает таблицу;
@getOneOfGoods[ID] — выбирает товар по ID, возвращает хэш;
@printOneOfGoods[hash] — выводит поля товара.

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

Посмотрите на код, если вперемешку идут запросы к базе (написанные снова и снова для одних и тех же действий), постобработка и вывод — это сигнал о том, что стоит навести порядок.
#13Webnode
→ max_rip [#11] | 04.09.11 21:43
www.parser.ru → | ответить → | в избранное →

Вы имеете ввиду оптимизацию базы?

Использование индексов, сортировка и устранение "дыр" в файле таблицы, памяти побольше в настройках и т.п.?
#14Sumo
→ Webnode [#13] | 04.09.11 22:22
www.parser.ru → | ответить → | в избранное →

В основном про кеш запросов MySQL...

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

Скажем, комбинация innodb-таблиц, кеш запросов, разумная денормализация данных и правильная настройка сервера делают использование кеша на стороне Парсера практически бессмысленным. Другое дело, что не все имеют собственный сервер или в состоянии грамотно настроить MySQL на виртуальных машинах. :)
#15max_rip
→ Webnode [#13] | 05.09.11 00:06
www.parser.ru → | ответить → | в избранное →

Не совсем, есть моменты которые надо максимально избегать...

Например при выборке данных за определенный период, не делать преобразование дат на стороне mysql, а отдавать именно правильную дату.
Избегать любых вычеслений на стороне сервера, например для новостей.
Новости вносятся в базу заранее и показываются, только тогда когда дата публикации прошла. Можно это сделать через
WHERE dPublish <= now()

Но такой запрос не попадет в кеш при самых правильных настройках, но если заменить now() на текущую дату в текстовом(числовом) значении, все поменяется в лучшую сторону.
#16
→ Ильяс [#10] | 05.09.11 18:32
www.parser.ru → | ответить → | в избранное →

Не очень ясен смысл объектной модели

... в данном случае.
Слишком мало для класса, по мне "обёртки" больше выйдет чем удобства.

Разве пример недостаточный - http://parser.ru/docs/lang/?lesson5.htm - очень даже неплохо написано по-моему.
#17Vint
→ Ильяс [#10] | 05.09.11 18:55
www.parser.ru → | ответить → | в избранное →

В простейшем представлении

Товар -- объект, список товаров -- коллекция объектов.
#18Vint
05.09.11 19:04
www.parser.ru → | ответить → | в избранное →

Зависит уже от сложности системы

Приведённый пример описывает список (коллекцию) как самодостаточный класс с минимальным функционалом: вывести всё, добавить запись.

Но ведь у конкретной записи может быть много методов.
На примере товара: создать (добавить) товар, удалить, изменить его свойства, перенести в другую категорию и т.д.
В этом случае теряется контекст конкретного списка, поэтому для товара должен быть свой класс, формирующий объект и предоставляющий все необходимые методы (в которых, не забываем, надо разделить логику и представление).
Страницы форума: ← Назад | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 600 | Дальше →