[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Один запрос к БД. Всегда.
b00tanik
Когда думаешь о том, как оптимизировать работу своих скриптов, понимаешь, что узким местом является зачастую работа с базой. На страницу приходится 10-15 запросов, которые в принципе можно собрать в один, используя некоторый синглтон, реализующий внутри себя функционал, поволяющий собрать внутри себя все данные из базы, и отдать это скрипту до его выполнения.

Возьмем это за идеализированную модель.

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

Итак, вопрос к знатокам: Существуют ли решения, максимально близкие к идеализированной модели, но в тоже время обладающие хоть какой-то гибкостью.






Спустя 53 минуты, 15 секунд (30.04.2010 - 12:46) sergeiss написал(а):
Цитата (b00tanik @ 30.04.2010 - 12:53)
в теории можно сделать супер-запрос, который бы заполнял супер-класс, а потом из этого класса брались бы данные.

То есть, ты хочешь получить все данные из БД? Только не говори, что это не так smile.gif Ты же не знаешь заранее, что именно захочет получить юзер. Поэтому ты или делаешь запрос сообразно его требованиям, что противоречит описанному тобой пожеланию, или гружишь из БД все данные, в надежде, что они понадобятся юзеру.
А вот представь, что у тебя 200 ГБ (Двести Гига Байт) данных, и то, не считая индексов. И чё? Ты их будешь грузить в скрипт? А нафига тогда вообще было создано такое понятие, как БД и всякие запросы к ней?

Спустя 1 час, 1 минута, 10 секунд (30.04.2010 - 13:47) Gabriel написал(а):
b00tanik
не всегда один запрос к БД будет быстрее 2. оптимизировать есть смысл только в том случае, когда ты уверен, что получишь те-же данные с меньшей затратой времени и ресурсов, а если это не так то это получится ДЕоптимизация какая-то которая в свою очередь не нужна не программеру ни юзеру, ни серверу.

Спустя 3 часа, 45 минут, 2 секунды (30.04.2010 - 17:32) b00tanik написал(а):
Цитата (sergeiss @ 30.04.2010 - 09:46)

То есть, ты хочешь получить все данные из БД? Только не говори, что это не так smile.gif

Это не так:) Я хочу получить из БД только те данные, которые мне нужны для данной страницы. Одним большим запросом, с вложенными селектами и тд.

В этом и состоит сложность - чтобы оптимизировать на получение только нужных данных а не всех.

ЗЫ: 200 Гб таскать мускулем? Знаете толк в извращениях. Для таких объемов уже свои решения, таже касандра например.

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

Цитата (Gabriel @ 30.04.2010 - 09:46)

не всегда один запрос к БД будет быстрее 2


Были ли исследования по данному вопросу или это из личного опыта. Если из опыта, то можете в паре слов описать, когда выгодней 2 запроса.

Спустя 24 минуты, 26 секунд (30.04.2010 - 17:57) FatCat написал(а):
Цитата (b00tanik @ 30.04.2010 - 18:32)
Если из опыта, то можете в паре слов описать, когда выгодней 2 запроса.

Пример из реальной практики движка этого форума.
При отображении списка тем форума требуется пометить темы, содержащие сообщения того, кто просматривает страницу.
Если заджойнить к таблице топиков таблицу постов, можно получить все данные в один запрос, время выполнения примерно 0.05 секунды.
Если забрать только таблицу топиков и в цикле по топикам гонять запросы к таблице постов, получается 51 запрос, но суммарное время этих запросов меньше 0.01 секунды.

Спустя 6 минут, 2 секунды (30.04.2010 - 18:03) FatCat написал(а):
Кстати, у MySQL есть свои средства оптимизации запросов. Нужно только воспользоваться.
При коннекте получить идентификатор соединения, например так:
$this->connection_id = mysql_connect(...);

При отправке запросов, отправлять их в том же идентификаторе:
$this->query = mysql_query($the_query, $this->connection_id);

И в конце закрывать соединение:
mysql_close($this->connection_id);

Спустя 9 минут, 29 секунд (30.04.2010 - 18:12) vasa_c написал(а):
А если не указывать connection_id, запросы будут отправляться в каком-то другом идентификаторе?

Спустя 43 минуты, 5 секунд (30.04.2010 - 18:55) sergeiss написал(а):
Цитата (b00tanik @ 30.04.2010 - 18:32)
ЗЫ: 200 Гб таскать мускулем? Знаете толк в извращениях.

