Тормоз при запросах к БД

Для теста производительности движка импортировал базу данных [4092 юзера, 4113 блогов (включая личные), 4845 топиков, 27231 комментириев] с работающего сайта из другого движка.
По данным du -hc /каталог/базы данных, размер базы, включая индексы, составляет 53 МБайта в бинарном виде.

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

Факт, что узким местом являются запросы к базе подтверждается и блоком статистики внизу страницы и менеджером процессов top (ОС Debian Linux 4.0).

Использование memcached вместо файлового хранилища ускоряет загрузку, но уже после выполнения запросов и при повторных обращениях, когда роль БД минимальна.

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

Есть у уважаемого сообщества какие-либо идеи по поводу узкого места в работе с БД и/или предложения по нейтрализации сего узного места?

Отдельно хочу поинтересоваться у автора о причинах такого неоптимального хранения текста топиков?

52 комментария

avatar
плохой коммент. для попадания в эфир..., сорри
  • kruft
  • +1
avatar
можешь дать эту БД? чтоб реально оценить запросы
текст хранится в 3-х видах как раз для оптимальности: анонс — чтоб не воротить большой текст при выводе списка блогов, сам текст и исходный текст до обработки парсером — без этого никак, иначе будет много проблем с парсером.
если тормаза из-за ордер бая при больших текстах то это решается через селфджойн. Вобщем мне нужна большая БД чтоб что то сделать :)
  • ort
  • +1
avatar
ща дамп сниму и залью куданить. ссылку на куданить кину в личку.

только там дамп «грязный» — комментов в базе нет, теги в таблице для поиска по тегу не перенесены, где-то слеши лишние экранирующие не удалены. короче чисто база для экспериментов по быстродействию. но даже на этой базе без комментов, только что проверил, тормоза более чем заметны
  • kruft
  • +1
avatar
отлично
avatar

кинул в личку ссылку.
В качестве версии могу предположить вот что:
запросы

$rows = getAllRows()
foreach($rows as $row){ ... }
значительно медленнее, чем
while($row = getSingleRow()) { ... }
так как в первом случае приходится все ряды загружать в память php-интерпретатора/модуля веб-севрера, а во втором только по одному. при использовании смарти, к сожалению, из коробки возможен только первый вариант :( если отделять логику и представление и передавать ее объекту результирующий массив.

ну и, конечно, Limit большое число, другое большое число значительно тормознутее, чем where чтото >= чегото AND чтото < чего-то или чтото BETWEEN чегото AND чегото, т.к. в первом случае мускль сначала выбирает все ряды, удовлетворяющие where-у и условиям JOIN-ов, создает временную таблицу и из нее уже отдает что указано LIMIT-ом. создание временных таблиц операция дорогая до памяти и времени, а во 2-м и 3-м случае этого не происходит
  • kruft
  • 0
avatar

while($row = getSingleRow()) { ... }
Вы считаете, что запрос в цикле быстрее? Ой-ой!
Limit большое число, другое большое число
MySQL оптимизирует LIMIT, т.к. при выборе нескольких строк он не выполняет полное сканирование таблицы, а использует индексы. ;)
avatar

про цикл вайл:
ессно имелось ввиду не запросы по одному к базе :-)
я имел ввиду примерно
$res = mysqli_query(...);
while($row = $res->fetch_assoc()) { ...}
 
другими словами, запрос к базе ессно один (парсинг SQL один раз, если хотите), а вот получение результирующих рядов по одному

надеюсь с тем, что mysqli быстрее mysql, спорить не будете? :)
avatar

А! Ну это:
$res = mysqli_query(...);
while($row = $res->fetch_assoc()) { ...}
правило хорошего тона уже :)

Я вообще не спорю, а высказываю свое имхо :)
avatar
угу, но в схеме со сматри используется 1й способ с загрузкой сначала всех результатов в память, а потом «цикл для каждого» по массиму. уже в смарти.

по спорить сори)
avatar

Смарти вообще урод! Зачем его использовать в языке, который изначально создан для отделения кода от представления. ;)

<html><body><?=$var?></body></html>
будет выполнятся точно быстрее, чем отпарсенное смарти:
<html><body>{var}</body></html>

Но раз автор движка выбрал его — значит руководствовался некими соображениями на этот счет.
avatar

spectator.ru/technology/php/easy_templates
Быстрее, имхо, ничего не придумано. :)

<b>PHP is an HTML-embedded scripting language</b>. The goal of the language is to allow web developers to write dynamically generated pages quickly.
Но это всё лирика! Макс всё равно БОЛЬШОЙ МОЛОДЕЦ!!! Проделать такую огромную работу за такой короткий срок! А оптимизация — дело техники. ;)
avatar
Макс молодец, это бесспорно. с нуля написать движок с нехилой функциональностью, не используя при этом никакие CakePHP, CodeIgniter, симфонии и тп.

