Любая система управления данными относится к одному из двух типов.
Первый — schema-on-write.
Schema-on-write
Наверняка многие уже работали с реляционными базами данных. Ты знаешь, что первый шаг при работе с реляционной базой — создать таблицы и настроить схемы. Как только схемы настроены, а таблицы созданы, можно начинать загружать данные. Скорее всего, это будет что-то вроде массовой загрузки из текстового файла, структуру которого мы знаем заранее — потому что она так или иначе совпадает со схемой таблиц. И когда данные загружены в таблицу, можно начинать выполнять аналитические запросы. Это и есть schema-on-write — подход, при котором мы определяем колонки, формат данных, связи между колонками и так далее до того, как реально зальём данные.
Но есть неприятная проблема — мы не можем загрузить данные, пока не создана таблица, и не можем создать таблицу, пока не поймём схему данных, которые в ней будут лежать. А это невозможно, пока мы не разберёмся, какие сущности эти данные описывают, чтобы правильно отразить их связи в таблицах. Отсюда же вырастают и проблемы с изменением данных. Например, поменялся исходный текстовый файл, или кто-то добавил данные, или сменил тип колонки. Тогда нам нужно дропнуть таблицу и загрузить все данные заново. Это нормально, когда речь про небольшой объём данных, и нормально, если нет внешних ключей. Но если внешние ключи есть, а данных, например, 500 ГБ? Вот это уже настоящая проблема — при таком подходе такие простые изменения займут дни.
Так зачем вообще нужен этот подход?
После того как мы завершили ETL-процесс загрузки данных, последующие чтения будут быстрыми и детерминированными (все получат одни и те же строго определённые данные, соответствующие исходной схеме). Но помни: за это мы уже заплатили штраф, когда загружали данные в первый раз. Ещё один минус строго определённой схемы в том, что данные были изменены и структурированы под конкретную ограниченную цель и не могут быть переиспользованы для будущих задач, о которых мы пока не знаем.
Schema-on-read
С ростом Big Data из-за проблем с традиционными методами и растущих объёмов данных родился другой подход. Здесь мы загружаем данные в том виде, в каком они приходят, без каких-либо изменений и трансформаций. По сути, при таком подходе мы полностью убираем исходный ETL-процесс и бережём себе нервы, не пытаясь понять исходные паттерны данных и их структуру.
Это schema-on-read, и загрузка данных здесь быстрая, потому что данные не обязаны следовать какой-либо внутренней схеме — это просто копирование/перемещение файлов. Такой способ работы с данными более гибкий в случае больших данных, неструктурированных данных или частых изменений схемы.
Поэтому если мы анализируем данные, пытаемся понять их структуру и найти другой способ их интерпретировать, мы можем просто поменять код, который эти данные обрабатывает. Нам не нужно менять схемы и перезаливать все данные в хранилище.
Правда, поскольку данные не проходят через строгие ETL и трансформацию в строгие схемы хранилища, в них может быть много пропущенных или невалидных значений, дублей и куча других проблем, которые приводят к неточным или неполным результатам запросов.
При этом надо понимать, что какой-то уровень проектирования схемы неизбежен. Так или иначе, структуру данных нужно понимать, чтобы искать в них информацию, проверять входящие данные и управлять ими. Но не все данные попадают под этот процесс — часть данных непонятна самому бизнесу, и не стоит пытаться формально их моделировать и ими управлять, пока ты не можешь их использовать.
Какой подход лучше?

Как всегда, серебряной пули нет. Как и почти всё в инженерии, всё зависит от того, как ты этим пользуешься. Schema-on-write помогает выполнять запросы быстрее, потому что данные уже загружены в строгом формате и нужные данные легко найти. Но это требует куда больше предварительной подготовки и постоянной трансформации входящих данных, а стоимость изменений схемы высока, и их лучше избегать.
Schema-on-read, с другой стороны, даёт гибкость, масштабируемость и защищает от части человеческих ошибок. В целом рекомендуется хранить данные в исходном сыром формате (на всякий случай) и оптимизировать их в другом формате, подходящем для дальнейшей обработки. В ETL могут быть некорректные трансформации, а исходные сырые данные позволят вернуться к оригиналу и обработать его правильно. Кроме того, поскольку схему приходится определять на этапе запроса, SQL-запросы обычно получаются очень сложными. Их долго писать и ещё дольше выполнять.
Стоит отметить, что некоторые более новые подходы, например использование Apache Avro, могут абстрагировать изменения схемы через контроль версий (механизм форматирования, а не VCS) или даже через schema registry. Но существующие типы никуда не денутся — меняется только подход.