Я как раз по столько не таскаю smile.gif Да и БД - Постгре.
Я выбираю только те данные, что мне нужны. Вот только фенька в том, что в начале страницы может быть неизвестно, что же именно надо будет вытащить! Это определяется в процессе обработки данных. В зависимости от каких-то условий подключаются те или иные скрипты, в которых могут делаться еще какие-то выборки.
Поэтому я и говорю, что либо данные будут выбираться по мере необходимости в несколько запросов, либо надо загрузить всё, и уже в ПХП-скрипте выбирать, что же нужно.
И еще - скорость выполнения запросов (в первую очередь для сложных запросов) зависит также и от того, насколько правильно прописаны индексы. И насколько правильно построен сам сложный запрос. Разница может быть в десятки-сотни-тысячи раз.

Спустя 4 часа, 19 минут, 4 секунды (30.04.2010 - 23:15) kirik написал(а):
b00tanik
Уменьшение количества запросов не есть оптимизация. Запрос отправляется серверу БД за доли долей секунды, а вот процесс выборки уже может происходить довольно продолжительное время. Именно по этому появляются "too many connections" - 100 сложных запросов пришли, сервер их отрабатывает, а 101-й запрос уже взять не может.

Спустя 22 минуты, 19 секунд (30.04.2010 - 23:37) FatCat написал(а):
Цитата (vasa_c @ 30.04.2010 - 19:12)
если не указывать connection_id, запросы будут отправляться в каком-то другом идентификаторе?

Не знаю.
Была ситуация, когда чат валил sql-сервер в перезагрузки. Переписал движок чата, посадил все запросы на один connection_id, падения прекратились. Может совпало, другого опыта не было.

Спустя 18 часов, 41 минута, 50 секунд (1.05.2010 - 18:19) Nikitian написал(а):
Цитата (FatCat @ 30.04.2010 - 20:37)
Может совпало, другого опыта не было.

Совпало. Если не указывать явно идентификатор соединения, то будет использоваться последний использованный (:
Цитата

Именно по этому появляются "too many connections" - 100 сложных запросов пришли, сервер их отрабатывает, а 101-й запрос уже взять не может.

Не так. Too many connections появляется, когда количество одновременных соединений с бд превышает параметр max_connections. Из доков. Именно поэтому всем советую коннектиться к бд не в самом начале скрипта, а когда это действительно нужно. Mysql_connect в __construct() не есть гуд, как и игнорирование mysql_close(). А уж если wait_timeout и connect_timeout имеют большое значение, то too many connections долго ждать не приходится - очень неприятная ошибка, т.к. появляется неожиданно, когда в раскрутку начинают вкладываться, соответственно деньги просто вылетают в трубу.

Спустя 3 часа, 46 минут, 49 секунд (1.05.2010 - 22:05) FatCat написал(а):
Цитата (Nikitian @ 1.05.2010 - 19:19)
Если не указывать явно идентификатор соединения, то будет использоваться последний использованный (:

А если у меня одновременно сотни посетителей? обычная нагрузка в вечернее время составляет 2500 генераций страниц в минуту; это больше 40 в секунду.
Мне кажется, что запросы от разных генераций страниц разными посетителями будут или пересекаться, или сваливаться в один айдишник.


Цитата (Nikitian @ 1.05.2010 - 19:19)
Too many connections появляется, когда количество одновременных соединений с бд превышает параметр max_connections.

Ни разу не видел такого от MySQL, нверное с хостерами везло, но и сам не усердствовал в "оптмизациях". Если в апаче не переусердствовать, то он первым даст отлуп, не заваливая MySQL-сервер.

Спустя 3 часа, 55 минут, 53 секунды (2.05.2010 - 02:01) Nikitian написал(а):
Цитата (FatCat @ 1.05.2010 - 19:05)

А если у меня одновременно сотни посетителей? обычная нагрузка в вечернее время составляет 2500 генераций страниц в минуту; это больше 40 в секунду.
Мне кажется, что запросы от разных генераций страниц разными посетителями будут или пересекаться, или сваливаться в один айдишник.

Вам кажется. Такое ощущение, что вы от пятницы ещё не отошли )) Идентификатор запроса - это ресурс, а не какой-то псевдослучайный числовой идентификатор. Он не повторяется, т.к. выдаётся конкретному потоку веб-сервера b убивается сборщиком мусора. Повторяться он не может, т.к. выдаётся в рамках вебсервера и в этих рамках он уникален независимо от количества одновременных потоков. Если мне не изменяет память (а она у меня верная), после отправки запроса к серверу бд не происходит разрыва соединения, а оно висит, пока не будут возвращены данные. Отсюда очевидно, что пересекаться там нечему. Если же соединение с клиентом потеряно, то результат запроса не передаётся никуда вообще. Не может быть такой ситуации, что один отправил запрос и отвалился, а другой подключился и принял ответ - вновь подключённый будет новым клиентом для бд, даже если это тот же поток и скрипт подключается снова. Единственное, что не могу объяснить: как связка: php-mysql будет себя вести на одновременных запросах одного скрипта при использовании одновременных потоков и одного соединения с бд, но уверен, что там всё схвачено )
Цитата (FatCat @ 1.05.2010 - 19:05)

