Как стать автором
Обновить

Комментарии 81

100 мегабайт кода? О_о
Ну да, а в чём проблема :)? За несколько лет работы 4-7 человек можно и больше написать :). Активного кода — где-то 20 Мб
Пардон, показалось сначала, что 100 мегабайт кода занял инструмент для автоматического предупреждения подобных проблем :-)
получается 3.75 млн строк примерно, за 4 года работы каждому кодеру из 7 человек нужно писать по 466 строк кода каждый день. учитывая выходные и праздники. дайте мне таких кодеров, которые могут 24/7 писать 500 строк нужного рабочего кода в день :) я им буду платить ооочень много
Symfony — нефиговый кодогенератор, так что почему бы и нет.
дык тут же человек говорит, что все руками написано :)
Symfony действительно помогает писать код во многом, как и Propel, генерируя «каркас», поэтому, конечно, действительно вручную написанного кода будет немного меньше. Но несколько лет — это не 4 года, а больше :). Ну и там не весь код на Symfony, большАя часть кода на обычном PHP
У нас проект примерно на 300МБ кода
Исключая внешние библиотеки?..
Так точно. Без Пейпала, картинок интерфейса (css, js и шаблоны будем считать за код — они же написаны вот этими мозолистыми руками :) ) и т.п.
А у вас пейпал (PayPal) какой-то большой?
8 МБ с доками и примерами
хотел бы я поглядеть на проект в 300 МБ чистого пхп кода. если в среднем одна строка занимает 80 символов, то это получается 3.75 млн строк кода. вы что амазон там написали? чтобы обслуживать такой воистину громадный проект нужен как минимум кластер. а там уже совсем другие методики оптимизации используются в дополнение к сказанному вами.
Ну, вот цифры для проекта, о котором шла речь:

# количество строк
$ cat `find . | grep .php | grep -v 'cache/'` 2>/dev/null | wc -l
2066539
# количество байт
$ cat `find . | grep .php | grep -v 'cache/'` 2>/dev/null | wc -c
74121750


Думайте сами :))
ну тут же 75 мегабайт кода ;) а не 300. и сдается мне это со всеми библиотеками.
Я говорил про проект в статье :). И это один проект, а их у нас есть ещё другие, поменьше :)
а какой тематики эти проекты, если не секрет, что в них столько логики?
Туризм :). Это не так важно, в любом более-менее крупном бизнесе будет немало внутренней логики, которой извне может быть вообще не видно
Тащемта это моя была реплика про 300МБ и youROCK не имеет никакого отношения к нему. Вот моя статистика:

$ find. \( -name "*.php" -o -name "*.js" -o -name "*.css" \) -exec cat {} \; | wc -l
7400511
$ find. \( -name "*.php" -o -name "*.js" -o -name "*.css" \) -exec cat {} \; | wc -c
283243229

Поводов для гордости не так уж и много — куча легаси, написанного не за один год. Уууу.
Серьёзные размерчики. Я стараюсь держать проекты в рамках 40K строк (без учёта фреймворка). Если проект становится больше — рефакторю, улучшаю архитектуру и в итоге минус несколько тыс. Строк кода. Так уже года три — функционал растёт, а размер не меняется!
наверняка есть много дублирующего кода
есть. Не сильно дублирущего, не так-то и много тупого копи-паста, но он есть. Работаем над этим :)
Простая арифметика:
Если простой вывод из Бд занимает пять строк (строим массив) и он повторяется 300 раз, то уже экономим 1450 строк (1500 — 50 на формирование класса)
Добро пожаловать в мемкеш/редис. Объекты и так кешируются, опкод кешируется, вывод веб-сервера — и тот кешируется (который нужен). Поэтому проблема 1450 строк хоть и присутствует, но не так актуальна.
что-то уж очень много.
Вот и меня заинтересовало, что за проект с кодом в таком объеме.
Искусственный интеллект с памятью на MySQL?
Вот вам смешно, а я ведь действительно отчасти похожими вещами и занимаюсь :(. До искусственного интеллекта, конечно, не дотягивает, но логика порой бывает оооочень сложная, и да, как ни странно, память именно на MySQL, ибо больше никто так компактно хранить информацию не умеет (кроме других реляционных СУБД, конечно же)
а что здесь странного?
Поправил статью, написав чуть более ясно, что под этим подразумевается
Огромное спасибо!
SHOW SESSION STATUS LIKE 'handler_%' -- магия ;)!
Ставим Percona Server, включаем handler socket и радуемся жизни :)
p.s. Percona Server — это форк MySQL, со всякими оптимизациями.
От запросов, которые требуют FULL SCAN на 100 000 строк (потому что индексов соответствующих нет) даже Percona не спасет, я думаю ;)
Хм… а как потестить производительность, если нет такой объёмной БД?
Если такой объемной БД нет, то и проблем у вас, скорее всего, тоже не будет с этим :). Ну попробуйте сделать JOIN для всех таблиц, что у вас есть по условию 1=1 — я думаю, на 100 000 строк сможете набрать даже с 3 таблицами
Нагуглил пакет sysbench и с его помощью потестировал :)

