Go API тормозит при свободном CPU: как проверить очередь пула БД
Процесс почти не использует CPU, но запросы долго ждут перед обращением к базе. Один из возможных барьеров — лимит открытых соединений. Увеличение числа goroutine не ускоряет работу, если все они ждут одни и те же доступные слоты.
Как разобрать ситуацию
sql.DB управляет пулом соединений, а не представляет одно постоянное соединение. При ограничении MaxOpenConns операции могут ждать освобождения ресурса. Изучите DB.Stats, включая ожидания, и сопоставьте их с длительностью запросов и временем удержания соединения. Сначала ищите незакрытые Rows, долгие транзакции и медленный SQL, затем оценивайте лимит.
Пример и ожидаемый результат
Учебная модель: одновременно пришли десять операций, доступно два соединения, каждая работа занимает 100 миллисекунд. При идеальных условиях потребуется пять последовательных волн, около 500 миллисекунд для последней. Свободный CPU не отменяет эту очередь.
package main
import "fmt"
func main() {
jobs, slots, ms := 10, 2, 100
waves := (jobs + slots - 1) / slots
fmt.Println(waves, waves*ms)
}
Локальная программа выводит 5 500: пять волн и 500 миллисекунд в упрощённой модели. Это проверка арифметики ограничения, а не benchmark sql.DB.
Проверка перед применением
- Проверьте WaitCount и WaitDuration в сопоставимом интервале наблюдения.
- Проверьте, закрываются ли результаты чтения и завершаются ли транзакции на ошибочных путях.
- Не повышайте лимит без оценки доступной ёмкости самой базы и числа экземпляров приложения.
Учитывайте все реплики приложения: лимит двадцать на процесс при десяти репликах означает потенциально двести соединений. Местное ускорение одного сервиса способно перегрузить общую базу и замедлить остальных. Проверка должна включать внешнюю систему и хвосты времени ответа, а не только локальный показатель свободного процессора.
Границы решения
Реальные операции имеют разную длительность и могут занимать больше одного ресурса. Модель не подключается к PostgreSQL. Рост ожиданий поддерживает гипотезу о пуле, но не доказывает, что повышение лимита является правильным лечением.
Если базовый Go уже знаком и хочется связать HTTP, хранение данных и обработку отказов в одну систему, посмотрите наш платный курс
Микросервисы на Go: HTTP, PostgreSQL, gRPC, Kafka и Docker. Перед записью сравните программу со своей задачей и подготовкой.
Техническая справка: go.dev: документация, pkg.go.dev: документация.
Следующий шаг: Go backend: HTTP, конкурентность и устойчивость сервиса. По смежным вопросам: Отмена HTTP-запроса в Go: как убедиться, что context дошёл до клиента, Как оборачивать ошибки в Go и не потерять машинную проверку причины.
