Теория и практика миграции веб-систем на PostgreSQLИсточник: habrahabr
В последние месяцы проблематика миграции работающих систем на open-source решения для хранения данных захватила умы отечественных разработчиков. Особой популярностью в роли целевой платформы пользуется PostgreSQL. Причин тому можно назвать несколько:
Нам удалось убедиться в эффективности PostgreSQL несколько лет назад. Внедрение СУБД позволило ликвидировать серьезный технологический кризис на одном из крупных проектов компании. Подробный рассказ об этой success story состоялся на PG Day"14 Russia, прошедшем в прошлом году в Санкт-Петербурге. С тех пор нам довелось попробовать базу данных для решения широкого спектра проблем. Задача данной статьи - познакомить вас с нашим опытом миграций и поведать о некоторых практиках, которые значительно упростят процесс миграции и снизят возможные трудозатраты. Рассмотрим типичные заблуждения и неизбежные реалии коммерческой разработки, c которыми придется столкнуться. Обсудим кое-какие организационные нюансы, о которых редко задумываются при планировании миграции.
Что такое "миграция"?Общий смысл термина понятен всем. Миграция - это процесс смены одного хранилища данных на другое. Довольно часто миграцию воспринимают как сугубо технологический процесс (подготовка приложения к работе с новой СУБД), и в этом кроется самая распространенная ошибка. Миграция - процесс комплексный, включающий в себя организационные и учебные задачи, планирование, правильное распределение доступных руководителю разработки ресурсов, оценку рисков. Прежде чем бросаться переписывать код, надо взвесить все "за" и "против" и удостовериться, что мигрировать базу все-таки надо. Частенько, принимающие решение о миграции лица неадекватно оценивают необходимость смены базы данных, руководствуясь необъективными критериями. Нередко выясняется, что добиться приемлемого результата можно на уже имеющейся базе данных, хорошенько ее изучив и попробовав разные варианты. Если вы еще этого не сделали, забудьте про миграцию. "Съезжать" с неизученной базы - неоправданно высокий риск. Неизведанные подводные камни не раз и не два всплывут в процессе работы, порождая неучтенные зависимости, задержки в сроках и общее недовольство. Если вы принимаете решение сменить базу, которая более-менее прилично работает, просто потому что вы прочитали статью о новой и интересной NoSQL-разработке, пожалуйста, увольтесь и не создавайте проблем коллегам. Нет ничего хуже, чем некомпетентный "техлид" или руководитель, который принимает серьезные решения, основываясь на своих прихотях и личных интересах. Не торопитесь. Задача миграции сложна, неинтересна, трудозатратна и порождает неоправданно завышенные ожидания к получившемуся продукту. Вы столкнетесь с массой нюансов, потратите много времени на откровенную рутину, которой и так много в нашей работе, неизбежно "завалите" сроки по той или иной причине. В полученном после миграции продукте придется еще некоторое время подчищать скрытые проблемы. Слабо подкованное в технических вопросах руководство будет думать, что наступило светлое будущее, и с удвоенными усилиями начнет придумывать коммерческие задачи. В общем и целом, если у вас есть возможность не делать миграцию - не делайте ее. Постарайтесь решить проблему существующими средствами. Если же другого выбора у вас нет, все варианты решить задачу с минимальными усилиями вы исчерпали, (поту-)сторонние силы вынуждают вас приступить к миграции, и вы твердо приняли решение двигаться вперед - читайте дальше.
С чего начать миграцию?Миграция - это перенос данных в новое хранилище. Логично предположить, что начинать надо с оценки состояния текущего хранилища. Изучить схему, оценить её запутанность. Сколько таблиц в ней хранится, какие на них определены индексы, триггеры, внешние ключи. Каковы объемы данных, которые хранятся в этих таблицах? Есть ли какой-то код, который располагается внутри хранилища (хранимые процедуры и подобные вещи)? Какие компоненты и сервисы вашей системы работают с базой? Какой код приложения взаимодействует с базой? Ответы на указанные вопросы позволят понять несколько важных вещей. Прежде всего, сколько кода предстоит переписать, внутри базы и на стороне приложения. Не лишним сразу будет прикинуть для себя, какой код является более или менее сложным. Предположим, что вы, как компетентный "техлид", хорошо знаете способности и возможности своей команды. Следовательно, вы сразу можете понять, кому какой пласт работ можно доверить и, исходя из этого, какую часть коллектива необходимо задействовать в подготовке к миграции. Миграция предполагает, что в какой-то момент текущее хранилище придется остановить с целью выгрузки оттуда данных и последующей отправки в новое хранилище. Это, так или иначе, предполагает время простоя для всех компонентов системы, работающих с хранилищем. Уже на данном этапе полезно подумать, чем это обернется и какие есть обходные пути, если выключение сервисов категорически недопустимо. Если неприятная перспектива миграции без "даунтайма" замаячила на горизонте, не лишним сразу будет подумать о том, можно ли разделить миграцию на несколько последовательных этапов. Здесь все очень сильно зависит от конкретной архитектуры вашего отдельно взятого проекта, однозначный ответ дать не возможно. Могу лишь предостеречь от изобретения чего-то, о чем вы и ваши коллеги будете потом всю жизнь жалеть. Допустим, вы хотите смигрировать базу с MySQL на PostgreSQL, и для разбиения задачи на части намереваетесь сделать PostgreSQL slave-узлом "мускуля". Прием вполне осуществимый и на поверку кажущийся нехитрым: включить репликацию, "парсить" бинарный лог самодельным скриптом, преобразовывать SQL-инструкции в PostgreSQL-совместимый формат и засовывать в целевую базу. На практике, это обернется кошмаром и адскими муками. Каждая приходящая на телефон "смска" будет заставлять вас нервно вздрагивать и ожидать, что пришел "привет" от сломавшегося скрипта, которому не удалось разобрать очередной пришедший запрос. Нет смысла объяснять, почему такие вещи крайне опасны. Любой человек, поработавший в разработке более-менее крупного, коммерчески успешного сервиса, понимает, что с большой долей вероятности разделенная на два этапа миграция будет спешно приторможена после осуществления первой половины задуманного и команде придется жить с "временными" решениями еще пару лет. Ну и, конечно же, факт оценки схемы позволит составить представление о том, насколько безумно устроено текущее хранилище данных, и с какими проблемами коллектив объективно столкнется при подготовке. На текущем этапе это мало чем поможет, кроме как дополнительной подсказкой при оценке сроков и приблизительным пониманием, о каких нюансах следует задуматься заранее. Вооружившись вышеописанным знанием, можно отправляться в кабинет к начальству и предметно обсуждать сложность предполагаемой миграции, количество требуемых людей, подход к разработке новых фич во время миграции, возможность простоя сервиса в момент фактического переезда и подобные аспекты. Требуемые сроки всегда завышайте. Вам не только код перерабатывать, но еще команду готовить, тренироваться, всякие-разные подводные камни отыскивать. Одна лишь переработка кода точно займет на треть больше времени, чем вы ожидаете.
Переработка схемы и данныхПрежде чем хоть как-то задуматься об адаптации схемы данных, выкиньте из базы лишний мусор. Мне еще ни разу не довелось встретить базу данных, сколь угодно большую или малую, в которой не было бы временных таблиц, устаревшей информации и прочих непонятных архаизмов. Абсолютно в любой базе есть bloat. Чем сложнее и старше ваш проект, тем больше вероятность, что вы можете смело выкинуть треть таблиц, ни коим образом не нарушив логику работу приложения. Не ленитесь, пройдитесь по схеме, поищите вхождения в коде и устраните все лишнее. Аналогичной экзекуции необходимо предать все данные, которые не нужны для функционирования сервиса. В любой системы есть миллионы и гигабайты статистики "за все время", которые жизненно необходимы для коммерческого отдела, но на практике изымаются на свет Божий от силы пару раз за год. Терабайты архивных логов, о которых так увлекательно коллеги рассказывают на конференциях. Все это надо безжалостно извести из базы данных. Если кто-то будет сопротивляться, аргументируйте это снижением затрат времени на миграцию вообще и перенос данных в частности. Меньше данных - быстрее перекинем их в новую базу и запустим сервис. Математика простая. Теперь пора подумать над адаптацией схемы данных. Сразу же огорчу, автоматизировать этот процесс невозможно и, в общем-то, не нужно. Не тратьте временя на поиск инструмента, который автоматически сконвертирует вашу схему. То, что вы получите на выходе, с первого раза не заведется, и вы потратите кучу времени на "допиливание" результатов труда такой программы. Схема данных функционирующего продукта - явление слишком сложное и четкой формализации не поддающееся. Любая сторонняя утилита выдаст некоторую аппроксимацию того, "как быть должно", и абсолютно проигнорирует большинство тонкостей вашей конкретной системы. Пожалуй, хуже идеи поиска такой утилиты может быть только лишь идея самому написать такую штуковину. Не тратьте время! Ну зачем вам делать универсальный инструмент для одноразовой и уникальной задачи? Самый печальный случай из моей практики состоял в попытке написания такой утилиты моими предшественниками по управлению одним крупным проектом. Программу, на лету конвертирующую крайне непростой SQL-код с кучей DDL-конструкций, разрабатывали около полутора лет. Адекватно работающей ее так никто и не увидел. Выход из этой ситуации очень прост, но крайне противен и омерзителен для любого уважающего себя программиста. Нужно сесть и, убив один день своего рабочего времени, вручную переписать схему под новое хранилище, с умом и аккуратно используя функцию Copy & Replace. Занятие не из приятных, но я уже упоминал, что процесс миграции, в принципе, не сильно воодушевляющее мероприятие. Тем не менее, пользы от такого подхода масса. Вы досконально изучите всю схему данных, найдете еще что-нибудь бесполезное, что можно выкинуть, и проработаете все нюансы. Вы наверняка увидите какие-то типовые проблемы и их возможные решения. Не ленитесь, конспектируйте. Скоро пригодится. Со схемой разобрались, пора мигрировать данные. Тут вся история выглядит ровно аналогичным образом. Забудьте про автоматизацию и универсальные решения. Пишите на коленке нехитрый скрипт, который выгрузит данные из исходного хранилища и импортирует в целевое. В нашей практике, наиболее эффективным способом это сделать оказался банальный "дамп" табличек в формате CSV. Все более-менее адекватные базы позволяют сделать такой "дампик", сконфигурировав некоторое количество параметров (символ экранирования, разделитель, перенос строки, символ NULL и т.д.). Файлики получаются сравнительно небольшие, аккуратные, хорошо сжимаются gzip-ом для последующей передачи по сети (опять же, банальным rsync-ом) и очень быстро кушаются PostgreSQL (хинт: используйте COPY). Сделать скрипт - вопрос одного вечера. Особая прелесть такого инструмента заключается в том, что вы легко можете запрограммировать в него обработку любых специфических ситуаций. Не стесняйтесь, "хардкодьте". Если в какой-то табличке умудрились сохранить "копипасту" из Microsoft Word со спецсимволами, пройдитесь по файлику с помощью perl/sed или чего-нибудь еще, откорректируйте и сложите на импорт. В общем-то, такой подход - единственный, который позволит избежать неожиданностей и гарантировать полный перенос всей базы данных. Аналогичным образом подготовьте скрипты, которые импортируют какие-то дополнительные объекты, например, значения sequences для соответствующих колонок. Как только инструменты сделаны и достигнут успешный экспорт-импорт данных, необходимо организовать автоматизированную проверку всего этого хозяйства. Представьте, что это backup & recovery решение. Любой уважащий себя инженер знает, что недостаточно только лишь штатно "снимать" резервную копию. Ее надо так же штатно проверять, поскольку "бекап" имеет обыкновение ломаться, чего лучше не допускать. Поступайте аналогично. Раз в неделю осуществляйте автоматизированный (или ручной, кому как удобнее) прогон экспорта-импорта со свежими данными из "боевой" базы. Работать над подготовкой к миграции еще предстоит очень долго и, с большой долей вероятности, в базе за это время произойдут какие-то изменения или появятся данные, которые вы не учли. Чтобы не бороться с этими сюрпризами в боевых условиях во время реальной миграции, своевременно выявляйте проблемы и решайте их. Периодическая проверка процедуры экспорта-импорта поможет вам оценить затраты времени на перенос данных. В этот момент у вас есть возможность подумать, где и что можно оптимизировать. Например, сделать инструмент многопоточным и параллельно выгружать/загружать данные из нескольких таблиц.
Переработка кодаАдаптация кода приложения - самая неизбежная и печальная часть всего мероприятия. Прежде чем начинать, надо определиться с некоторыми важными моментами. Во-первых, что и в каком порядке надо переписывать. Общее правило таково, что наиболее сложные и запутанные вещи переделываются в первую очередь. Вещи с плохо формализуемой бизнес-логикой, которые невозможно проверить четко заданным набором входных параметров (обычно, это какие-нибудь вещи типа биллинга или расчета аналитики), лучше подготовить сразу и крутить постоянно на тестовом стенде. Со временем будут всплывать разные косячки, которые вы сможете своевременно поправить до отправки в "продакшн". Очевидно, что чем больше таких прецедентов случится до выкатки решения "в бой", тем лучше. Во-вторых, подготовьте базу знаний. С большой долей вероятности ваши коллеги и подчиненные не обладают хорошо развитыми навыками воспитания новой базы данных. В связи с этим, обозначьте оптимальные способы решения отдельных задач. Найдите типовые, повторяющие проблемы в коде приложения и опишите, как их нужно устранять. Это сэкономит время всем соучастникам процесса. В-третьих, забудьте про рефакторинг, оптимизацию, улучшение кода и прочие прелести жизни программиста. Ваша задача - перенести приложение на новую платформу максимально близко к оригиналу. Не переписывайте бизнес-логику. Не применяйте паттернов проектирования. Не заменяйте одни библиотеки на другие. Запретите это всем своим подчиненным программистам. Любые попытки сделать что-то подобное приведут к появлению новых багов, задержке сроков и общему беспокойству. Что-то переписывать и оптимизировать можно только в одной ситуации: когда переписанный участок кода, по тем или иным причинам, не справляется эффективно со своей задачей. В-четвертых, формализуйте и структурируйте правила для написания нового кода. Крайне велика вероятность того, что миграцию затеяли по причине невозможности адекватно поддерживать текущую систему. Если вы не хотите повторять ошибок прошлого (а их лучше не повторять, повторную миграцию начальство не одобрит), составьте общие правила и заставьте всех им следовать. Помните, что хорошо написанным является тот проект, который можно легко и системно поддерживать. Раз уж затеяли большую переработку, приложите усилия к тому, чтобы жизнь после миграции не была таким же мучением, как и жизнь до нее. Теперь можно открывать текстовый редактор и приступать к переработке кода. Насколько это окажется сложным и сколько времени займет, никто вам сказать не может. Ваш код, вам виднее. Возможно, вам повезет и у вас не будет трехэтажных SQL-запросов в каждом модуле, во всю использующих database-specific "фичи" языка. К слову, я считаю, что это один из немного сценариев, где использование ORM для исполнения CRUD-запросов сильно объективно ускоряет разработку и упрощает жизнь программиста, а не наоборот.
Про тестированиеЕсли вы ожидаете, что на время миграции представители бизнеса полностью приостановят коммерческое развитие продукта и связанные с ним задачи на техотдел, то вы очень наивны и глубоко заблуждаетесь. Задачи появляться будут, и делать их придется. Как решать эту дилемму? Очень просто. Выделите часть команды на поддержку существующей системы. Обучите их тем же правилам разработки, что и людей, работающих над миграцией. Очевидно, что после миграции вся команда будет разрабатывать под новую платформу, поэтому все инженеры без исключения должны понимать и принимать новые реалии разработки. Внедрите новое правило работы над задачей: код не уходит в "продакшн" до тех пор, пока ваши бойцы не подготовили и протестировали версию для новой платформы. Да, это фактически означает, что вы будете для каждой задачи делать две версии кода, старую и новую. Все это противно, нудно и неприятно. Но других альтернатив нет. Никто и никогда не выделит вам время после миграции на адаптацию новой функциональности. Не говоря уже о том, что спустя месяц накопившийся стек изменений выйдет из-под контроля и концов вы потом уже не отыщете. На выходе получится нестабильный и потерявший часть функциональных возможностей продукт. Начальство имеет тенденцию на такое обижаться. Если вам вдруг повезло и представители бизнеса согласились приостановить коммерческую разработку на время миграции, возрадуйтесь. Но вероятность этого крайне мала. Процесс тестирования, как и все прочее, не имеет каких-то секретов и серебряных пуль. Нужен компетентный тестировщик, который все перерабатываемое будет досконально проверять на предмет соответствия поведения новой логики по отношению к старой. Автоматическое тестирование является вторичным аспектом. Лично я крайне не советую тратить время на написание "автотестов", лучше потратьте это время на доскональную проверку вручную. Если у вас уже есть развитая инфраструктура и написаны тесты, обязательно их используйте. Иначе, не заморачивайтесь. Тестируйте много и часто, делайте несколько подходов к уже ранее проверенному. Будут всплывать новые и новые изъяны. Не жалейте сил и времени. Чем меньше проблем потом всплывет в "продакшене", тем успешнее будет результат работы, что приведет к категорическому одобрению со стороны руководства и поспособствует воспитанию трудовой дисциплины в вашем коллективе.
Про обучениеКак уже отмечалось выше, крайне важно задать правильный вектор движения к светлому будущему для ваших программистов. Грамотно сформированные правила разработки обеспечат системный подход к работе над задачей и внесут очень важный вклад в профессиональный рост всего техотдела. Безусловно, одних лишь правил разработки и прочих "гайдлайнов" мало. Чтобы коллеги оценили ваши усилия, коллектив надо обучать. Моя многолетняя практика показала, что обучение программиста - процесс сугубо индивидуальный, вне зависимости от того, какой задачей человек занимается, будь то миграция или стандартная ежедневная рутина разработки. Хотите получить компетентного специалиста? Извольте потратить время. Что для этого надо сделать? Погрузиться в разработку с головой и следить, кто что и как делает. Организуйте командное ревью кода. Не стесняйтесь заниматься "микроменеджментом" и наглядно объяснять, что вам не нравится и по какой причине. Всегда обязательно аргументируйте свою позицию. Докапывайтесь к деталям, добивайтесь того результата, который хотите увидеть. Если кому-то из ваших программистов не понятен тот или иной момент, не поленитесь написать или объяснить, почему все работает именно так, а не иначе. Это крайне неблагодарная работа, и люди будут на вас обижаться. Кто-то хронически не сможет подстраиваться под общую идеологию, обидится окончательно и уйдет из проекта. Результат появится лишь спустя некоторое время, когда все программисты привыкнут системно работать по одним и тем же правилам и оценят на практике, насколько легко и приятно разрабатывать код и поддерживать систему, когда все написано в едином стиле, качественно и понятно. И это не смотря на то, что в коллективе может трудиться до 10 человек и более. Запуск проекта после миграции - это своего рода новая жизнь для продукта. Как сказал недавно один мой коллега, никогда не удавалось застать тот момент, когда очередной проект перещелкивается из состояния "Сразу Сделать Все Правильно" в состояние "Изначально Все Было Сделано Неправильно". Тем не менее, все соучастники будут решительно настроены на то, чтобы в этот раз сделать лучше. Воспользуйтесь моментом и направьте энергию своих соратников в действительно нужное русло.
Час ЧНепосредственно процедура миграции - задача не менее сложная и ответственная, чем все то, что ей предшествовало. К ее выполнению тоже надо подготовиться. Сядьте с коллегами и досконально напишите последовательность выполнения миграции. Кто что останавливает и включает, кто куда какой код выкатывает и т.д. Пишите все очень подробно. На выходе каждый из сотрудников должен иметь пошаговый план, который позволит ему выполнять действия, не задумываясь очень сильно, что за чем следует и не пропустил ли он чего-нибудь. Подготовив такой сценарий, обязательно проведите несколько тестовых прогонов. Это позволит окончательно убедиться, что все работает как надо, сгладить неровности и устранить недочеты в составленном плане. Обязательно предусмотрите план Б. Имеется вероятность, что где-то что-то пойдет не так. Серьезные и непредвиденные обстоятельства во время выполнения миграции являются гарантированным поводом "откатиться" и подготовиться к повторному заходу. Если такая ситуация произойдет, вы должны быть к ней готовы, вся последовательность отката к исходной точке точно также должна быть расписана и отрепетирована. Потренируйтесь выполнять процедуру отката с командой. Обсудите возможную процедуру отката с представителями коммерции, они должны быть к этому морально готовы. Не менее обязательным является симуляция поведения системы после штатного переключения. Это была одна из ключевых ошибок нашей первой миграции. Мы проверили производительность биллинга в штатных условиях на новой базе, но не учли тот факт, что включение биллинга после простоя в обслуживании приведет к накоплению большого числа задач на обработку. На практике, во время миграции, выяснилось, что биллинг с задачей не справлялся. Пришлось отменить миграцию, переделать алгоритмы биллинга с учетом специфики PostgreSQL и попробовать заново. Не повторяйте наших ошибок, проведите симуляцию включения сервиса после простоя. Полученные по результатам тестовых прогонов оценки по времени обговорите с нетехническим персоналом (менеджеры, сотрудники службы поддержки и т.д.). Им предстоит держать оборону и отбиваться от нервничающих партнеров. Объясните, что и как будет происходить, будьте готовы своевременно их оповещать о непредвиденных обстоятельствах и изменениях в планах. Планируйте миграцию на утро понедельника. Все должны быть отдохнувшие и выспавшиеся. Запретите накануне злоупотреблять спиртными напитками и иными веществами, "рубиться" всю ночь в MMORPG. Не давайте ставить на рабочие станции обновления ПО и ОС, пересобирать ядро линукса. Убедитесь, что не будет проблем с доступом в Интернет, организуйте резервную опцию, на случай поломки основного соединения. Подумайте о других возможных обстоятельствах, которые могут вам помешать.
Светлое будущее?Как понять что миграция прошла успешно? Однозначно это станет известно только лишь спустя некоторое время. Но несколько более-менее объективных критериев выделить не помешает. Прежде всего, процедура миграции прошла в запланированные сроки, плюс-минус 30 минут на разные мелочи и человеческий фактор. Любое отклонение от исходного плана означает, что что-то пошло не так, и оно может дать о себе знать в будущем. Вам не потребовалось какими-то страшными костылями "подпирать" ключевые компоненты бизнес-логики. Новую жизнь так не начинают. Крепко подумайте, готовы ли вы взять на себя риск выпустить такое в окончательный и безвозвратный "продакшн". В процессе не вылезли дикие баги, на устранение которых вам понадобится еще месяц простоя по бизнес-задачам. Никому это не понравится, вам в том числе. Эти проблемы надо выявлять до миграции. Откатитесь, разрешите, протестируйте и попробуйте еще раз. Любая неожиданность, с которой не ясно, как бороться - повод откатиться. Помните, самое главное - не потерять то, что уже работало до вашего вмешательства. Никакие задержки по срокам не сравнимы с такими рисками. Не уверены - не принимайте поспешных решений. Если миграция состоялась, то спешу вас поздравить. Проблем в вашей жизни теперь станет еще больше. Продукт нужно будет еще долгое время "причесывать" и оптимизировать под возможности новой базы. Задач, не смотря на это, станет еще больше. Заданную высокую планку качества работы в техотделе придется теперь постоянно держать, чтобы не довести дело до повторной миграции. Так что не ожидайте, что задача миграции оправдается себя в тот момент, когда она будет исполнена. Эффект появится позже, когда стабильно работающий сервис перестанет тревожить вас каждую неделю ночными "падениями", натренированный и обученный отдел будет решать задачи дружно, слаженно и оперативно. Для нас миграция на PostgreSQL не является только лишь средством решения крупных технологических проблем. Это отличный инструмент для построения эффективной разработки, отладки соответствующих процессов в команде и системное развитие общей базы компетенций внутри компаний. Одна из причин, по которой сообщество с возрастающей регулярностью выбирает эту СУБД для своих задач, это осознание, что PostgreSQL уже достаточно давно используется для решения потребностей крупного бизнеса. Даже представители промышленного и банковского сектора (областей, в которых традиционно правят балом коммерческие СУБД) не боятся применять бесплатную и открытую технологию. Ярким примером является крупнейший бразильский банк Caixa. Весь процессинг банковских карт в банкоматах страны построен с использованием PostgreSQL. Крайне интересный business case был описан одним из архитекторов системы на канадском PGCon"e в 2010 году. Мы решили пригласить коллегу из далекой Бразилии поделиться секретами успеха на предстоящий питерский PG Day. PostgreSQL зарекомендовал себя как продукт, способный решать серьезные задачи. Именно это мотивирует нас к внедрению технологии в собственных проектах и внесению вклада в ее популяризацию на отечественном рынке разработки. |