pastebin.com/eHXRbaTb

Правда сравнить не с чем. Тестировалось на виртуалке с ubuntu server 11.10. 70% от 2 ядер E8400@3.33Ghz, 512mb ram.
Сделать тестовое наполнение БД. Не зря при разработке высоконагруженных систем выделяют даже команды подготовителей данных.
про EXPLAIN интересно…
Да, profiling тоже бывает очень полезен, но как раз проблему с количеством прочитанных строк он решает далеко не всегда :). И когда используются подзапросы, SHOW PROFILE генерит ооочень много строк
По-моему автор изобрёл внутреннюю стату Percona
Про оптимизацию запросок как-то мало, я бы еще добавил денормализацию, избавление от групбаев, джойнов. Да и о кешировании как-то глухо.
Да, про собственно оптимизацию запросов я специально ничего не писал, потому что статей на эту тему не просто много, а очень много, и здесь, как раз, серебряной пули нет, к сожалению
А джойны чем плохи? Иногда без них никак.
обойтись можно всегда, а плохи тем, что масштабируются плохо, и кешировать без них проще
> EXPLAIN в MySQL зачастую нагло врёт.

С чего это он врёт? Конечно, никто 100% точности от него не ожидает, но тут он, скорее всего, говорит правду.
Ну, статистика по количеству просмотренных строк (SHOW STATUS LIKE 'handler_%') говорит об обратном — в том-то и дело :)
Ой, простите, я перепутала запросы в блоках: у вас же разные в 5.1 и 5.0
С помощью этой методики мне удалось выявить узкие места и снизить времена исполнения большинства страниц на сайте на Symfony с ~1000 ms до где-то 200-300 ms и добавить в дев-версию инструмент для автоматического предупреждения подобных проблем в будущем.
Это еще раз доказывает, что если хочешь написать Высокопроизводительный порект, или с проект с притензией на Hiloa, то забей на всякие там Симфони и Друпалы и тому подобное…
Решил как-то наш IT начальник использовать ОперКарт. В результате пришлось переписать весь фронтэнд (в том числе и из-за нового интерфейса). По кучи запросов на страницу — это жесь. По одному селекту на вывод одной ветки дерева — это вообще песня…
В результатеот ОпенКарта осталась одна админка, которая использовалась и то на 10%, так как пришлось добавлять кучу своего функционала.
200-300 ms — тоже жесть, у меня в проекте по 20-40 ms на полное формирование стр.
Полностью согласен, что для хайлоада симфони или drupal — не лучший выбор. Но, к сожалению, этот выбор был сделан до меня, а для себя лишь поставил задачу заставить это чудо хотя бы как-то шевелиться. С 200-300 ms на страницу уже можно жить, в принципе
+1 в карму
10 000 000 хитов в сутки это уже хайлоад или нет?
Я про sportbox.ru на Drupal, если что
А сколько серверов, и каких, если не секрет, обеспечивают работу сайта?
6 фронтэндов для веба, не забываем про трансляции, когда до 45000 онлайн
1 сервер для спортивной статистики, пропущенные/забитые голы и всё такое.
1 сервер БД
3 мемкеша
+ девелоперское.
Но могу ошибаться, некоторые машины являются виртуальными машинами.
Спасибо, неплохо, 3 мемкеша панацея:) У нас аналогичная нагрузка на чуть меньшем кол-ве серверов работала, там самописный мвц был, к сожалению, не могу раскрывать подробности, коммерческая тайна и всё такое.
Долго искал в статье хоть какой-то намек на использование log-slow-queries и log-queries-not-using-indexes. Не нашел.

