В этой статье я решил собрать сборную солянку из советов о том, как разрабатывать высоконагруженные сервисы, полученных практическим путем. Для каждого совета я постараюсь приводить небольшое обоснование, без подробностей (иначе статья бы получилась бы сравнима по размеру с войной и миром). Поскольку обоснований я буду приводить не очень много, не стоит воспринимать эту статью, как догму — в каждом конкретном случае приведенные здесь советы могут быть вредны. Всегда думайте своей головой перед тем, как что-то делать.
1. Думайте своей головой и проверяйте факты
Это самое важное. Для вас не должно существовать безусловных авторитетов. Если кто-либо говорит полную чушь, или говорит что-то, что противоречит вашей практике — не прислушивайтесь к таким советам, и не важно, насколько этот человек известный и уважаемый. Если вы разрабатываете большую систему, и она не будет работать хорошо, то спрашивать будут с вас и в данном случае «мы следовали лучшим мировым практикам» оправданием не является. Умение применять нужные технологии в нужном месте и делает вас ценным специалистом, а не слепое следование чьим-то советам — для этого как раз квалификации не требуется.
2. Простое лучше сложного и «правильного»
Системы, которые вы разрабатываете, должны быть понятны прежде всего вам и вашим коллегам, а не «сферическому коню в вакууме». Например, если готовая система, на которую вы хотите перейти всей командой, не прозрачна и не понятна всем участникам, то вряд ли можно надеяться на то, что люди смогут эффективно её использовать. Как правило, под нагрузкой всплывают разного рода проблемы даже в самых лучших и отлаженных системах, а времени на устранение проблем обычно мало, поскольку в этот момент страдает большое количество пользователей. Без хорошего понимания, как всё работает, у вас мало шансов решить проблему быстро. Можно вспомнить, к примеру, как лежали «одноклассники» в течение нескольких дней — в хорошо спроектированной и продуманной системе даже серьезные сбои устраняются за несколько часов, а не суток.
3. Не забывайте про мониторинг
Обычно никто не забывает о том, что нужно тестировать изменения перед тем, как их выкладывать. Но намного реже люди задумываются о том, что происходит после деплоя, и как себя система ведет в продакшене. Но на самом деле мониторинг — это логичное продолжение QA для кода. У вашей инфраструктуры тоже должен быть Quality Assurance — вы должны видеть проблемы заранее, а если они все-таки возникли, то иметь возможность быстро диагностировать причины. Без мониторинга этого сделать попросту невозможно.
4. Пишите постмортемы (у каждой поломки есть стоимость)
Когда что-то важное упало — напишите постмортем, в котором будет описано:
- что случилось
- почему это случилось
- сколько пользователей пострадало (если это возможно посчитать, то и сколько денег компания потеряла)
- что нужно системно поменять, чтобы такого больше не повторилось (варианты вроде «быть внимательнее» на практике не работают)
Зачастую, уже при подготовке к написанию постмортема, можно сделать вывод, что на самом деле ничего страшного не произошло. То есть, устранение причин, вызвавших инцидент, обойдется компании дороже, чем оставить всё «как есть». Не стоит забывать и о таком варианте — то есть, иногда что-то будет ломаться, и это нормально, если оно находится в допустимых границах (для каждой компании эти границы свои).
5. Производительность — это фича
Несмотря на то, что формального определения «высоконагруженного проекта» не существует, обычно все более-менее понимают, что имеется в виду. Одной из особенностей приложений под хорошей нагрузкой является то, что зачастую намного проще оптимизировать какой-то кусок кода, чем разнести логику на несколько серверов. То есть, при разработке высоконагруженных систем часто приходится заниматься оптимизацией и полезно уметь это делать. Если оптимизируется не только пропускная способность, но и время отклика, то вы заодно улучшаете user experience. Win-win.
6. Двухфазный коммит — это сложно, но неизбежно
Если только ваша система не помещается полностью на один сервер, вы неизбежно будете сталкиваться с необходимостью делать т.н. «двухфазный коммит» — необходимость атомарно обновить данные в нескольких местах, например в базе данных и в сервисе. Двухфазный коммит на практике — это миф. Атомарно обновить что-либо больше, чем на одном сервере невозможно. Есть разные способы, как можно достигать консистентности в таких случаях, но самым распространненым являются очереди — вы добавляете в одной транзакции данные и запись в отдельную таблицу — очередь на обновление сервиса. Поскольку это транзакция на одном сервере, то она либо пройдет целиком, либо не пройдет вовсе. Соответственно, даже если обновить данные в сервисе сразу не удалось, в конечном итоге данные будут согласованы.
7. Постраничная навигация — сложная задача
«Какая ещё сложная? Делаете обычный SELECT с LIMIT/OFFSET и всё, есть постраничная навигация!» — наверняка подумали вы. Вы правы, но этот подход хорошо работает только в случае, если данные не меняются (иначе будут возникать дубли и, наоборот, пропуски некоторых записей). Кроме этого, использование больших значений OFFSET обычно приводит к серьезной потере производительности, поскольку базе данных нужно буквально выбрать все запрошенные строки в количестве LIMIT+OFFSET, отбросить OFFSET штук и вернуть оставшийся LIMIT. Эта задача линейна по времени относительно значения OFFSET и при больших значениях эта конструкция обычно знатно тормозит.
В зависимости от задачи, решение этой задачи может быть различным, но оно практически никогда не является простым и однозначным. Например, можно использовать minId вместо номера страницы — вы просто пропускаете большинство записей по индексу. Или, если страницы возращаются в результате поиска, нужно уметь игнорировать новые изменения или где-то хранить снэпшот данных для соответствующего запроса.
8. (Ре)Шардинг — сложная задача
В общем случае нет способа эффективно расшардить базу данных, чтобы получить близкий к линейному прирост производительности. Вам нужно выбирать стратегию шардирования, которая будет подходить именно для вашего проекта, и выбирать нужно очень хорошо, потому что процесс решардинга, а тем более изменение схемы шардирования — это одна из самых сложных задач при хранении большого массива данных, и она тоже в общем виде практически не решается.
Если вы думаете, что проекты вроде CockroachDB представляют собой серебряную пулю, которая решит все ваши проблемы, то вы ошибаетесь. Если вы хотите получить от системы нормальную производительность, вам всё равно нужно хотя бы в общих чертах понимать, как шардирование происходит внутри базы данных, чтобы минимизировать общение между узлами (интенсивное общение между нодами — основная причина, по которой рост производительности редко бывает линейным при добавлении новых узлов).
9. Хороший код отличается от плохого тем, как обрабатываются ошибки
Вопреки распространенному мнению, хороший код определяется не только тем, насколько всё логично разложено по пакетам, можно ли этот код прочитать, следует ли он всем мыслимым и немыслимым style-гайдам, и так далее. Это всё важно, но пользователей системы (под пользователями здесь подразумеваются не только конечные пользователи сайта, но и системные администраторы, другие программисты, и т.д.) прежде всего волнует, чтобы всё работало так, как ожидается, а когда не работало, было понятно, что происходит.
На удивление много софта вообще не обрабатывают ошибки, или обрабатывает их в стиле «ой, что-то случилось», или вообще начинают DDoS'ить сами себя бесконечными ретраями. Обработка ошибок — это сложно, и адекватная реакция на них — тем более. Ошибки бывают самыми разными, и далеко не все из них требуют, к примеру, завершения программы. Например, если вы разрабатываете файловую систему и она совсем перестает работать (в том числе не дает удалить данные) при заполненности диска в 100%, то это — плохая файловая система. При этом, вы могли следовать всем «лучшим практикам» от известных людей и у вас миллион звезд на гитхабе — это ни о чём не говорит. Грубо говоря, shit happens, и даже у самых крутых людей место на серверах иногда неожиданно кончается, и вы должны уметь эту ситуацию.
На этом всё
Спасибо за то, что прочитали эту «статью». Интересно будет почитать ваше мнение, или ваши собственные советы, которые основаны на практическом опыте, пишите об этом в комментариях!
Автор: youROCK