Комментарии 52
Классная статья, спасибо!
Рекомендую поглядеть на MongoDB. Она не так сильно упирается в размер доступной RAM и имеет некоторые другие полезности по сравнению с традиционными key-value хранилищами. Взять хотя бы aggregation framework или caped collections с ttl (это ваш кэш в виде одной опции).
Не стоит так сильно акцентировать внимание в статье на чужом говнокоде, так как ваш потом окажется в точности таком же положении.
Не стоит так сильно акцентировать внимание в статье на чужом говнокоде, так как ваш потом окажется в точности таком же положении.
на счет говнокода — ты конечно прав. Я сам смотрю на свой код, написанный пару лет назад и ужасаюсь… Но, в статье больше про архитектуру кода, а не его запахи.
Что касается MongoDb — есть опыт внедрения пары проектов. Очень требовательна к ресурсам и много жрет дискового пространства. На виртуальной машине в РАМ 2Гб — на 1М профилях начинает тупить. Для определенного круга задач — идеальное решение. В данном случае — она (монга) значительно проигрывает по скорости.
Что касается MongoDb — есть опыт внедрения пары проектов. Очень требовательна к ресурсам и много жрет дискового пространства. На виртуальной машине в РАМ 2Гб — на 1М профилях начинает тупить. Для определенного круга задач — идеальное решение. В данном случае — она (монга) значительно проигрывает по скорости.
Расскажите, как мерили разницу по скорости Тарантул/МонгоДБ и каков порядок у «значительно»?
Не понятно, что значит «требовательна к ресурсам»?
Запускать такую бд на виртуалке с 1Г памяти это все-равно, что на телефоне.
Опять же странно читать про «жрет дисковое пространство» в сравнении с «memory only database».
Монга по умолчанию при старте большие файлики создает, но это как бы не проблема, вот названию ключей не надо сильно раскидистые делать — это да.
Не понятно, что значит «требовательна к ресурсам»?
Запускать такую бд на виртуалке с 1Г памяти это все-равно, что на телефоне.
Опять же странно читать про «жрет дисковое пространство» в сравнении с «memory only database».
Монга по умолчанию при старте большие файлики создает, но это как бы не проблема, вот названию ключей не надо сильно раскидистые делать — это да.
Расскажите, как мерили разницу по скорости Тарантул/МонгоДБ и каков порядок у «значительно»?
мерил Си клиентом, засылал 100 000 запросов на несколько потоков (от 8- 16)
значительно, это на 30-40%
Запускать такую бд на виртуалке с 1Г памяти это все-равно, что на телефоне.
Это верно, и пытались это вразумить инвестору, что нужен выделенный сервер…
Монга по умолчанию при старте большие файлики создает, но это как бы не проблема,
да там вообще облако, так что настройка монги — это вообще проблема хостера.
мерил Си клиентом, засылал 100 000 запросов на несколько потоков (от 8- 16)
значительно, это на 30-40%
Запускать такую бд на виртуалке с 1Г памяти это все-равно, что на телефоне.
Это верно, и пытались это вразумить инвестору, что нужен выделенный сервер…
Монга по умолчанию при старте большие файлики создает, но это как бы не проблема,
да там вообще облако, так что настройка монги — это вообще проблема хостера.
С годик полтора назад тестировали производительность различных key/value в том числе и монго. Оказался самым медленным.
У нас получилось
mysql+handlersocket
memcahed+mysql (с различными костыликами)
mysql in memory db
redis
mongo
handlersocket > 600Kqps выше не хватило мощности клиентов
на memcahe получили порядка 450Kqps
redis/mongo на порядки медленее были (меньше 100Kqps. с лагами и падениями. при том, что за mongo были девелоперы и они отчаяно пытались её натюнить)
У нас получилось
mysql+handlersocket
memcahed+mysql (с различными костыликами)
mysql in memory db
redis
mongo
handlersocket > 600Kqps выше не хватило мощности клиентов
на memcahe получили порядка 450Kqps
redis/mongo на порядки медленее были (меньше 100Kqps. с лагами и падениями. при том, что за mongo были девелоперы и они отчаяно пытались её натюнить)
конечно все зависит от локального железа и настроек, но по моим тестам редис был быстрее чем mysql+hs
тут вообще еще очень многое зависит от методике измерения:
— в одном ли коннекте на поток, или на каждый запрос новый коннект,
— во сколько потоков шли запросы
— делали инсерты или уже изменяли зааллоцированные области.
— первоночальный размер карзины хештейбла или таблицы
— Каким клиентом мерили. Желательно, чтоб это был родной сишный клиент.
Но в целом корреляция существует…
тут вообще еще очень многое зависит от методике измерения:
— в одном ли коннекте на поток, или на каждый запрос новый коннект,
— во сколько потоков шли запросы
— делали инсерты или уже изменяли зааллоцированные области.
— первоночальный размер карзины хештейбла или таблицы
— Каким клиентом мерили. Желательно, чтоб это был родной сишный клиент.
Но в целом корреляция существует…
Сервер с базой
2 или 3 web (php, python, perl) как fcgi с разными клиентскими либами
siege или ab
На каждый клиентский запрос надо было получить от 100 до 500 значений из хранилища.
Только чтение. Запись в один поток и только инсерты (достаточно медленые)
Все данные по которым делается селект помещаются в память.
Для нас было актуально такое тестирование.
Нагрузка спецефичная, получилось так. Пока ещё не жалеем о выборе
2 или 3 web (php, python, perl) как fcgi с разными клиентскими либами
siege или ab
На каждый клиентский запрос надо было получить от 100 до 500 значений из хранилища.
Только чтение. Запись в один поток и только инсерты (достаточно медленые)
Все данные по которым делается селект помещаются в память.
Для нас было актуально такое тестирование.
Нагрузка спецефичная, получилось так. Пока ещё не жалеем о выборе
Меня интересовала производительность самого хранилища, по этому делалось 10К рандомных запросов (GET/SET) по num PK ключу в n потоков( каждый поток 10K / n ) сишными клиентами, писался специальныйтестировочный софт. Что бы по минимуму исклюить сетевое взаимодействие, то все происходило в одном коннекте. Ели на каждый запрос делать свой коннект, то общее время увеличится на 20-30%.
Все тестирование происходило на локалхосте, что не совсем правильно.
Пример тулзы для memcached протокола github.com/akalend/mcstrass
аналогичные тулзы писались и для redis, для HandlerSocket и для mongo…
Все тестирование происходило на локалхосте, что не совсем правильно.
Пример тулзы для memcached протокола github.com/akalend/mcstrass
аналогичные тулзы писались и для redis, для HandlerSocket и для mongo…
От названия статьи аж мороз по коже. Все известные мне говнокодеры мнят себя хайлоадерами.
что верно — то верно
ну, спасибо за минус… и меня к говнокедерам причислили :)
Не я ))
Мой комментарий был направлен на название статьи.
Говнокодинг — это уже целая культура. Списать просто на глупость людей, судя по их количеству — нельзя. И как-то заметил, что у большинства из них есть общая черта. Они не стремятся писать чистый код. Они стремятся оптимизировать, причем заранее. Причем всегда. Мозги у людей так повернуты, что они бесконечно думают только о том, чтобы код «летал». Т.е. смещена картина мира — они говнокодом называют то, что не быстро работает. Даже если не требуется. А то, что их код — говно, они не замечают совершенно.
В итоге, их код не читается, нельзя менять. И еще в добавок и не летает, а ползает, как недобитое животное. Предварительная оптимизация — такая предварительная.
У Вас вроде всё верно — написали на реляционной базе — работает, деньги приносит. А потом, уже когда код работал, дали оптимизировать.
Мой комментарий был направлен на название статьи.
Говнокодинг — это уже целая культура. Списать просто на глупость людей, судя по их количеству — нельзя. И как-то заметил, что у большинства из них есть общая черта. Они не стремятся писать чистый код. Они стремятся оптимизировать, причем заранее. Причем всегда. Мозги у людей так повернуты, что они бесконечно думают только о том, чтобы код «летал». Т.е. смещена картина мира — они говнокодом называют то, что не быстро работает. Даже если не требуется. А то, что их код — говно, они не замечают совершенно.
В итоге, их код не читается, нельзя менять. И еще в добавок и не летает, а ползает, как недобитое животное. Предварительная оптимизация — такая предварительная.
У Вас вроде всё верно — написали на реляционной базе — работает, деньги приносит. А потом, уже когда код работал, дали оптимизировать.
а я оценил твой юмор плюсом :)
писал код не я, довел до прибыли не я, так что это лавры хозяина проекта…
моя задача — чтобы все это взлетело, когда стало тормозить… и мои изменения были минимальны, просто другой программист стал делать рефакторинг под моим руководством
У Вас вроде всё верно — написали на реляционной базе — работает, деньги приносит. А потом, уже когда код работал, дали оптимизировать.
писал код не я, довел до прибыли не я, так что это лавры хозяина проекта…
моя задача — чтобы все это взлетело, когда стало тормозить… и мои изменения были минимальны, просто другой программист стал делать рефакторинг под моим руководством
что-то в коде менял я… но общий рефакторинг сейчас производит другой програмист. Мной лишь привнесены эти 5-ть рецептов.
Да, да, давайте дальше кричать «говнокодеры», «говнокодеры» и писать статьи с советами а-ля первый урок php (извините, дальше части про php не смог читать), от этого карма (не хабровская) должна обязательно улучшиться!
фреймворки заточены под узкий круг типовых задач
Фреймворк (англ. framework — каркас, структура) — структура программной системы; программное обеспечение, облегчающее разработку и объединение разных компонентов большого программного проекта. Употребляется также слово «каркас», а некоторые авторы используют его в качестве основного, в том числе не базируясь вообще на англоязычном аналоге. Можно также говорить о каркасном подходе как о подходе к построению программ, где любая конфигурация программы строится из двух частей: первая, постоянная часть — каркас, не меняющийся от конфигурации к конфигурации и несущий в себе гнезда, в которых размещается вторая, переменная часть — сменные модули (или точки расширения).
Я тут хотел начать кому-то что-то обьяснять, но перехотел. Пустое это.
это верно, иначе бы Баду, Мамба и Контакт были бы реализованы на симфони или джомула.
Нужно было просто уточнить, что речь о публичных фреймворках. Ибо любой нетривиальный проект содержит в себе фреймворк облегчающий команде его поддержку и/или модификацию.
Леша, согласен с тобой — полностью.
У каждого программиста, проработавшее определенное кол-во времени над определенным кругом задач появляется свой наработанный инструментарий в широком смысле этого слова, который принципиально можно назвать фреймворком. У меня есть собственный фреймворк, который я использую в большинстве задач. По этому, в тексте статьи и было написано, что это вечно флеймовый вопрос…: Быть или не Быть?
Конечно, было быпросто замечательно, если бы группа интузиастов выпустила фреймворк, заточеный специально под соцсети, нагрузку и бинарным обменом с флешью, а не адаптировало бы ZF или симфони
У каждого программиста, проработавшее определенное кол-во времени над определенным кругом задач появляется свой наработанный инструментарий в широком смысле этого слова, который принципиально можно назвать фреймворком. У меня есть собственный фреймворк, который я использую в большинстве задач. По этому, в тексте статьи и было написано, что это вечно флеймовый вопрос…: Быть или не Быть?
Конечно, было быпросто замечательно, если бы группа интузиастов выпустила фреймворк, заточеный специально под соцсети, нагрузку и бинарным обменом с флешью, а не адаптировало бы ZF или симфони
да, еще вспомнил случай реализации одного проекта. Взял свой фреймворк, для реализации социгры, далее решили прикрутить PHP-AMF, прикрутили, оказалось 75% функфиональности в корзину… потом отказались от AMF и заменили на протобуф, ну и вообще 90 процентов кода в никуда…
так что решаем спец задачи
так что решаем спец задачи
ну и чем поможет какой-то фреймворк, общий или свой… если задача специфична…
MySQL пробовали настроить перед тем, как NoSQL запихивать? Какой там объём данных и рейт запросов хоть?
Ваш код
$tuple = $res['tuples_list'][0];
switch ($tuple[1]) {
case UPDATE_SPIN_COUNT:
$sql = "UPDATE users SET spinCount ={$tuple[2]} WHERE uid ={$tuple[3]}";
break;
case 2:
$sql = "UPDATE users SET money = money + {$tuple[2]} WHERE uid ={$tuple[3]}";
break;
default:
throw new Exception ('unknow task type');
break;
}
Почему не так:
switch ($res['tuples_list'][0][1]) {
case UPDATE_SPIN_COUNT:
case 2:
$sql = "UPDATE users SET spinCount ={$tuple[2]} WHERE uid ={$tuple[3]}";
break;
default:
throw new Exception ('unknow task type');
break;
}
Впрочем и так и так следующий разработчик посмотрев на этот код запросто сможет написать подобную статью, удивляясь откуда и почему берутся магические цифры в этом коде
потому-что везде в статье псевдокод, для наглядного понимания, реального кода реально больше (больше проверок, больше методов, больше диапозон выбора данных)
ну а если по логике, то на UPDATE_SPIN_COUNT (=1) формируем один запрос, а на UPDATE_USER_BALANCE (=2) выполняем другой,
а судя вашей логике, на 1 и 2 я выполняю один и тот же запрос
ну а если по логике, то на UPDATE_SPIN_COUNT (=1) формируем один запрос, а на UPDATE_USER_BALANCE (=2) выполняем другой,
а судя вашей логике, на 1 и 2 я выполняю один и тот же запрос
НЛО прилетело и опубликовало эту надпись здесь
>$this->execSQL( «SELECT * FRAO users WHERE uid=$uid»); // Тут SQL Injection
принципиально ты прав, и молодец что заменил… Думаю, другие это оценят.
про псевдокод — мой коммент выше, а что касается $uid — то проверка на SQL Injection осуществлена еще в контроллере, а не в модели.
в данном случае это было приведение к инту и проверка на ноль. Ну и скажи — какой смысл пускать ноль в модель если можно дать отлуп на более высоком уровне?
И кто тебе сказал, что клиент и сервер общаются по HTTP? Может клиент и сервер общаются по AMF, protobuf или иному бинарному протоколу?
кстати, если вы разрабатываете социальные игры — то советую присмотреться к протобуфу
принципиально ты прав, и молодец что заменил… Думаю, другие это оценят.
про псевдокод — мой коммент выше, а что касается $uid — то проверка на SQL Injection осуществлена еще в контроллере, а не в модели.
в данном случае это было приведение к инту и проверка на ноль. Ну и скажи — какой смысл пускать ноль в модель если можно дать отлуп на более высоком уровне?
И кто тебе сказал, что клиент и сервер общаются по HTTP? Может клиент и сервер общаются по AMF, protobuf или иному бинарному протоколу?
кстати, если вы разрабатываете социальные игры — то советую присмотреться к протобуфу
Ну и скажи — какой смысл пускать ноль в модель если можно дать отлуп на более высоком уровне?
Модель одна, контроллеров с ней сообщающихся может быть сколько угодно.
Не заманает каждый раз писать проверку? Точно ни разу не забудете?
Всё же перед тем как творить что-то своё стоит обратить свое внимание на реализацию тех или иных вещей в современных открытых системах. Хоть в том же yii или Kohana, Меньше лапши в голове будет.
Очевидно у нас разные взгляды на архитектуру и понятия… Это не типовой WEB проект, точка входа одна, парсинга урла нет, один контролер который работает с моделями…
Расскажите про вашу архитектуру с одной точкой входа. Чем это обусловлено, неужели у Flash игр такая специфика? Или у вас что-то вроде постоянного соединения клиента с сервером?
Я писал много разных API.
Я писал много разных API.
В данном проекте нет, так как там моего кода — кот наплакал, рефакторинг еще предстоит
но в предыдущем проекте архитектура системы была следующая:
— клиент стучится по сокетному соединению, если оно не проходит, то тогда стучится по HTTP
— сервер запущен как демон, которые принимает сокетные соединения
— все HTTP запросы прокидываются через nginx на демон скрипта
— протокол обмена бинарный
статистика показывала, что из 1000 одновременных подключений, где-то только 30-65 идут по HTTP
преимущество бинарного протокола:
— компактность
— защищенность, хотя это сомнительно, но через протокол ни кто не влез, а флеш взламывали
преимущество сокетного соединения — скорость
но в предыдущем проекте архитектура системы была следующая:
— клиент стучится по сокетному соединению, если оно не проходит, то тогда стучится по HTTP
— сервер запущен как демон, которые принимает сокетные соединения
— все HTTP запросы прокидываются через nginx на демон скрипта
— протокол обмена бинарный
статистика показывала, что из 1000 одновременных подключений, где-то только 30-65 идут по HTTP
преимущество бинарного протокола:
— компактность
— защищенность, хотя это сомнительно, но через протокол ни кто не влез, а флеш взламывали
преимущество сокетного соединения — скорость
преимущество использование демонов:
— инициализация справочниками проходит один раз, во время загрузки
— соединение с БД, кешами и прочими сервисами проходит один раз
возможны проблемы масштабирования, но они просто решаются, да и нагрузок пока таких, чтоб требовалось масштабирование демона — не возникало.
— инициализация справочниками проходит один раз, во время загрузки
— соединение с БД, кешами и прочими сервисами проходит один раз
возможны проблемы масштабирования, но они просто решаются, да и нагрузок пока таких, чтоб требовалось масштабирование демона — не возникало.
Я курсе про демонов, сам пишу сервер на golang сейчас для одного приложения. И проблем с масштабированием быть не должно — шарлинг, реплики, синхронизация между серверами.
Но я всё равно не понимаю — у вас вся обработка происходит в череде if-else и switch-case в одной(грубо говоря) процедуре?
Ну нет у вас парсинга урла, но операций ведь больше чем одна? Даже несмотря на то, что соединение постоянное, есть ведь разные action`ы, есть контроллеры.
Но я всё равно не понимаю — у вас вся обработка происходит в череде if-else и switch-case в одной(грубо говоря) процедуре?
Ну нет у вас парсинга урла, но операций ведь больше чем одна? Даже несмотря на то, что соединение постоянное, есть ведь разные action`ы, есть контроллеры.
> у вас вся обработка происходит в череде if-else и switch-case в одной(грубо говоря) процедуре?
да, есть switch в котором есть вызывается соответствующий метод (action)
Каждый метод делает некоторые действия. Как правило, инстанцирует свой класс модели и вызывает её определенный метод. Результат выполнения присваивается переменной $result, из которой формируетя ответ.
да, есть switch в котором есть вызывается соответствующий метод (action)
Каждый метод делает некоторые действия. Как правило, инстанцирует свой класс модели и вызывает её определенный метод. Результат выполнения присваивается переменной $result, из которой формируетя ответ.
как вариант можно сделать как-то так…
но в данном проекте это совсем не катит, пока что реализовано на switch
$actionName= $action; $this->$actionName($args);
но в данном проекте это совсем не катит, пока что реализовано на switch
вот про демоны статья была бы интересна, особенно меня интересуют простые сишные вещи, beanstalkd и аналоги
Автору советую быть проще, и никогда себя не переоценивать, даже если вы очень хороши в проектировании высоконагруженных систем — готовые проекты покажут что вы стоите на самом деле. Ребята из андев делали сайт выборы 2012, вот это реальных хайлоад, одного разработчика я знаю лично. Скромнейший человек кстати. Это делает ему честь. А у вас в статье столько ляпов и дыр в знаниях. Может лучше больше читать и заниматься практикой и не писать такие сложные статьи с горяча?
Архитектор из автора очень даже не плох, а вот о вычитке нужно с ним договориться ;)
Если у вашего знакомого есть желание поделиться своими знаниями и при этом у него нет обременяющих обязательств перед работодателем — то ждем его статьи/заметки/твита об архитектуре его проекта! Будьте добрее!
Если у вашего знакомого есть желание поделиться своими знаниями и при этом у него нет обременяющих обязательств перед работодателем — то ждем его статьи/заметки/твита об архитектуре его проекта! Будьте добрее!
просто те кто работает в очень крутых проектах всегда очень и очень заняты и не всегда хотят делится
обосрасть всегда легче, чем написать хорошую статью
обосрасть всегда легче, чем написать хорошую статью
и почему я не удивлён?
Скажите, о какой нагрузке идет речь, по подробнее статистику.
Нагрузка была небольшая: при нагрузке на WEB сервер на 50-100 одновременных тестирующих потоках был возврат 35% ошибок (MySQL server has gone away).
после некоторого архитектурного улучшения (рефакторингом это не назовешь, его нужно еще проводить следующим шагом)
сейчас 320-350 rps, Все работает стабильно и MySQL уже точно не падает.
после некоторого архитектурного улучшения (рефакторингом это не назовешь, его нужно еще проводить следующим шагом)
сейчас 320-350 rps, Все работает стабильно и MySQL уже точно не падает.
Код проекта… В общем у меня осталось впечатление, что писал его недоученный студент… И это, немотря на то, что уже был сделан частичный рефакторинг другим программистом. Единственное, что радовало, то это то, что не использовался какой-либо фреймворк. Конечно, это вечно флеймовый вопрос: Иисус или Магомед? Быть или не Быть?
Когда прочитал, понял что дальше читать бессмысленно, Видимо автор, пишет в бинарном виде на перфокартах. И при чем гавнокод к оптимизации структуры базы данных?
Когда прочитал, понял что дальше читать бессмысленно, Видимо автор, пишет в бинарном виде на перфокартах. И при чем гавнокод к оптимизации структуры базы данных?
Почему не Mysql+Handlersocket, чтобы избавиться от дублирования памяти и не изобретать велосипед с синхронизацией и очередями?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Из говнокода в Highload. Используем ТАРАНtool. 5 рецептов повышения производительности