Как же это вы забыли про штатный способ поиска узких мест?
Не то, чтобы я про него забыл, я его просто пропустил, как и много чего другого. slow log — это хорошо, но его нужно смотреть отдельно, и польза от него далеко не однозначная, ибо в slow log часто попадают вполне «легальные» запросы. По сути, предложенный мной метод почти полностью повторяет информацию, доступную в slow log, только без, собственно, использования slow log
Под «легальными» запросами я подразумевал различные статистические выборки или выборки, которые исполняются редко, или же, скажем, по крону. В теории, можно всё это настроить и отфильтровать, но мой способ, как мне кажется, намного проще и удобнее в использовании
Я ответил, но почему-то вот здесь, извините
То есть MySQL предоставляет вам надежный способ учесть:
а) чистое время исполнения запроса, без учета кэширования и времени построения плана и
бэ) количество реально просканированных записей
и даже построить по ним отчет (mysqldumpslow), но вы его пропускаете, потому что в него надо «смотреть отдельно»?
Ладно, давайте поставим вопрос по-другому. Была задача — снизить времена исполнения страниц на сайте. Что дает нам slow log, в самом лучшем случае? Он дает нам непосредственно SQL-запросы, которые были исполнены. По этим SQL-запросам ещё нужно найти кусок кода, в котором происходит вызов этого запроса (что далеко не всегда очевидно, если используется ORM, а у нас она используется очень конкретно) и только потом его исследовать.

Итого, действия при работе со slow log'ом:
1) нужно туда не забывать смотреть (причём для того, чтобы был доступ к slow log'у, по сути, нужен доступ к контейнеру с СУБД)
2) нужно (вручную?) отсеять запросы, которые не исполняются на страницах, а исполняются где-либо ещё (например, в кроне)
3) отсеять «тормозные» запросы, вызванные случайными всплесками активности / длительными блокировками при дампе таблиц и тд
4) по найденному запросу догадаться, на какой части сайта он используется и найти соответствующий кусок кода
5) начать действовать

Конечно, slow log хорош, но он реально полезен только тогда, когда есть откровенно «тормозящие» запросы. Даже ситуацию с тем, что запросов очень много (и они относительно простые), и поэтому не попадают в slow log этот подход уже не решает (при этом, скажем, этот запрос прекрасно виден в SHOW FULL PROCESSLIST через раз).

В моей статье я пытался описать действия, которые можно ещё попробовать, когда стандартные и известные средства плохо помогают (счетчик числа/времен запросов на странице, slow log, авто-explain, авто-show profile, анализ с помощью SHOW FULL PROCESSLIST и наблюдения, «за что глаз зацепится», ...). Понятно же, что есть стандартные средства для диагностики запросов, просто их часто бывает недостаточно, и приходится выдумывать такие фокусы, которые предоставляют, по сути «живой» slow log прямо на странице.

Для более-сложных запросов, из моего опыта, временами блокировок/кеширования/построения плана запроса/оптимизации запроса можно пренебречь, хотя их в любом случае нужно тоже считать вместе с временем исполнения запроса, чтобы получить полную картину. В общем, slow log — это хорошо, но не всегда это позволяет решить конкретную задачу — ускорить исполнение страницы на сервере, а не просто найти все «медленные» запросы.
Аргументация понятна. Но, согласитесь, место ей в начале статьи, а не в подвале коментов.
Соглашаюсь, из моей статьи это могло быть не совсем ясно, что все «стандартные» способы тоже существуют, и что нужно пробовать именно их в первую очередь, а потом уже, если не получилось (а вероятность этого весьма ненулевая), читать мою статью :). Я просто сделал предположение, что люди, которые будут на хабре читать эту статью, уже всё, что знали, перепробовали, и надеятся здесь увидеть что-нибудь ещё, чего они не знали до этого. И, судя по количеству добавлений в избранное, я действительно смог рассказать что-то новое для большого количества людей, которые используют MySQL :)
Для поиска кода, из которого пришел запрос, есть очень простой прием: где–нибудь на нижнем уровне вашего ОРМ добавляете код, который будет автоматически комментировать запросы. В комментарии можно добавить все что угодно: название метода, глобальный счетчик запросов, кусок стектрейса.
Конечно, это не очень хорошо повлияет на QC (одинаковые данные могут кешироваться для разных запросов), но и никто не говорит, что это надо делать постоянно. Можно периодически включать в дебаг режиме для одного из слейвов.
EXPLAIN вроде как ни разу мне не врал. Единственное это то, что по какой-то причине индексы странно себя ведут порой. То есть, например, делаешь EXPLAIN и он показывает что-то похожее на ваши результаты, потом делаешь OPTIMIZE TABLE и он начинает показывать ожидаемо вменяемый результат. Ну и до того как делаешь OPTIMIZE, Cardinality у индексов выдаёт неверное значение. Отчего не знаю, как избавиться тем более, поэтому просто периодически делаю OPTIMIZE.
Это связано с тем, что в InnoDB статистика перестраивается при OPTIMIZE TABLE, а до этого момента она может быть сильно испорченной частыми апдейтами.
Есть несколько простых советов, которые подходят для большинства проектов: как достичь высокой производительности. Скорее всего, чтоб их раскрыть — это тема отдельной статьи.
Первое, надо по возможности исключить все JOIN, используем денормализацию.
Во вторых, кешируем все справочники и неизменные таблицы, не надеемся на Кеш Запросов. Эти данные вытеснятся другими пользовательскими данными. Склейку данных делаем на клиенте.
Третье — предварительная подготовка данных. Используем очереди и бэкграундовские процессы.
Четвертое — большие порции данных бьем на маленькие части (иногда это называют шардинг).
Пятое — для доступа к данным на чтение используем HandlerSocket, так же можно использовать на обновление данных, на вставку не советую. Не забываем, что Autoincrement в HS работает не так как бы нам хотелось.