Про смарти: она делает написание шаблонов более приятным, предостерегая при этом от смешение MС и V, из аббривиатуры, что называется. очень полезными, подчастую, оказываются ее пре/пост-фильтры, различные хелперы и тп. но из шаблонизаторов это один из самых тормознутых движков :( хотя, наверное, и самый мощный.

Само-собой, что с выбором автора спорить не приходится, можно только мириться и выссказывать свои мысли по поводу улучшения того или иного в движке.
avatar
Кстати, я написал на CodeIgniter этот движок блога. Хочу сказать, что это самый быстрый фрэймворк из всех ныне существующих. И писать на нём одно удовольствие. Для меня. :)
Один из плюсов — встанет на любую БД, нет проблем с переходом проекта, скажем, с MySQL на MSSQL.
avatar
Классный блог и по исполнению и по содержанию.
Я кстати сплю и вижу, что лайвстрит переведут на CodeIgniter, а mootools заменят jquery :) ну это наверно от нешироты угла зрения: )
avatar
Спасибо!
Я всё никак не соберусь выложить исходники блога :)
avatar
А что так? Поделись с народом братан:)
avatar
Релиз же готовить надо: дефолтный дизайн лепить, инструкцию писать, скрипт установки и т.п. Как-нить добью это. :)
avatar
про лимиты:
Цитата: самое свежое про то с хабра
Инногда достаточно большой проблемой является LIMIT в запросах, я не буду тут говорить, что некоторые вытягивают 100 записей, а иногда и 1000 если реально используют 10; скажу следующее — польза от лимита есть только тогда когда в запросе используется индекс по полю, которое сортируем, т.к. в противном случае Using temporary; Using filesort нивелируют всю пользу от лимита. Также стоит избегать лимитов следующего вида LIMIT 1000000, 25 т.к. выбраны все равно будут 1000025 записей, и только потом 1000000 отброшен. Такое часто используется для pagination, и многие программисты часто оправдываются тем, что пользователи все равно в основном ходят на новые страницы (последние в хронологическом порядке), т.е. запросы с такими лимитами выполняются достаточно редко… Да, пользователи заходят на страницы годичной, двухгодичной давности не часто, но если на сайт зайдет поисковый бот, то он зайдет на все страницы, и этот бот, индексирующий контент сайта, положит нам сервер БД.
avatar

Всё верно. Выше я отметил, что mysql использует индексы вместо полного сканирования таблицы. Если индексы по этим полям есть! +))

SELECT
    *
FROM
   `table`
WHERE
   id>X*Y-1
LIMIT
    X;

Зачот! :)
avatar
про первое — разница очень незначительная, т.к. выбирается в основном не больше несколько десятков записей.
про limit — это проканает если нет дополнительных условий, а они как правило всегда есть
avatar
btw! Какая разница, сколько записей в базе — сто или сто тысяч? Если структура базы и запросы написаны оптимально, то время их выполнения практически не будет отличаться вне зависимости от размеров базы. Что вывести десять записей из таблицы со ста записями, что из таблицы со ста тысячами — это фиолетово. Хотя у MySQL, конечно, есть и предел быстродействия, но он ГОРАЗДО выше выбора десятка строк джоином из пары таблиц с пятью тысячами записей. ;)
avatar
Вопрос к знатокам SQL :)
Есть выборка с покрывающим индексом, как добавить в такую выборку дополнительное условие по связанной таблице сохранив преимущество покрывающего индекса?
  • ort
  • 0
avatar
Макс, не конопать мозги! )

Построение покрывающих индексов оправдано до тех пор, пока суммарная длина всех входящих в индекс столбцов остается значительно меньше длины строки таблицы. Всё остальное — от лукавого!
avatar
что имеешь ввиду под длиной таблицы?
avatar
Длина строки таблицы.
avatar

По определению покрывающим индексом называется такой индекс, который содержит все столбцы, упомянутые в операторах SELECT, UPDATE или DELETE. Запрос при этом называется покрываемым запросом. Поскольку не кластеризованный индекс содержит на уровне листьев запись для каждой строки в таблице, то вся информация для выполнения запроса находится в индексе. В силу этого процессор запросов может сканировать не огромную таблицу, а только небольшой индекс. В общем случае, если вам удастся построить покрывающий индекс, то вы сразу почувствуете значительное улучшение производительности обработки запросов. Это объясняется тем, что индекс содержит не всю строку таблицы, а только ее подмножество. Однако оборотная сторона медали состоит в том, что введение в индекс дополнительных столбцов приводит к тому, что на странице индекса умещается меньше записей. Это, в свою очередь, вызывает увеличение места, занимаемого индексом, и возрастание числа операций ввода/вывода, необходимых для считывания индекса в кэш. Построение покрывающих индексов оправдано до тех пор, пока суммарная длина всех входящих в индекс столбцов остается значительно меньше длины строки таблицы.
Морис Льюис
avatar
я имел ввиду что понимается под длиной строки таблицы? не пойму )
avatar
Физическая длина имеется ввиду.
avatar
длина строки таблицы? а что за строка таблицы? у таблицы есть столбцы и записи(строки). Получаем что длина строки это сумма всех длин стлобцов в записи? Тогда как эта длина может быть больше длины сумму индекса, если этот индекс делает по части столбцов из данной таблице.
Надеюсь доступно объяснил своё непонимание :)
avatar
Все верно, длина строки должна быть значительно больше суммарной длины столбцов, входящих в индекс. :) Тогда имеет смысл заморачиваться с покрывающим индексом. Но на самом деле я и без него живу неплохо уже много лет и не парюсь. :)
avatar
Всё фигня! Заполнил ща базу локально тестовыми данными. Около 10,000 юзеров, около 10,000 персональных блогов, около 50,000 топиков. По одному голосу за каждый топик и за каждого юзера. Общее время выполнения на всех страницах не превышает 0,3 сек. Машина — Core2duo двух ядерный, 2 гига памяти, обычный sata винт.
avatar
Кстати, кэширование выключено было. :)
avatar
у меня тестовая машина — Cel 1400Mhz, 256Mb. на ней стоит дебин 4.0, используется nginx+fastcgi. нгинкс гзип-ует весь отдаваемый трафик. стоит мемкашед.
avatar
Но всё равно, одна минута — это жесть!

