Рекомендую присмотреться к списку, если ваш проект вырос или планирует рост, написанный на любом интерпретируемом языке (php/ruby/python) на нескольких серверах с обычным стеком (веб-сервер/сервер приложений, субд, redis/memcahed, rabbitmq, ...).  В качестве подопытного для оптимизации был взят PHP backend - все нижеперечисленные приёмы были опробованы и применены. Наш проект почему-то задыхался на казалось бы неплохом железе и к тому же не утилизировал выданные ему аппаратные ресурсы. Чего не будет Соединение между компонентами приложения Избегаем сети Оптимизируем сеть Настройки хранилищ данных Репликация Оценка нагрузки Кэширование Не буду подробно расписывать способы, практически везде повторённые сотни раз в разных местах (как раз детали можно найти в них): оптимизировать настройки БД / кэша (выключение сброса на диск) / сервера приложений (про pm=static); увеличить мощности сервера; заказать ещё серверов; вынести на разные сервера приложение/БД/кэш; обновить версии программного обеспечения (зачастую в мажорных и иногда в минорных версиях привносят оптимизации) не перегружать единые точки отказа - например: не класть в кэш мегабайты данных (или хотя бы иметь кэш для мелких и больших данных с разным требованиям к времени доступа);  не выполнять очень много долгих операций в однопоточных сервисах (redis, например);  не выполнять одинаковые запросы, иметь индексы в БД; не выполнять тяжёлые операции синхронно - перевести всё на очереди; ну и куча других ошибок в разных частях системы; Предположим что программно у нас все хорошо: везде стоят оптимальные настройки (соответствующие железу и характеру нагрузки), аппаратных ресурсов достаточно, нет единых точек отказа, тяжёлые задачи не выполняются синхронно и вообще нет поводов для волнений. Но приходящая нагрузка (причём далекая от прогнозируемых цифр) показывает что то здесь, то там возникают перегрузки и система не выдерживает нагрузочного тестирования. На что же обратить внимание? Если несколько блоков системы находятся на одном сервере, их можно связать через Unix domain socket вместо сети. Что это можно быть: nginx -> php-fpm и обратно (например, для reverse-прокси) php-fpm -> nginx php-fpm -> pgbouncer / любая БД, pgbouncer -> БД Снижаем сетевую нагрузку, уменьшаем задержки. Без потока клиентского трафика разницы не будет, а с ним получиться убрать нагрузку на ядро ОС в установке и проведении по всему процессу сетевых соединений от одного до другого сервиса на одной и то же машине. Довольно просто - делаем все соединения до внешних хранилищ данных постоянными. Только аккуратнее если у вас целевой адрес динамический (внутри docker/k8s). Например: до СУБД - с помощью драйвера подключения к БД до redis/memcached/rabbitmq/... - с помощью параметров подключения библиотеки (включить атрибут "постоянное" в открываемом сокете) до внешних http-источников - с помощью реверсивного-прокси В статье хорошо описывается принцип reverse-прокси для сетевого межсервисного взаимодействия.  Поднимаем дополнительно контейнер с nginx в качестве reverse-прокси, который будет держать коннекты, и подключаемся к нему из приложения. Нагрузка на сеть уменьшится в разы, да и трафик (при хождении ко всяким https). Особенным плюсом для умирающей после каждого запроса области памяти при запуске через php-fpm (вместе с curl-handler'ами) является то, что мы можем таким образом держать постоянные коннекты от reverse-прокси до внешних сервисов, а приложение будет ходить по дешёвому http к reverse-прокси (а можно же ещё и по UDS ходить, тогда задержки будут минимальны). Устанавливаем пулер запросов pgbouncer перед всеми БД. Некоторые большие бэкэнды используют пулинг как на стороне приложения (т.е сразу после приложения и проксирует к нескольким БД), так и на стороне БД (т.е прямо перед БД и ~ лимитирует запросы к конкретной БД).  Используем по умолчанию режим transaction. Возможно потребует переписывания части кода - особенно в части блокировок в БД. Настраиваем количество коннектов исходя из оценки ресурсов серверов. Заводим ещё БД, настраиваем репликацию - ходим туда за чтением данных. Ставим несколько слейвов в дополнении к мастер БД, настраиваем репликацию. Но чтобы утилизировать все БД (и мастер, и слейвы) по максимуму - мы перенаправим на слейвы только часть запросов на чтение (но уж точно не все). Для этого в приложении настраиваем несколько коннектов и явно задаём коннект, через который пойдут те или иные запросы (зачастую играясь с mt_rand(0, 100) < 70 для указания % запросов на слейвы).  По моей оценке, неплохое соотношение при условии одинаковых суммарных мощностей мастера и слейв(ов): 70% запросов на чтение слейвы, 30% на мастер (оставляем свободу для операций записи). В любом случае частично мастер стоит использовать - лучше для более важных к актуальности данных (например, балансы пользователя). Делаем несколько кэшей/БД - шардируем данные между ними. Например, у нас сессии хранятся в одном редисе, а данные кэша - во втором, а кэш репозитория (см. дальше) - в третьем. В postgres - pg_stat_statements, в redis/php-fpm - slowlog. Смотрим, находим медленные запросы/функции и либо уменьшаем количество их выполнения (кэшированием), либо упрощаем/дробим.  Можно пойти дальше и добавить в приложение (вручную или с помощью APM-систем) сбор метрик: работы с БД, кэшем, внешними сервисами, чтобы смотреть на производительность и причины тормозов отдельных частей приложения. Если у нас так выходит, что после установки пулера запросов у нас кончаются свободные коннекты, но при этом нагрузка базы не стремится к 100%, тогда у нас БД слишком долго выполняет довольно простые запросы. Довольно часто эти простые запрос - это выборка с очень детерминированными фильтрами (id = :id / id IN (:listIds)). Не стоит тратить на это время БД - закэшируйте это, лучше всего прозрачно (с помощью слоя репозитория):  перед тем как вытащить сущность/N сущностей из БД, проверьте в кэш (redis) и выдайте оттуда, если есть; если данных нет, сходите в бд и отдайте их оттуда; перед тем как отдать, положите их в кэш перед возвратом, чтобы в следующий раз вытащить быстрее. Всё то же самое актуально и для тех выборок, где у нас агрегация/куча фильтров = сложные запросы, которые заставляют базу немного (или много) поработать. Делаем всё то же самое, только перед хождением в БД, синхронизуруйте (как в java) процесс заполнения кэша - ставьте мьютекс (хотя бы в тот же самый кэш) и освобождайте после заполнения кэша - чтобы несколько клиентов не запустили процесс генерации кэша (= несколько сложных запросов к бд). Если необходимо ещё разгрузить БД, для не слишком критичных к актуальности данных (какие-нибудь подборки новостей на главной, список доступных предложений для пользователя и т.д) можно скомбинировать два приёма кэширования, чтобы убрать большое количество нагрузки: Выполняем сложный запрос к БД с фильтрами, получаем id каких-то элементов из базы; Кэшируем (можно даже в shm - см. след. раздел); Если полученный кусок общий для всех пользователей, то ещё лучше. Но зачастую такие списки привязаны (= т.к. фильтруются по нему) к пользователю, т.е кэш не переиспользуется между пользователями - ставим небольшое время жизни; Выбираем сами элементы из базы по id из п.1; Кэшируем; Здесь данные не привязаны к пользователю и кэш будет общий для всех пользователей. Ставим время жизни больше (+ добавляем инвалидацию при редактировании элементов в админ-панели); В следующих запросах (пока кэш живой) данные будут выбираться уже из кэша, минуя БД. В качестве задачи со звёздочкой, можно на п.1 сделать сложным с 2 этапами: Выбираем общий список элементов из БД (например, активных акции сайта); Кэшируем; Причём тут уже будет кэш общий для всех. Фильтруем его для конкретного пользователя, после сортируем в php. Возможны разные комбинации в зависимости от целей: уменьшить нагрузку на БД, уменьшить количество однотипных запросов, увеличить количество выдаваемых в секунду ответов с незначительной потерей точности. Рекомендую попробовать разные варианты и решить что для вас оптимальнее. Нередка ситуация когда для API требуется много cpu (условно, 32 ядра и 16 гб озу или больше), поэтому на серверах приложений довольно часто можно найти и какое-то количество свободной памяти (не занимаемой php-fpm, nginx или что там у вас ещё крутится на этих же серверах?). Давайте её используем: выставляем лимит для контейнера shm_size, (напр, 512Мб) и подключаем каталог /dev/shm как каталог для кэша в виде файлов. В хост-машине с этим сложнее - придётся следить самостоятельно за очисткой кэша. Далее подключаем этот кэш как отдельный компонент кэширования в фреймворке и кладём в него большие куски не особо критичных данных (каких-нибудь списков) на небольшой промежуток времени (исчисляемый минутами). Если протухнет или будет не самый актуальный (если на одном сервере кэш будет лежать до 14:00:00, а на втором до 14:00:02), то ничего страшного не произойдёт. В итоге мы утилизировали память и получили очень быстрый кэш (т.к данные по сути лежат в памяти, а не на диске).