В эпоху генеративного ИИ многим может показаться, что знать основы тех или иных технологий вовсе не нужно - все сделает добрая LLM, а вам надо лишь правильно дать ей задание. Вот только в этом рассуждении опускается ответ на вопрос о том, что значит это "правильно". Нейросеть может сломать код в самых неожиданных местах и не исправлять его по общему запросу "найди и исправь баги". Вам придется самостоятельно искать причину поломки и тыкать бездушную машину в ее же ошибку носом. Чтобы это сделать, надо уметь читать код, понимать, что нейросеть делает, и в целом быть подкованным в теме. А чтобы вам не пришлось страдать над толстыми и сложными книжками, я сделал это за вас и собрал информацию в упрощенном виде. Сегодня речь пойдет про реляционные базы данных, в частности - про соединения. От вас ожидаются только базовое понимание SQL и интерес к backend-разработке.
Как вообще выглядит взаимодействие с базой?
Первым делом стоит поговорить про то, как вообще мы взаимодействуем с базой данных. В современных фреймворках это скрыто под кучей готового кода, в котором без пары бокалов не разберешься, но на самом деле все не так сложно. Можно вообще обобщить это взаимодействие до шести шагов:
Устанавливаем соединение - получаем коннект. Это своего рода труба, по которой мы можем общаться с нашей БД.
Посылаем базе запрос, написанные на языке SQL.
База анализирует запрос, строит план, выполняет его и формирует ответ.
Получаем ответ.
Фиксируем изменения - делаем коммит.
Закрываем соединение - отпускаем коннект.
Технически пункт 5 является частным случаем пунктов 2-4. Но я вынес его отдельно специально и расскажу об этом в следующей статье.
Давайте подробнее про коннекты
Коннекты - это первое, с чем мы соприкасаемся при работе с базой. Их можно воспринимать, как физические границы нашей работы. Главное, что стоит понимать - SQL выражение мы выполняем в рамках одного коннекта. Мы не можем отправить запрос по одному коннекту и получить по другому. Сам коннект мы храним на стороне приложения, но сама логика обработки запроса выполняется в БД. Такое распределение дает простор для некоторых очень неприятных ошибок.
Представим ситуацию: пользователь нажимает кнопку в приложении, кнопка отправляет тяжелый запрос в базу данных. База данных начинает запрос обрабатывать, а в этот момент пользователь случайно завершает приложение. Запрос все равно продолжает выполнятся, пока не сформирует ответ и не попытается отправить его обратно - только в этот момент возникнет ошибка. Если пользователь сразу после завершения запустит приложение обратно и нажмет ту же кнопку еще раз, новый запрос начнет работать параллельно с предыдущим. Если пользователь будет делать так несколько раз, то очень скоро он придет к тому, что БД начнет тротлить (от англ. throttle - душить) обработку запросов из-за большого количества тяжелых запросов и это сильно замедлит вообще все запросы к ней.
Эта ситуация может показаться надуманной, но это специально упрощенная модель. Поставьте между кнопкой и базой API Gateway и backend-сервер с таймаутами на запросы к ним и получится куда более реалистичная ситуация: gateway отдает ошибку 504, фронтенд выводит что-то непонятное и пользователь думает, что кнопка его не услышала и надо нажать еще раз.
Работа с коннектами
Обычно коннектами управляют специальные программы или библиотеки - пулы соединений (HikariCP, PgBouncer, ...). Работают они под капотом различных фреймворков по работе с данными, так что вам не приходится думать об этом постоянно. Достаточно их подключить и настроить.
Настраивать пулы соединений стоит с учетом архитектуры вашего проекта, его профиля нагрузки и особенностей. Из общих советов могу только обратить ваше внимание на количество коннектов в общем и незанятых - в частности. Установление соединения - дело не бесплатное, поэтому лучше переиспользовать уже готовый коннект, чем создавать новый. Но даже поддержание коннекта - совсем не бесплатно. Вы тратите ресурсы сервера базы данных на поддержание коннекта - CPU БД все время мониторит коннект на предмет новых запросов. Немного подумав над этим, вы поймете, что количество коннектов ограничено. Причем оно ограничено не тем, сколько вы их можете поддерживать в незанятом состоянии, а тем, сколько параллельных запросов может выполнять база данных без тротлинга. В качестве стартовой точки я обычно считаю, сколько % CPU занимает один самый тяжелый запрос, затем смотрю, сколько раз это уместится в 100% (просто делю 100 на это число) и получаю лимит на количество коннектов в общем, а на незанятые я оставляю половину от этого. Если вы чувствуете в себе уверенность, вы можете отходить от этой формулы.
Но у пулов соединений тоже есть свои минусы - может произойти утечка соединений. Это когда старые соединения все еще живут, но воспользоваться ими мы не можем. Поэтому ресурсы базы уходят в никуда, а мы можем упереться в свои же лимиты даже при незначительной нагрузке. Механизмы их возникновения бывают разные и они специфичны для каждого конкретного случая - где-то это работа прокси, где-то - особенности API для работы с базой данных. Как раз на такой случай лимит на количество незанятых коннектов и ставится ниже, чем на количество в общем. Это даст некоторое время, чтобы засечь утечку и предпринять меры.
Подытожим
Коннекты обычно не приносят много головной боли на начальных этапах разработки. Вообще работа с ними обычно делается один раз, во время настройки, и дальше тюнится по необходимости. Но эта необходимость возникает часто в виде непонятных ошибок, которые выкидываются в случайных местах, непонятных графиков в графане и суеты админов вашей базы данных. Я постарался собрать ту информацию, которая позволит вам не потерятся в такой ситуации и даже поможет определить суть проблемы.
Indifferentno
Учусь писать на го и если честно, спросил ИИ стоит ли мне париться по поводу дескриптора соединений или как его там, при работе с БД. Он ответил, что в принципе нет, его ж не дурачки писали, но так же выкатил мне тутор как менять количество открытых подключений и все такое. Я это к чему. Спасибо за статью. Для меня она прям в тему. Парочку небольших пробелов в теоретических знаниях залатал
nvantropov Автор
Рад стараться)
По поводу "не дурачки писали" – так можно почти про все распространенные IT технологии сказать. На мой взгляд важно понимать, как они устроены, чтобы понимать, как ими пользоваться, и выбирать то, что больше подходит для конкретной задачи. Я думаю, разработка в ближайшем будущем будет эволюционировать в эту сторону с развитием ИИ.