Чему я научился, перенеся 25 миллиардов записей

Миграция данных — это просто процесс перемещения данных из одного места в другое.

По мере роста бизнеса миграция данных практически неизбежна. Вам может понадобиться хранилище большего размера для поддержки роста объема данных. Или вам нужно изменить формат данных из-за меняющихся требований.

Недавно я завершил миграцию данных с помощью Elasticsearch. Всего было 25 миллиардов документов, и весь процесс занял почти 14 дней!

Как бы просто это ни звучало, миграция данных — непростая задача. Есть много предостережений для плавной миграции без простоя системы.

В этой статье я поделюсь с вами знаниями, которые я получил после завершения масштабной миграции данных. Это методы, которым вы можете следовать независимо от архитектуры вашей системы. Давайте начнем! 🏃

Флаг чтения-записи

Перед выполнением переноса данных в непрерывно работающей системе необходимо решить две проблемы.

Во-первых, как обеспечить актуальность данных в новом местоположении? На данный момент ваши API все еще выполняют запись в старое хранилище. Любые обновления данных, которые были перенесены, будут пропущены в новом хранилище.

Во-вторых, как плавно перенести API для использования нового хранилища? Точно так же, если произойдет что-то плохое, вам нужно будет откатить свои API обратно в старое хранилище. Плавный переход в любое время между старым и новым хранилищем является ключом к миграции без простоев.

Чтобы решить эти проблемы, мы введем флаг чтения-записи. Флаг — это переключатель, который включает или выключает определенные функции во время выполнения без какого-либо изменения кода.

Наш флаг имеет 4 значения, каждое из которых представляет определенный способ взаимодействия API с хранилищем данных.

Вначале API взаимодействуют только со старым хранилищем, что является поведением по умолчанию.

Когда вы переключаете флаг на 1, API считывают данные из старого хранилища, но записывают в оба хранилища. Запись данных в оба расположения должна начаться до начала переноса данных. Это гарантирует, что новое хранилище будет обновляться, а старое хранилище останется работоспособным.

После завершения миграции API-интерфейсы могут считывать данные из нового хранилища. Если возникают непредвиденные ошибки, вы можете сразу переключить флаг обратно на 1. Благодаря «двойной записи» старое хранилище по-прежнему можно использовать.

Наконец, когда все в порядке, вы можете переключить свои API на полное использование нового хранилища. Это означает конец миграции.

Обратите внимание, как поток данных динамически управляется одним переключателем. На протяжении всего процесса не требуется никаких изменений кода или развертывания. Вся связанная логика может быть предварительно подготовлена ​​до начала миграции.

На уровне кода мы можем легко выполнять условные проверки с помощью флага чтения-записи. Псевдокод выглядит примерно так, как показано ниже.

// 4 values of the flag
oldOnly = 0
writeBoth = 1
readNew = 2
newOnly = 3
// helper functions for condition checks
function isReadNew(flag) {
    return flag == readNew || flag == newOnly
}
func isWriteOld(flag) {
    return flag != newOnly
}
func isWriteNew(flag) {
    return flag != oldOnly
}
// read logic
if isReadNew(flag) {
    read_new()
} else {
    read_old()
}
// write logic
if isWriteOld(flag) {
    write_old()
}
if isWriteNew(flag) {
    write_new()
}

Планирование ресурсов

Одна из серьезных проблем с «двойной записью» заключается в том, что она фактически удваивает обычный трафик записи в вашу систему хранения данных.

В зависимости от вашей системы, она может быть не в состоянии удерживать это дополнительное давление. Миграция может добавить задержки к вашему обычному трафику API. В худшем случае это может полностью нарушить работу системы, что является плохой новостью для вашего клиента!

Здесь вам нужно проявить должную осмотрительность, чтобы понять возможности вашей системы. Один из способов сделать это — провести нагрузочное тестирование, чтобы оценить максимальный трафик записи, который может поддерживать ваша система данных.

Как правило, стресс-тестирование включает в себя запуск нескольких фоновых потоков и непрерывный «спам» одновременных запросов к вашей системе. Вы можете представить каждый поток как одного неумолимого клиента.

Я не буду вдаваться в подробности стресс-тестирования, так как оно заслуживает отдельной статьи. Я пробовал стресс-тестирование с помощью инструмента под названием wrk и с помощью написанного мной сценария Go.

После того, как вы будете уверены в результатах стресс-теста, вы можете начать задавать себе несколько вопросов.

Должно ли ваше новое хранилище данных находиться на отдельной физической машине? Могут ли старое и новое хранилище данных находиться в одном кластере? Могут ли они находиться в одном экземпляре базы данных?

Ответы на эти вопросы должны сопровождаться глубоким пониманием того, какое хранилище данных вы используете. Различные системы хранения по-разному реагируют на высокий трафик записи.