Ни разу не видел такого от MySQL, нверное с хостерами везло, но и сам не усердствовал в "оптмизациях". Если в апаче не переусердствовать, то он первым даст отлуп, не заваливая MySQL-сервер.

Увеличиваете указанные ранее параметры, уменьшаете max_connections и вуаля.
Цитата
Получение ошибки Too many connections при попытке соединиться с MySQL означает, что уже есть max_connections клиентов, соединившихся с сервером mysqld.

Спустя 12 часов, 10 секунд (2.05.2010 - 14:02) FatCat написал(а):
Цитата (Nikitian @ 2.05.2010 - 03:01)
Единственное, что не могу объяснить: как связка: php-mysql будет себя вести на одновременных запросах одного скрипта при использовании одновременных потоков и одного соединения с бд, но уверен, что там всё схвачено )

Когда разбирались с обвалами SQL-сервера, логировали его работу в файл.
Файл был такого вида:
[99]SELECT .....
[
91]SELECT.....
[
97]SELECT.....
[
99]SELECT .....
В квадратных скобках - идентификатор сессии.
Я еще программку писал для разбора этих логов, чтобы посмотреть последовательность запросов именно той сессии, на которой произошло падение сервера, и посмотреть прошлые запросы в этой сессии.
Оказалось, что в самих запросах не было ничего криминального, падения происходили при накоплении сессий, не завершившихся командой mysql_close

Спустя 35 минут, 27 секунд (2.05.2010 - 14:37) Nikitian написал(а):
mysql_close() разумеется обязателен, несмотря на допущения в его неиспользовании из доков. Причём завершать соединение надо не в конце скрипта, а когда бд уже не требуется, но есть ещё чем занять скрипт Например, самый подходящий момент - перед генерацией шаблонизатором.

Спустя 6 часов, 50 минут, 40 секунд (2.05.2010 - 21:28) kirik написал(а):
Цитата (Nikitian @ 1.05.2010 - 10:19)
Не так. Too many connections появляется, когда количество одновременных соединений с бд превышает параметр max_connections. Из доков.

Эмм.. Я как-то по-другому сказал? smile.gif Число 100 я взял для примера (хотя до MySQL 5.1.15 max_connections по дефолту было именно 100).

Спустя 1 час, 14 минут, 13 секунд (2.05.2010 - 22:42) Nikitian написал(а):
kirik
Разница в том, что мускуль может и не выполнять никаких действий. С ним просто соединились, но запросы не шлют. Я к тому веду, что делать в начале скрипта mysql_connect(), и забывать или делать в конце mysql_close() - не самый правильный шаг. Например из базы дёргаются какие-то данные, которые потом курлом шлются во внешку, а таймаут соединения стоит скажем 20 секунд. Внешний сервер отвалился, курл терпеливо ждёт 20 секунд и потом возвращает ошибку, но все эти 20 секунд мускуль так же ждал запросов и -1 соединение держал, хотя после отправки данных мускуль нам был уже не нужен. Т.е. если за 20 секунд открыли 100 страниц, а это всего лишь 5 страниц в секунду, то всем остальным показали "Mysql not connected". И заметьте, здесь не делалось тяжёлых запросов в бд, не было сверх-посещаемости wink.gif

Спустя 29 минут, 17 секунд (2.05.2010 - 23:11) kirik написал(а):
Nikitian
Окей, если "too many connections" привязана только к превышению значения max_connections, то как можно объяснить подобный случай?