О! У мну ща VDS есть на firstVDS — 300MHz, 64 метра памяти. Вот куда я ща тестовую базу-то залью! ГЫ! )))
avatar
Почистил кэш, зашел на сайт:

* MySql
запросов: 11
время: 2,173
* Cache
запросов: 43
set: 17
get: 25
время: 0,62973
* PHP
загрузка модулей:0,482
общее время:3,983

Учитывая мощь тестовой машины, весьма неплохо. )
avatar
у меня кстати еще eaccelerator активен.
а мощь у тебя сильно большая. на шаред-хостинге никто никогда не даст занять столько ресурсов одному клиенту, а кто готов дедика под сайт поставить, не будут использоваться чужой движок, а напишут свой.
avatar
подозреваю, что skachko писал про 300 мегагерцовый VDS на firstvds, а не про 2-х ядерный Core2duo )
avatar
/me проникся всей глубиной сарказма в комменте про мощь.

да, действительно. но тогда единственное, что остается — что в моей базе посты сильно больше его тестовой. других версий у меня нет
avatar
Да, мощь — это 300/64 ))
Посты небольшие — 100 символов.
avatar
попробуй в каждый топик вставить текста на 3000 символов, во все три поля для этого
avatar
интересно, как движется решение тормознутости некоторых запросов ?)
  • kruft
  • 0
avatar
движется, но кардинальных изменений пока не будет. В начале октября участвую в конференции «Конференция разработчиков
высоконагруженных систем»
. Надеюсь после её посещения в моей голове будет ясно, где искать слабые места и как их оптимизировать
avatar
ого! :-) какие люди то здесь обитают!

если не секрет, с докладом или просто слушателем?
avatar
не-не-не, слушателем :)
avatar
Проверка кеширования блока последних комментариев…
avatar
вот спамер, тебе сюда http://test.livestreet.ru :)
avatar
попытался оптимизировать запрос топиков для большой БД — http://trac.assembla.com/livestreet/changeset/22
ВНИМАНИЕ!!! Это изменение приведет к неработоспособности движка, это изменение только для энтузиастов желающих проверить работу на большом числе топиков. Для обновления необходимо выполнить первую часть(создание таблицы и индекса) patch.sql, потом запустить convert.php и только потом выполнить вторую часть patch.sql!!! Иначе вы потеряете все тексты топиков.
  • ort
  • 0
avatar
разницу почувствовал. запрос к таблице топиков был 9072 ms, а сейчас 247 ms
  • kruft
  • 0
avatar

оттестировал последний транк. все супер. ракета взлетела :-)

[2008-09-28 00:26:23][32267][DEBUG][SELECT
                                        c.*,
                                        u.user_login as user_login,
                                        u.user_profile_avatar as user_profile_avatar,
                                        u.user_profile_avatar_type as user_profile_avatar_type,
                                        c.comment_id as ARRAY_KEY,
                                        c.comment_pid as PARENT_KEY,
                                        IF(cv.comment_id IS NULL,0,1) as user_is_vote,
                                        cv.vote_delta as user_vote_delta
                                FROM
                                        prefix_topic_comment as c
                                       
                                        LEFT JOIN (
                                                SELECT
                                                        comment_id,
                                                        vote_delta                                                                                             
                                                FROM prefix_topic_comment_vote
                                                WHERE user_voter_id = 2
                                        ) AS cv ON cv.comment_id = c.comment_id,
                                       
                                        prefix_user as u                                       
                                WHERE
                                        c.topic_id = 6028
                                        AND
                                        c.user_id=u.user_id
                                ORDER by c.comment_id asc;     
                                        ]
[2008-09-28 00:26:23][32267][DEBUG][  -- 7 ms = 6+0+1; returned 16 row(s)]

MySql
   запросов: 13
   время: 0,063

при чтении конкретного топика
 
  • kruft
  • +1
avatar
на самом деле именно этот запрос не менялся :) его можно немного укорить вложенным запросом и селф джойном
avatar
kruft, а есть возможность оценить скорость при поиске по тегам???
avatar
Ребята, вот у меня тоже очень сильно тормозит LS 5, как решить эту проблему? Пациент — roll.by
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.