Понимание того, какое влияние миграция данных может оказать на вашу систему, важно для подготовки к непредвиденным обстоятельствам!

Проверка согласованности

Даже с флагом чтения-записи и деликатным планированием ресурсов могут возникать ошибки! Например, время вставки данных может истечь из-за проблем с сетью.

«Все, что может пойти не так, пойдет не так». - Закон Мерфи

Перед переключением флага чтения-записи на 2 (чтение нового) рекомендуется выполнить проверку непротиворечивости.

Как следует из названия, проверка согласованности сравнивает данные между старым и новым хранилищем. Он рассматривает старое хранилище как источник истины. При наличии несоответствий проверка выполняет исправление данных.

Если размер ваших данных огромен, проверку согласованности следует выполнять с помощью автоматизированного скрипта. В зависимости от вашей терпимости к ошибкам вы можете выполнить полную или выборочную проверку согласованности.

Сценарий проверки также должен сообщать о проценте найденных несоответствий. Если они превышают ожидаемый порог, возможно, вы захотите изучить основную причину, прежде чем переключать API на чтение новых.

При этом исправление данных может выполняться синхронно или асинхронно.

Если синхронно, сценарий проверки применит исправления сразу же после обнаружения несоответствий. В противном случае сценарий может поставить исправление в очередь, чтобы работник мог его применить.

Однако имеет ли значение метод исправления? Ну, это зависит… Сделайте паузу, если хотите подумать об этом 😏

Допустим, ваш API синхронно записывает данные в новое хранилище. Но вы выбрали асинхронное исправление при проверке согласованности.

В очереди могут возникать кратковременные задержки. К тому времени, когда работник подберет «исправление», хранилище может уже содержать более новые обновления из API. Затем рабочий процесс перезапишет более обновленные данные устаревшим «исправлением».

Как правило, синхронного исправления достаточно, поскольку оно простое.

Здесь я хочу подчеркнуть, что миграция данных усложняется, когда данные начинают течь во всех местах. Вы должны понимать, как ведут себя ваши данные, иначе вы столкнетесь со странными несоответствиями.

Важность ведения журнала

Если вы какое-то время занимаетесь программированием, вы согласитесь, что системные журналы — лучшие друзья разработчиков. В миграции данных я хочу выделить два типа журналов, которые сделают вашу жизнь в десять раз проще.

Журналы ошибок

Помните, ошибки могут произойти в любой момент, и к ним нужно быть готовым. При возникновении ошибки ваш сценарий миграции должен вывести ее из журнала для целей отладки.

Кроме того, вместо того, чтобы полагаться на проверку согласованности для исправления ошибок, я настоятельно рекомендую вам исправлять ошибки по мере их возникновения.

Если миграция занимает 14 дней, полная проверка данных также займет еще 14 дней. Это может быть неосуществимо, если ваш проект находится в спешке для завершения.

Вот где журналы ошибок чрезвычайно полезны. Вы можете выполнить исправление вручную, проверив журналы на наличие данных, которые не были правильно перенесены. Если вы достаточно изобретательны, вы даже можете использовать скрипт для разбора журналов и внесения исправлений!

При такой практике проверка непротиворечивости будет просто «страховкой». Выборочная проверка непротиворечивости в конце будет быстрее и более чем достаточна.

Журналы прогресса

Перенос больших объемов данных — мучительный процесс. Лучше всего знать, когда он закончится, чтобы вы могли спланировать свое торжество!

Кроме шуток, знание хода миграции говорит вам о том, сколько дополнительного трафика записи вы добавили в свою систему данных. Кроме того, вы должны нести ответственность перед заинтересованными сторонами и техническим руководителем за прогресс.

Что я сделал, так это то, что у меня есть фоновый поток, который регистрирует объем данных, которые переносятся каждую минуту. Поскольку я знаю общий объем данных, я могу оценить, сколько времени потребуется для всего процесса.

Короче говоря, вы должны тщательно продумать информацию, необходимую для понимания процесса миграции. Зарегистрируйте их как можно больше. Как только вы начнете миграцию, будет слишком поздно сожалеть об упущенных журналах.

Последние мысли

В заключение я рассмотрел четыре практики, которые могут помочь вам лучше разработать и спланировать проект миграции. Вы можете (и должны) адаптировать эти методы в соответствии со своими потребностями.

Некоторые из этих методов являются излишними, если размер данных невелик. Однако при работе с большими объемами данных они могут быть чрезвычайно полезными.

Есть так много информации, которой я хотел бы поделиться, но эта статья становится слишком длинной. Не стесняйтесь оставлять комментарии, чтобы мы могли обменяться идеями и обсудить больше. А пока удачи в переносе данных!