[Перевод] Почему базы данных NoSQL — плохое решение для современных приложений
Здравствуйте, Хабр.
Сегодня мы предлагаем вашему вниманию перевод статьи из блога MemSQL, которая исходно является рекламной (посвящена достоинствам MemSQL, обновлена по состоянию на начало января 2020 года). Но мы решили все-таки перевести ее в сокращенном виде, поскольку она подробно объясняет, почему мы пока так и не собрались издавать ничего ни по MongoDB, ни по Cassandra, ни по прочим нереляционным базам данных. Может быть, мы были правы, ограничившись весьма успешной книгой «MySQL по максимуму».
Пришло время признать давно известную истину: базы данных NoSQL не подходят для решения многих практических задач, стоящих перед современными приложениями, и время этих баз данных прошло.
Базы данных NoSQL появились потому, что традиционные базы данных, существовавшие на момент их изобретения, не могли справляться с требуемыми масштабами задач. Это новое поколение сервисов для работы с данными, появившихся в обращении более десяти лет назад, позволяло решить многие проблемы, актуальные в масштабах всего Веба, а также оперировать стремительно растущими множествами данных. NoSQL также предлагали новый экономичный метод холодного хранения/эпизодического пакетного обращения к петабайтным объемам данных. Однако, в поспешных попытках ответить на вызовы больших данных и поддерживать большие количества конкурентно работающих пользователей, парадигма NoSQL потребовала отказаться от некоторых ключевых свойств традиционных баз данных, благодаря которым те стали настолько производительными и удобными в использовании.
Пожалуй, успешное нахождение всех этих компромиссов — величайший вклад NoSQL в мир баз данных. Они спровоцировали эволюцию, совместив наилучшие возможности обращения с большими данными со структурой и гибкостью проверенной реляционной модели и породив масштабируемые реляционные базы данных.
Реляционные базы данных развивались, дав начало совершенно новому поколению систем, способных справляться практически с любыми нагрузками и удовлетворять тем требованиям масштабируемости, надежности и доступности, которые предъявляются при работе с современными приложениями. Речь идет о разных рабочих нагрузках — от традиционных, например транзакционных приложений и бизнес-аналитики, до более инновационных, таких как совместное использование ПО разными подписчиками (multi-tenance) и операционная аналитика. Подъем новых баз данных, в частности, Google Spanner, Azure Data Warehouse и MemSQL, доказал, что в большинстве случаев реляционные базы данных удобнее в использовании и, как правило, показывают более высокую производительность, чем системы NoSQL.
Я знаю, что это противоречивые вопросы, и вы можете легко отвергнуть мою точку зрения как предвзятую. Однако, позвольте мне по полочкам разложить историю, архитектуру и прикладную составляющую этих баз данных –, а затем судите сами.
Восход NoSQL
NoSQL как следует вступили в свои права в конце 2000-х, хотя история их началась гораздо ранее. Их разработка велась в основном для решения проблем масштабирования, присущих имеющимся системам баз данных. Было очевидно, что горизонтальное масштабирование — более экономичная модель при создании больших систем. Самые крупные системы, например, поисковики и сервисы электронной почты от Google, Facebook, Microsoft и Yahoo, могли масштабироваться только таким образом.
Лично я впервые оценил всю ценность горизонтального масштабирования, когда прочел статью Джеймса Гамильтона о проектировании и развертывании сервисов в масштабах Интернета. Сначала довелось масштабировать уровень приложений, поскольку проще масштабируется система, не сохраняющая состояния. Уровень хранения данных — другая история. Базы данных по определению работают с сохранением состояния, а давать гарантии (имеется в виду ACID) по поводу этого состояния в масштабах целой распределенной системы по-настоящему сложно. Поэтому новые уровни возводились поверх уже существующих систем баз данных (MySQL, SQL Server, т.д.) для создания уровня распределенного хранения данных.
Мне пришлось иметь дело с некоторыми ситуациями такого рода, когда я работал продукт-менеджером в команде SQL Server в Microsoft. Первый случай был связан с внутренним продуктом Microsoft; тогда компания создавала Webstore, уровень шардирования, надстроенный над SQL Server и используемый Hotmail и связанными с ней сервисами. На самом деле, именно Webstore послужила стимулом для создания продукта, послужившего прообразом нынешней базы данных Azure SQL. Webstore был несколько корявым, в нем недоставало значительной части ключевого функционала, но он работал и обеспечил Microsoft как масштабирование до любых нужных объемов данных, так и высокую доступность. Но для создания и дальнейшей поддержки Webstore потребовалась целая команда инженеров.
В середине 2000-х компания MySpace использовала большое количество SQL-серверов для управления стремительно растущим сайтом. Пользовательская аудитория компании росла так быстро, что новые экземпляры SQL-серверов требовалось устанавливать ежедневно. Эксплуатация всех этих SQL-серверов и выполнение запросов по всем ним оказалось делом такой колоссальной сложности, что им также занималась целая армия инженеров.
Подобные истории повторялись и в Facebook, и в других компаниях, поскольку все бурно растущие технологические гиганты упирались в проблему масштабирования.
Становилось ясно, что при таких темпах роста и эксплуатации эти новые цифровые сервисы требуют нового решения для поглощения данных, управления ими и вывода их на поверхность. В идеале требовалось такое решение, которое могло бы нативно предоставлять единый интерфейс, но горизонтально масштабироваться на много машин и при этом располагать встроенными средствами для обеспечения высокой доступности.
В конечном итоге крупномасштабные облачные сервисы (Google, Facebook, Yahoo, Microsoft и другие) выстроили собственные специальные системы для удовлетворения потребности в масштабировании. Эти системы были разными, но в них были заложены общие идеи. На следующем этапе стали множиться опенсорсные системы, использовавшие те же самые идеи, и так возникло движение NoSQL.
Чтобы решать задачи веб-масштабов, NoSQL разошлись с традиционными базами данных по нескольким ключевым показателям. Итак, давайте рассмотрим, почему здесь были приняты данные конкретные решения.
Производительность и пороки согласованности в конечном счете
Существует два архитектурных подхода, ACID и BASE.
ACID означает «Atomic, Consistent, Isolation, Durable» (Атомарный, Согласованный, Изоляция, Долговечность). Эта парадигма охватывает все гарантии, обычно предоставляемые в реляционных базах данных. ACID гарантирует, что операциям записи придется дождаться, пока данные попадут на диск, и только после этого клиенту будет сообщено, что операция завершена успешно. Кроме того, если вас действительно заботит долговечность данных (то есть, вы стремитесь их не потерять), то вы конфигурируете базу данных так, чтобы операция записи успела проследовать по сети на какую-то другую машину, и данные также были записаны на диск и там. Так вы получаете гарантии, что в данные всегда попадает именно то, что вы записали, однако, отчасти жертвуете скоростью записи.
Архитектура BASE, типичная для NoSQL-систем, означает «Basically Available, Soft State, Eventually Consistent» («базовая доступность, неустойчивое состояние, согласованность в конечном счете»). Согласованность в конечном счете обеспечивает более высокую скорость записи, поскольку приложению не приходится дожидаться подтверждения того, что запись сохранилась. Как только хранилище данных приняло запись, но еще до того, как данные попали в долговременное хранение на ее диск или на диск другой машины, база данных может сообщить приложению, что операция записи прошла успешно, и приложение может переходить к следующей операции. Так вы выигрываете в производительности, правда, рискуете не увидеть тех данных, которые только что записали, либо данные могут совсем потеряться из-за какой-нибудь ошибки.
Согласованность в конечном счете — разумный компромисс, к которому удается прийти, стремясь одновременно к долговечности и доступности данных. Если ваш бизнес связан с вовлечением потребителей, то любые задержки прямо сказываются на ваших доходах (и это в не меньшей степени касается любого контента, сообществ и коммерческих приложений). Естественно, вы добиваетесь максимально возможной отзывчивости пользовательского интерфейса. Если ваша задача — масштабироваться для обслуживания миллионов пользователей, конкурентно работающих с системой, то любые узкие места для вас недопустимы. Реализуя в архитектуре своей базы данных согласованность в конечном счете, вы рискуете случайно потерять чей-нибудь пост или комментарий, и в приложениях такого типа подобный риск является приемлемым.
На другом конце спектра возможностей «долговечность против риска» находятся финансовые приложения. Если вы проводите транзакцию через банкомат, то, конечно же, согласованность в конечном счете вас не устраивает. То же касается торговых операций на бирже. В таких случаях все равно найдутся пользователи, которые будут согласны лишь на минимальные задержки (или вообще на них не согласны), но не готовые мириться с тем, что транзакция не будет записана на диск.
Итак, нам есть где применять согласованность в конечном счете, но, конечно же, она — не единственное решение. Архитекторы и разработчики систем для работы с данными должны уметь выбирать, какой уровень согласованности им нужен. Этот выбор должен зависеть от специфики использования, а не от возможностей платформы.
Попытка обойтись без схемы
Не вполне понятно, почему в движении NoSQL было решено отказаться от схем. Да, на заре существования NoSQL было сложно построить менеджер для управления распределенными метаданными, который обеспечивал бы поддержку схем в масштабах всей распределенной системы и поддерживал такие операции как добавление столбца. Поэтому неудивительно, что схемы исчезли уже в самых ранних проектах таких баз данных. Но, вместо того, чтобы найти способ впоследствии вновь добавить схемы, от них было решено полностью отказаться. Понятна и точка зрения тех ребят, которые указывают, что при наличии схемы база данных становится менее гибкой. Хорошую схему спроектировать сложно, для этого требуется тщательно и заранее все продумывать. Когда ситуация стремительно меняется (так было тогда и так есть сейчас), кому захочется заточить себя в схему.
Но это заблуждение.
Действительно, отсутствие схемы идет на пользу инженеру, задача которого — записывать данные в систему. Однако, в таком случае проблемы спихиваются на долю тех, кто читает данные, а их обычно на порядок больше, чем инженеров, причем, зачастую они не владеют информацией о контексте, в котором находились данные на момент записи. Именно пользователи обычно извлекают ценность из данных, и им нужно оставить минимум препятствий для работы с информацией, насколько это возможно.
Приведу аналогию. Представьте, что библиотекари заявляют, что им надоело работать с каталогами по десятичной классификации Дьюи, и теперь они будут просто сбрасывать книги в большую дыру в полу — ведь работа библиотекаря при этом сильно упрощается. Бывает так, что уместно пользоваться частично структурированными данными, поскольку иногда вы не представляете формы некоторых данных, либо сами данные являются слишком разреженными. Но если вы действительно не понимаете, откуда будут приходить те или иные данные, или как они должны выглядеть, то какой в них толк?
Истина в том, что схема есть всегда. Данные всегда несут для кого-то некоторый смысл. Но кто-то должен потратить время и встроить свои знания об этом смысле в платформу, чтобы после него данными могли пользоваться другие люди. Если мы имеем дело с данными, часть из которых нам понятна, а другая часть быстро меняется, то вторая часть попадает в столбец с частично структурированной информацией, а потом мы решаем, какие столбцы мы впоследствии сформируем из этой частично структурированной информации. SQL Server и Oracle удавалось осуществить это на языке XML 15 лет назад. В MemSQL и некоторых других современных базах данных сегодня то же самое делается при помощи данных JSON. Документное хранение данных (и работа с парами ключ-значение) должны быть фичами современных баз данных, но не единственной возможностью того или иного продукта.
Синтаксис запросов не как в SQL
Данное решение в проектировании NoSQL-баз данных последовало за отказом от схемы. Если схемы нет, то кажется целесообразным и отказаться от SQL-синтаксиса. Кроме того, обработчик запросов сложно написать и для единственного компьютера, а для распределенной системы — гораздо сложнее. Наиболее примечательно, что, если вы — разработчик, которому требуется побыстрее запустить новое приложение, то подобная новая система кажется проще.
MongoDB довела до совершенства искусство простой установки и использования без опыта. Однако, оказывается, что реляционная модель весьма мощная. Хорошо обходиться функциями get и put, если вам никогда не приходилось решать задачи сложнее чем «выбрать объект с id 2». Но в большинстве существующих приложений требуется делать гораздо больше. Если хотите почитать отличную статью от автора, который пришел к такому выводу (и при этом не работает над продуктом для хранения данных), позанимавшись двумя отдельными проектами с применением MongoDB — читайте вот эту. Отличный пример, показывающий, когда возможности документных баз данных ограничены.
В любых системах кроме самых тривиальных вам рано или поздно потребуется запрашивать данные по иному принципу, нежели вы их сохраняли. По иронии судьбы, реляционная модель была изобретена в 1960-е годы для решения точно такой же проблемы с хранилищами данных, существовавшими в то время (IMS и Codasyl). Реляционная модель, обеспечивавшая возможность объединений, казалась единственным разумным способом извлечения данных. Да, поначалу это довольно сложно, но гораздо проще, чем втянуть все данные в ваше приложение, в затем создавать объединения самостоятельно. Я видел, как клиенты снова и снова пытались сделать это при помощи NoSQL-баз данных, и это всякий раз приводило их к какой-то чуши.
Многие из таких NoSQL-систем достигли своей основной цели. Они обеспечили для хранилища данных единый интерфейс, через который можно было масштабироваться на много систем, полагаясь на встроенную высокую доступность. Однако, хотя NoSQL всегда делали некоторые успехи, их внедрение постоянно стопорилось.
На то есть несколько разных причин. Ключевая причина — производительность, в частности, касающаяся выполнения аналитических запросов с соблюдением какого-либо соглашения о качестве обслуживания. Другая причина — управляемость, ведь известно, насколько сложно управлять распределенными системами. Однако, ничто так не помешало широкому распространению NoSQL, как необходимость переучивать людей. Множество специалистов учились и профессионально формировались в мире реляционных баз данных. NoSQL на протяжении более чем десяти лет пыталась изменить мир, но почти ничего не добилась. Все компании, работающие с NoSQL, вместе взятые, занимают всего несколько процентов рынка баз данных, объем которого — 50 миллиардов долларов.
Тогда как программистам NoSQL явно понравилась, специалисты по работе с данными (DBA, архитекторы данных, аналитики) нехотя переходили в мир NoSQL, поскольку казалось, что только эта парадигма позволяет решить актуальные проблемы с масштабированием. Однако, это означало, что им придется переучиваться на новые API, инструменты, осваивать новую экосистему, отбросив многие годы, потраченные на изучение успешных подходов, паттернов и ресурсов. Они хотели делать свою работу по привычной модели, но при этом достигать нужной масштабируемости, не отказываясь от долговечности, доступности и надежности системы.
Прощайте, NoSQL
NoSQL-базы данных возникли для того, чтобы инженеры могли справиться с требованиями масштабирования, актуальными в новые времена веб-приложений и сервисов, рассчитанных на разных подписчиков. Учитывая, как сложно было решать такие проблемы, понятно, что первые попытки справиться с масштабированием на уровне хранения данных вынуждали клиентов идти на непростые компромиссы.
Однако, реляционные базы данных развивались. Сегодня они способны справляться практически с любыми рабочими нагрузками, отвечая всем требованиям масштабируемости, надежности и доступности, которые предъявляются современным приложениям.
Речь идет, например, о таких рабочих нагрузках, как операционная аналитика. Поскольку все компании осознают ценность data-driven подхода, они стремятся обеспечить своих сотрудников актуальными данными. Для этого требуются аналитические системы нового поколения, позволяющие масштабироваться на сотни конкурентных запросов, выдавать быстрые запросы без предварительной агрегации и поглощать данные в том же темпе, в каком они генерируются. Сверх всего этого, требуется предоставлять данные клиентам и партнерам, а для этого нужно соблюдать определенные соглашения об уровне качества обслуживания (SLA), гарантии безопасности, возможности производительности и масштабирования, труднодостижимые для большинства современных хранилищ данных. Вот одна из разновидностей рабочих нагрузок, с которыми не могут справиться ни унаследованные традиционные базы данных, ни NoSQL-системы.
Реляционная модель выдержала проверку временем и продолжает обрастать инновациями, например, SingleStore от MemSQL. Кроме того, старая парадигма впитала многие новые типы данных (поисковые, пространственные, полуструктурированные, т.д.) и модели согласования, позволяющие всем этим типам данных сосуществовать в одной системе. Для реляционной модели и синтаксиса SQL-запросов не существует непреодолимых препятствий. Просто нужна иная реализация хранилища данных, которая позволяла бы пользоваться всеми преимуществами вертикально масштабируемой архитектуры.
Новые базы данных, например, MemSQL, доказывают, что в большинстве практических случаев реляционные базы данных проще в использовании и, в целом, демонстрируют более высокую производительность, чем NoSQL-системы.
Спасибо вам, NoSQL. Вы оказали на сообщество разработчиков баз данных тот необходимый прессинг, который заставил дать достойный ответ на вызовы облачного мира. Это сработало. Реляционные базы данных стали развиваться и стали соответствовать современным требованиям. Благодаря вам.