Глава-выдержка из книги 2006 года
Ведущие сразу задают рамку: глава во многом выдержка из книги 2006 года про рефакторинг баз данных, написанной одним из авторов нынешней книги, — Александр специально в неё заглядывал и сравнивал.
Участники отмечают и другое: все прочитанные раньше книги про данные начинались с формулы «системы приходят и умирают, а данные остаются навсегда», и здесь авторы, кажется, впервые с этим немного спорят: данные тоже стареют, тоже должны эволюционировать, часть пора архивировать.
Сквозная мысль главы — у разработчиков есть тесты и рефакторинг, а у тех, кто отвечает за данные, этих практик будто нет; книга всё время разводит разработчиков и DBA с их отдельным миром, процессами и инструментами, привязанными к вендору вроде Oracle. Оттуда же комитет по миграциям из самых заслуженных DBA, одобряющий крупные изменения схемы: таких, замечают участники, почти не осталось.
Дальше — пробежка по рекомендациям: относиться к данным как к коду, тестировать их, версионировать схему и привязывать её версию к версии кода, менять инкрементально, использовать автоматические инструменты — в Java, например, популярен Flyway, и накатка миграций давно живёт внутри пайплайна.
Отдельный сюжет — undo-миграции. Авторы советуют их не писать: придётся поддерживать и проверять две версии схемы, а заранее вы всё равно не знаете, как захотите откатиться и надо ли сохранять данные. Участники добавляют, что undo-скрипты, дописанные задним числом, разводят историю миграций вилкой, а непроверенная undo-миграция — парашют, который никто ни разу не раскрывал. Александр спорит: у него недавно был крупный инцидент, где необратимым оказался второй шаг двухшаговой миграции и часть данных потом восстанавливали.
Expand/contract, триггеры и владельцы данных
Главный пример главы — несколько приложений поверх одной базы, хотя раньше авторы от такой схемы уводили. Предлагается паттерн expand/contract с переходным периодом, когда живут две схемы сразу, а данные лежат в старом и новом форматах.
Участники раскладывают варианты: чистая база без интеграций и накопленных данных бывает разве что во время разработки; при одной точке интеграции исторические данные можно разом конвертировать — в книге это разложение имени и фамилии; а в жизни приложение крутится в нескольких инстансах, релиз идёт без остановки сервиса, и старый код неизбежно встречается с новой схемой.
Книжное решение — триггеры, дублирующие запись в оба формата; после перехода их и старую колонку удаляют. Один из участников возражает: команда переключится на следующий спринт и может не убрать временные триггеры. Тогда скрытые зависимости останутся, и позднее изменение давно ненужной колонки затронет работающую систему.
Вторая половина главы — про запутанность и транзакции. По мысли авторов, связанность через данные особенно сильна, а транзакции в главе сравниваются с сильным ядерным взаимодействием; двухфазный коммит упомянут, но по существу почти не разобран. Николай иллюстрирует компромисс распределённых процессов примером перебронирования: пассажир приходит на своё место в самолёте, а там уже кто-то сидит — вместо ACID работают извинения. Если транзакционный контекст вылезает наружу, границы сервиса, скорее всего, определены неверно.
Отсюда вопрос о владельце данных: право записи остаётся у одного сервиса. Другие могут читать через него, держать кэш или строить собственное представление из потока изменений. Участники связывают выбор с ценой сетевых обращений и необходимостью не дать соседним приложениям менять чужие данные напрямую.
Нашлись и фитнес-функции: данные разъехались по нескольким базам, удалять нужно в каждой, решение — событие, которое слушает каждое приложение и чистит свой кусок, а фитнес-функция проверяет распределённое удаление. Участники смеются, что фитнес-функция обязана появиться в каждой главе, и замечают, что здесь она добавляет связность между квантами, которые должны быть независимыми.
Ценность данных и ограничения хранения
Разговор о возрасте данных начинается с привычки хранить всё. Участники описывают петабайты в HDFS, из которых при переезде полезными оказываются лишь десятки или сотни терабайт. Глава предлагает обсудить с бизнесом, что действительно используется, а остальное перенести в холодное хранилище.
Николай возражает против идеи бессрочного архива: кроме пользы для бизнеса, нужно обсудить с юристами договоры с пользователями, сроки хранения и требования удаления. Холодное хранилище не снимает этих вопросов. Упомянутый им юридический кейс в эфире не проверяют; ведущий просит позднее добавить ссылку.
Что изменилось за семнадцать лет
Вердикт Николая: проблемы главы настоящие, но предложенные решения он считает устаревшими. Главное приобретение за это время — поддержка JSON в базах: дополнительные поля можно добавлять без отдельного изменения схемы. Если база разделена на пятьдесят шардов, это избавляет от необходимости одновременно менять схему на каждом.
Однако отсутствие поля должен корректно обрабатывать код. Это не отмена модели данных: Николай отдельно выделяет поля, которые часто используются в соединениях таблиц. JSON помогает с изменяемой частью записи, но не избавляет приложение от договорённостей о её смысле.
С распределённой целостностью сложнее. Участники обсуждают, как двухфазный коммит замедляется при росте числа участников и почему фаза подготовки становится дорогой. Саги предлагают другой компромисс: простой основной сценарий и компенсирующие действия, возвращающие бизнес в приемлемое состояние, не обязательно точно в исходное.
Но компенсацию кто-то должен запустить, при необходимости повторить и проверить. По опыту Александра, команды готовы реализовать такую логику для критичного сценария, но неохотно распространяют её дальше. Отсюда цена разделения системы: независимая разработка не устраняет работу по согласованию данных.
Участники называют и альтернативу — NewSQL: масштабирование вместе с транзакционными гарантиями. Для пользователя это может выглядеть как обычная база, но эксплуатация сложна и стоит денег. Николай ожидает движения к единому интерфейсу базы и большей роли моделирования данных; возможные будущие трудности Figma после разделения базы он приводит как прогноз, а не известный результат.
В финале возвращаются к вопросу о поиске текста в JSON-колонке базы объёмом 40 ТБ. Николай говорит, что распараллеливание по сотням блоков может дать быстрый ответ, но регулярный запуск такого поиска дорог. Разовый аналитический запрос и постоянный сценарий требуют разных решений; сравнения конкретных СУБД или измерений в ответе нет.
Что стоит унести с собой
- 01Участники считают проблемы миграций из главы актуальными, но спорят с её организационными и техническими рецептами. Версионирование, тестирование и автоматизация остаются основой обсуждения.
- 02Спор об undo-миграциях нельзя свести к запрету откатов: инцидент Александра показывает цену необратимого шага, а непроверенный скрипт не даёт надёжного пути восстановления.
- 03Для expand/contract нужно определить владельца записи, совместимость старого и нового кода и завершение переходного периода. Возражение против триггеров связано в том числе с риском оставить временные зависимости навсегда.
- 04JSON упрощает добавление полей, если код обрабатывает их отсутствие. Распределённая целостность остаётся отдельной задачей: саги требуют компенсаций, а NewSQL переносит часть сложности в базу и её эксплуатацию.