[{"data":1,"prerenderedAt":222},["ShallowReactive",2],{"content:\u002F10-databases\u002F00-intro":3},{"title":4,"description":5,"path":6,"body":7},"Введение в базы данных","Этот модуль нужен, чтобы backend-разработчик уверенно работал с данными, а не просто перекладывал JSON из HTTP в таблицу. База данных отвечает за факты, ограничения, транзакции, конкурентный доступ, поиск, хранение истории и восстановление после сбоев.","\u002F10-databases\u002F00-intro",{"type":8,"value":9,"toc":214},"minimark",[10,14,17,20,25,37,40,50,54,57,60,150,153,157,160,179,182,186,189,193],[11,12,4],"h1",{"id":13},"введение-в-базы-данных",[15,16,5],"p",{},[15,18,19],{},"Основной фокус будет на SQL и PostgreSQL, потому что это базовый инструмент для большинства Go-сервисов. Но важно не заучить синтаксис, а понять, какие гарантии даёт база, где она помогает приложению, а где требует аккуратного проектирования.",[21,22,24],"h2",{"id":23},"что-будет-внутри","Что будет внутри",[15,26,27,28,32,33,36],{},"Разберём реляционную модель, SQL, нормализацию, транзакции, уровни изоляции, MVCC, индексы, planner, production-возможности PostgreSQL, работу из Go через ",[29,30,31],"code",{},"pgx",", ",[29,34,35],{},"sqlc",", Squirrel и обзор других типов баз.",[15,38,39],{},"Общая логика модуля:",[41,42,48],"pre",{"className":43,"code":45,"language":46,"meta":47},[44],"language-text","data model -> constraints -> transactions -> indexes -> Go access layer -> production behavior\n","text","",[29,49,45],{"__ignoreMap":47},[21,51,53],{"id":52},"production-map","Production map",[15,55,56],{},"В production база почти всегда оказывается не \"деталью хранения\", а границей надежности сервиса. Ошибка в handler можно откатить релизом, а ошибка в данных часто живет дольше кода: она попадает в отчеты, расчеты, кэш, события, интеграции и ручные операции поддержки.",[15,58,59],{},"В этом модуле будем смотреть на базу через несколько рабочих вопросов:",[61,62,63,80],"table",{},[64,65,66],"thead",{},[67,68,69,74,77],"tr",{},[70,71,73],"th",{"align":72},"left","Область",[70,75,76],{"align":72},"Что может сломаться",[70,78,79],{"align":72},"Что должен видеть backend-разработчик",[81,82,83,95,106,117,128,139],"tbody",{},[67,84,85,89,92],{},[86,87,88],"td",{"align":72},"Модель данных",[86,90,91],{"align":72},"дубли, невозможные статусы, потеря истории",[86,93,94],{"align":72},"сущности, ключи, constraints, source of truth",[67,96,97,100,103],{},[86,98,99],{"align":72},"Запросы",[86,101,102],{"align":72},"медленные страницы, N+1, нестабильная пагинация",[86,104,105],{"align":72},"план запроса, индексы, объем данных, порядок сортировки",[67,107,108,111,114],{},[86,109,110],{"align":72},"Транзакции",[86,112,113],{"align":72},"lost update, гонки, двойное списание, read skew",[86,115,116],{"align":72},"границы транзакции, isolation level, retry policy",[67,118,119,122,125],{},[86,120,121],{"align":72},"Миграции",[86,123,124],{"align":72},"блокировки, долгий backfill, несовместимые версии приложения",[86,126,127],{"align":72},"expand-contract, батчи, rollback, validation",[67,129,130,133,136],{},[86,131,132],{"align":72},"Доступ",[86,134,135],{"align":72},"утечки чужих данных, широкие права service account",[86,137,138],{"align":72},"tenant\u002Fuser predicates, least privilege, audit trail",[67,140,141,144,147],{},[86,142,143],{"align":72},"Эксплуатация",[86,145,146],{"align":72},"рост таблиц, bloat, истощение pool'а, неясные ошибки",[86,148,149],{"align":72},"метрики, slow queries, connection pool, понятный error mapping",[15,151,152],{},"Такой взгляд нужен не для усложнения каждого маленького CRUD. Он помогает заранее заметить места, где простое решение перестает быть безопасным при росте данных, параллельных запросах, ретраях и нескольких версиях приложения.",[21,154,156],{"id":155},"связь-с-ratedesk","Связь с RateDesk",[15,158,159],{},"В проекте уровня RateDesk база становится источником истины для курсов, провайдеров, правил выбора ставки, статусов заявок и аудита решений. Для такой системы важно не только \"сохранить курс\", но и ответить на production-вопросы:",[161,162,163,167,170,173,176],"ul",{},[164,165,166],"li",{},"какая запись является фактом от провайдера, а какая - расчетным решением сервиса;",[164,168,169],{},"как запретить две активные настройки для одного валютного направления;",[164,171,172],{},"как сохранить историю изменения ставки и не перетереть ее очередным импортом;",[164,174,175],{},"как отличить \"курс не найден\" от \"провайдер временно недоступен\";",[164,177,178],{},"как доказать в разборе инцидента, какой курс и какая версия правила были использованы.",[15,180,181],{},"Статьи модуля будут давать теорию для таких решений: где помогает нормализация, где нужен snapshot, почему constraints важнее \"проверили в Go\", как читать планы запросов и как проводить миграции без долгих блокировок.",[21,183,185],{"id":184},"как-понять-что-освоил","Как понять, что освоил",[15,187,188],{},"Вы освоили модуль, если можете спроектировать простую схему, написать запрос с join и агрегацией, объяснить аномалии транзакций, подобрать индекс под запрос, прочитать базовый query plan и выбрать адекватный способ работы с PostgreSQL из Go.",[21,190,192],{"id":191},"рабочие-ориентиры","Рабочие ориентиры",[161,194,195,198,201,204,207],{},[164,196,197],{},"Constraints в базе не дублируют бизнес-логику, а защищают данные от неконсистентных состояний.",[164,199,200],{},"Индекс нужен под конкретный запрос и профиль нагрузки, а не \"на всякий случай\".",[164,202,203],{},"Хорошая модель данных обычно переживает несколько версий приложения. Её стоит проектировать медленнее, чем handler.",[164,205,206],{},"Миграция - это часть релиза: у нее должны быть совместимость, проверка результата и план отката.",[164,208,209,210,213],{},"Ошибка базы должна маппиться в понятное поведение API, а не превращаться по умолчанию в ",[29,211,212],{},"500",".",{"title":47,"searchDepth":215,"depth":215,"links":216},2,[217,218,219,220,221],{"id":23,"depth":215,"text":24},{"id":52,"depth":215,"text":53},{"id":155,"depth":215,"text":156},{"id":184,"depth":215,"text":185},{"id":191,"depth":215,"text":192},1781022065390]