Используя эти пять простых принципов, у меня ни когда БД не была узким местом.
Не сочтите за рекламу:
много полезного можно найти в этой книге
цена в books.ru 90 руб
Все это, конечно, хорошо, но магия работает только в том случае если на сервере вообще больше ничего не исполняется. Т.е. на продакшне да с более-менее интенсивной загрузкой этот метод зачастую будет показывать сколько лет бабушке Пушкина вместо реальных данных. Хотя метод, конечно же, интересный.
Метод был хорош — но уже морально устарел (для иннодб таблиц удобнее смотреть значение innodb переменных). Если граммотно настроить performance_schema то можно снимать статистику по запросу в режиме рельного времени и, если понимать какие строчки кода что обозначают, можно докапаться не только до истины, что у вас слишком активно читается табличка 1...0 раз, но и понять почему её нет предположим в кэше, а так же что более важно обнаружить другие более узкие места, которые не показываются в сессионных переменных.
вот про это бы и отдельную статью!
Кстати, забыл ответить — performance_schema ведь появилась только начиная с MySQL 5.5, а она используется далеко не везде и далеко не всеми (причём по вполне понятным причинам, ибо в ней довольно много багов)
Странно что вы обращаете внимание сразу на количество строк. По-моему, первостепенным является нахождение запросов у которых происходит using filesort, using temporary table и using fullscan (в порядке уменьшения приоритетов) по мнению неточного Explain-а и преобразование их в такой вид, чтобы было using index. Это, ИМХО, решает примерно 70% потери производительности. Дальше уже можно делать профайлинг, изучать количество строк, убирать из выборки лишние столбцы, кешировать, денормализовать структуру и т.д.

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

P.S. Я обычно выдаю предупреждения на dev-сервере как раз если в Explain-е встречается filesort и temporary table. Когда одновременно, предупреждение жирное :)
Я рад, что смог помочь самим (хоть и бывшим) разработчикам Хабра :). Очень приятно, правда
Запросы

SELECT * FROM some_table WHERE some_field = 100500 ORDER BY id LIMIT 100

и

SELECT * FROM some_table WHERE some_field <> 0 ORDER BY id LIMIT 100

принципиально разные. И если у нас есть индекс по полю some_field, то как раз в первом запросе не надо прочитывать 100500 строк, во втором — надо, так как условия там заданы разные. Или я чего-то крупно не понимаю?
Вы правы, в первом случае при наличии индекса запрос не займет значительного времени, даже при сотнях миллионов записей(ох, был у меня проект со статистикой...). Индексы — наше все!:)
Товарищи, которые плюсуют этот комментарий, прочитайте описание структуры таблицы. Где вы там увидели индекс на some_field?
Хм, действительно, в описании указан только индекс первичного ключа. В таком случае непонятно, почему «буду индекс праймаре использовать», так как он для поиска по some_field совершенно не нужен.
Потому что стоит ORDER BY id
В таком случае, лучше бы Вы реальный вывод EXPLAIN привели, а не свою интерпретацию. Потому что есть поле possible_keys, и есть key, и они имеют разный смысл.
Вывод будет такой (за исключением названия таблицы):


Предполагается, что читателю очевидно, какой будет EXPLAIN, либо ему будет интересно проверить это самому (ибо утверждения действительно не совсем тривиальны). В обоих случаях реальный EXPLAIN не является необходимым
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.