Есть сайт (соцсеть), онлайн где-то 500-700 человек постоянный. Так вот, стала частенько вылетать ошибка "too many connections". Глянул "лог медленных запросов" - некоторые выполнялись аж под 30 секунд! Решено было прикрутить мемкэш - прикрутили, ошибка стала появляться намного реже. mysql_connect никуда не девался, директиву max_connections не изменяли (да и вообще настройки mysql не трогали). Когда онлайн поднялся до 900 человек, опять повалили "too many connections".. Тогда уже решили попробовать расширить память на 1ГБ и соответственно выделить mysql'у побольше. Так ошибка пропала, и теперь появляется очень редко.

Как я предположил, вероятно это было связано с тем, что mysql не закроет соединение, пока не вернет запрос данному клиенту. Тоесть превышение количества одновременных подключений возможно не только из-за того что реально 101 юзер просто обрататся к БД одновременно..

Спустя 9 минут, 41 секунда (2.05.2010 - 23:21) FatCat написал(а):
Цитата (Nikitian @ 2.05.2010 - 23:42)
Т.е. если за 20 секунд открыли 100 страниц, а это всего лишь 5 страниц в секунду

Эм...
У меня сейчас в httpd.conf установлено
MaxClients 15
- это втрое выше дефолтных "5".
Если скрипт 20 секунд ждет курла, 15 клиентов будут наблюдать страницу с ожиданием ответа, а 16-й просто получит 502-ю страницу. Или не так?

Спустя 10 минут, 16 секунд (2.05.2010 - 23:31) FatCat написал(а):
Цитата (kirik @ 3.05.2010 - 00:11)
решили попробовать расширить память на 1ГБ и соответственно выделить mysql'у побольше

Ох, подозреваю, что над одной проблемой маемся, только с разными ее проявлениями.
Я сейчас ковыряюсь с проблемой "холодных стартов": например, не было вообще посетителей, захожу на сайт, и... 10-15 секунд ожидания.
При этом дебагер показывает время генерации страницы php несчастные 0,03-0,05 секунды. Обновляю страницу, и получаю с теми же цифрами на дебаггере, но ответ моментально.
И время задержки "холодного старта" растет прямо пропорционально размеру одной из таблиц в БД...
Думал, что может всю таблицу считывает с харда и в оперативу грузит; проверил в локалке на денвере, вроде ни фига, таблица хорошо за сотню Мб, а прирост потребления оперативы виндой составляет всего пару мегабайт...

Спустя 20 минут, 24 секунды (2.05.2010 - 23:52) Nikitian написал(а):
Превысить max_connections можно как долбёжкой медленными запросами, так и удержанием неактивных соединений.
Цитата
как можно объяснить подобный случай?

500 человек онлайн - это сколько страниц в секунду? Если соцсеть, то 500 человек за 5 минут (обычно онлайнеров считают именно за это время) - это 1.5 человека в секунду. За 5 минут каждый откроет минимум 5 страниц, итого округлим до 10 страниц в секунду. Допустим одна страница - это 10 запросов. Итого 100 запросов в секунду. Отсюда видим, что был предел и любой запрос, отрабатывающий больше секунды начинал строить очередь ожидания, увеличивая очередь в геометрической прогрессии. А пропала ошибка из-за того, что мускуль быстрее стал отрабатывать ваши запросы (памяти больше -> больше таблиц в памяти помещалось -> быстрее отрабатываются запросы) и очередь не строится.
Вроде правильно посчитал.
Вообще зря не трогали настройки мускуля. Очень советую админов нагружать оптимизацией софта. Если админ ненагружаем, то как вариант, mysqltuner - интересные вещи творит.

Короче, ошибка too many connections возникает и от тяжёлых запросов, и от большого количества одновременных открытий соединений с бд.


Цитата
Я сейчас ковыряюсь с проблемой "холодных стартов": например, не было вообще посетителей, захожу на сайт, и... 10-15 секунд ожидания.
При этом дебагер показывает время генерации страницы php несчастные 0,03-0,05 секунды.

Может это проблема днса? Либо MaxSpareServers сильно мал, а мускуль забивает всю память кэшем этой большой таблицы. Соответственно когда апачу надо запустить нового клиента часть мускульной оперативки свопится и заполняется дополнительным процессом апача и он отрабатывает именно за указанное время, ведь раньше, чем запустится дочерний процесс статистика дебаггера считаться не будет?

Цитата
MaxClients 15
Если скрипт 20 секунд ждет курла, 15 клиентов будут наблюдать страницу с ожиданием ответа, а 16-й просто получит 502-ю страницу. Или не так?

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

Спустя 1 день, 11 часов, 11 минут, 21 секунда (4.05.2010 - 11:03) b00tanik написал(а):
Решил проблему по-другому. Вот топик с новой темой
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.