За последние пару десятилетий с ростом объёма данных на рынке СУБД сложился интересный ландшафт. Появились новые СУБД. При этом старые продолжали развиваться — и сориентироваться среди них становилось всё сложнее. 

В этой статье предлагаем рассмотреть эволюцию разных СУБД и сравнить их между собой. Поможет нам в этом Олег Бондарь, директор по продукту в Яндексе, который отвечает за развитие YDB — транзакционной реляционной базы данных с открытым исходным кодом. Статья написана по материалам его доклада на Saint Highload++.

Для чего изучать эволюцию СУБД?

Сравнение СУБД — это всегда холиварный вопрос. Можно написать большую статью, попарно сравнивая разные виды БД. Но в сети и так можно найти много таких обзоров. Поэтому в этом рассказе мы попробовали подойти к вопросу по-другому.

Намного интереснее, почему СУБД вообще развиваются. С точки зрения пользователя — это максимально скучная часть ПО. Её поставили, запустили, подключились, записали данные, прочитали — и всё должно работать. На пользовательском уровне СУБД функционирует как самолёт для пассажиров — сели в салон, взлетели, приземлились. Но за последние 25 лет БД получили колоссальное развитие. Появились совершенно новые и разные продукты. И если они скучны для пользователей, то для разработчиков и DBA — это максимально интересная тема.

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

В чём же конкретно выражается скучность подобных СУБД для пользователей? БД должна просто работать при любых условиях и нагрузках. Соответственно, будем обращать внимание на отказоустойчивость и масштабируемость (и увидим, что прийти к этому можно по-разному).

Итак, посмотрим на четыре класса СУБД:

  • реляционные;

  • In-memory;

  • NoSQL;

  • Distributed SQL.

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

Реляционные СУБД

Развитие реляционных БД началось с конца 90-х годов, и сейчас на рынке популярны несколько решений: PostgreSQL, MySQL, MariaDB. 

Интернет-бум начала нулевых привёл к тому, что все получили возможность строить свои стартапы и проекты, используя опенсорс-продукты. Но денег было немного, продукты часто бесплатные, а нагрузки при этом продолжали расти. Возможность работать с большим количеством пользователей, не вкладывая много денег в оборудование, приводила к тому, что возникала проблема масштабируемости. А так как серверы и вообще оборудование были достаточно ограничены по своим возможностям, всплывала и проблема отказоустойчивости. Начнём именно с неё, потому что она в реляционных БД была решена достаточно быстро.

Отказоустойчивость

Рассмотрим классический кластер PostgreSQL (аналогичную схему можно нарисовать для любой другой традиционной БД).

Мастер обрабатывает входящую нагрузку на чтение и на запись и куда-то реплицирует свои данные. Чтение обычно происходит с реплик на чтение. Реплики могут быть синхронными, асинхронными. В Yandex Cloud, например, используется кворумная репликация. Из набора реплик на чтение выбирается какое-то количество, и до записи в эти реплики операция не считается успешно завершённой.

За счёт использования WAL-логов и наличия реплик возможно, с какими-то трейд-оффами, мы можем сказать, что система получается надёжной и все проблемы отказоустойчивости решены. 

Масштабируемость

Упрощённо, масштабировать базу данных можно следующими способами:

  1. Выделить сервер БД. С сервера, на котором физически работает БД, просто выводим любую другую нагрузку.

  2. Выделить отдельные базы данных на чтение и на запись: пишем в одну БД , читаем из нескольких других.

  3. Перейти на микросервисы. Мы выделяем определённые области знаний (домены), делим логически данные по их микросервисному предназначению и выносим базу данных в соответствии с этой логикой.

  1. Шардировать данные — разбить данные для размещения на разных инстансах.

Практически во всех системах шардирование — один из самых популярных подходов для масштабирования. Мы берём несколько серверов, на них расселяем какие-то части данных, пишем маршрутизацию запросов: определяем, какой именно сервер будет отвечать за конкретный шард данных.

Что это за части? 

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

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

Но часто этого недостаточно: столбец всё равно может быть довольно большой по количеству строк. Тогда на помощь приходит горизонтальное партицирование. 

Горизонтальное партицирование

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

Типичная таблица, разделённая или по диапазону данных, или по хэшу на разные части, выглядит так:

В итоге мы получаем разные части таблицы и размещаем их на разных серверах.

В этом случае с типичным приложением работающим с БД происходило следующее. Оно подключалось к отказоустойчивому кластеру и что-то туда писало, а теперь таких кластеров стало несколько. Маршрутизация запросов усложнилась.

Казалось бы, шардирование и маршрутизацию можно реализовать на стороне клиентского приложения, но выносить логику на сторону клиентского приложения — нетривиальная, трудоёмкая и очень дорогая задача. Ко всему прочему это приводит к сложности поддержания кода. А мы помним, что работа с БД должна быть очень “скучной”. Поэтому хочется выносить маршрутизацию запросов на сторону промежуточного слоя, который делает магию за разработчика.

Этот слой — маршрутизация запросов.

Как должна работать “скучная” маршрутизация запросов: записываем данные в нужные шарды, а при чтении берём их в нужном месте. Если схема данных меняется, то хочется, чтобы изменения были консистентными на всех шардах кластера. 

Также иногда необходимо делать кросс-шардовые запросы: запросы, которые в JOIN не включают данные, лежащие в разных шардах.

В Yandex Cloud для решения задачи маршрутизации создали компонент SPQR с открытым исходным кодом.

Он поддерживает полезную функциональность для PostgreSQL, чтобы работать с базой данных было максимально скучно:

  • пулинг сессий и транзакций;

  • работа с несколькими роутерами для отказоустойчивости;

  • прозрачное шардирование;

  • многошардовые запросы;

  • TLS.

Таких маршрутизаторов за последние десятилетия появилось несколько. Популярные примеры — это Citus для PostgreSQL и Vitess для MySQL.

Итак, обычные реляционные СУБД научились отказоустойчивости и масштабированию (за счёт горизонтального шардирования). Но это не всё.

Реляционные СУБД как полноценная платформа 

Изначально реляционные БД поддерживали только строгую схематизацию, но со временем появилась поддержка других моделей данных.

Так, в PostgreSQL постепенно стала популярна документная модель работы с JSON. За последние 10 лет поддержка JSON развилась очень сильно. Получается, что PostgreSQL становится одновременно документной БД.

Если посмотреть на количество форков на официальной страничке PostgreSQL, то видно, что их там несколько сотен, и они посвящены абсолютно разным областям:

  • аналитика, 

  • массивно-параллельные обработки;

  • MapReduce;

  • работа с абсолютно разными моделями данных — Timeseries, графовые базы и т. д.

Так происходит потому что система, которая реализует базовые примитивы работы с сетью, с жёсткими дисками и конкурентным доступом, реализует огромный массив различной функциональности. И доработать логику на её основе уже не так сложно. Получается, что обычные реляционные БД пришли к платформенности. То есть на основе одной базы данных можно построить целую платформу.

In-memory

Выше мы говорили о способах масштабирования БД, но немного слукавили, не упомянув один из самых простых и доступных способов — кеш.

Масштабирование через кеш

Можно держать кеш в какой-то отдельной БД, просто складывая туда предподсчитанный датасет, а можно держать набор данных в оперативной памяти. С начала 21-го века объёмы доступной оперативной памяти стремительно росли.

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

In-memory СУБД — тоже платформы

Посмотрим, какую функциональность сейчас предлагают пользователям некоторые представители in-memory баз данных. 

Для примера возьмём Redis:

  • горизонтальное масштабирование с шардированием;

  • очереди;

  • сложные структуры данных и транзакции;

  • Persistence;

  • HA;

  • LUA;

  • ...

За последние годы функциональность in-memory СУБД существенно расширилась. Появилось горизонтальное масштабирование, расширилась функциональность — появились очереди, поддержка сложных структур данных, вторичные индексы.

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

NoSQL

В середине нулевых годов для решения проблем с масштабируемостью появился новый тип БД — NoSQL.

Основные проблемы, которые решали эти БД — высокая доступность и практически неограниченная масштабируемость.

Компромиссы NoSQL-решений

Достоинства NoSQL СУБД оказались не бесплатными, и тянули за собой ряд проблем: 

  • Консистентность в итоге;

  • Отсутствие транзакций;

  • Отсутствие JOIN;

  • Отсутствие единого стандарта API.

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

Спустя примерно десять лет существенные изменения произошли и в мире NoSQL БД.

«Not Only SQL»

С 2018 года MongoDB анонсирует появление сначала SQL-диалекта, а потом и многодокументных транзакций. Одновременно с этим одна из наиболее популярных баз данных DynamoDB от Amazon также анонсирует появление SQL-диалекта для доступа к своей документной или key-value-модели. 

Еще немного позднее в MongoDB появляются уже не просто кросс-документные транзакции, но также ещё и кросс-шардовые.

Distributed SQL

Параллельно с развитием NoSQL появился совершенно другой мир БД — Distributed SQL (изначально он назывался NewSQL).

Основная задача нового типа СУБД была представлять привычную модель данных и SQL-интерфейс и одновременно с этим обеспечивать все те же свойства, которые характерны для NoSQL-решений:

  • отказоустойчивость;

  • практически неограниченная масштабируемость;

  • сложность системы полностью скрыта от пользователя.

Эти БД характеризует также то, что в большинстве своём весь исходный код новых решений был написан с нуля, то есть это не форки MySQL или PostgreSQL.

Одновременно с этим мы видим, как реляционные БД начинают обеспечивать прозрачное масштабирование допуская компромиссы в консистентности, масштабирование NoSQL-систем, также допускающее компромиссы в консистентности. И вот появляется новый класс систем, который призван закрыть последнюю проблему и обеспечить отказоустойчивость, масштабируемость и строгую консистентность без компромиссов.

Проблемы Distributed SQL

Технологии Distributed SQL уже больше десяти лет. При её развитии были сделаны точно такие же ошибки, как и в NoSQL. Так как весь код писался с нуля, большинство технологий использовали проприетарные API. А проприетарное API — это уже не очень скучно. Людям, которые его используют, приходится изучать что-то новое, а они этого не хотят.

Но за последние несколько лет ситуация сильно изменилась. Практически все БД нового поколения начинают поддерживать совместимость с теми или иными традиционными СУБД. Есть СУБД, которые поддерживают совместимость с MySQL или PostgreSQL. Команда YDB тоже идёт в сторону поддержки PostgreSQL.

Ещё одна проблема, из-за которой Distributed SQL СУБД долгое время не получали широкого распространения, была в том, что исходный код большинства этих БД был закрыт, при этом воспользоваться ими можно было только в облаках.

Привлечь людей к использованию закрытого продукта очень сложно. Чтобы решить эти проблемы, одна из таких БД — YugabyteDB, которая достаточно давно начала свое развитие, в 2019 году перешла на полностью открытую модель. Команда YDB полтора года назад тоже решила полностью открыть исходный код и опубликовать его под лицензией Apache 2.0.

Это ещё не все: HTAP

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

Если ещё пять лет назад термин HTAP (гибридная транзакционная аналитическая обработка) считался чем-то вымышленным и совершенно ненужным, то на текущий момент некоторые решения из нового поколения БД поддерживают одновременно построчное и поколоночное хранение и возможность выполнять запросы разного характера (или стремятся к этому). 

Что дальше?

Мы поговорили про эволюцию четырёх классов БД. Что нас ожидает дальше?

  1. Растёт популярность продуктов с открытым исходным кодом.

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

  1. Базы данных превращаются в платформы. Амбиции разработчиков СУБД не дают останавливаться на узко ограниченной функциональности. Уже можно встретить одновременно и поддержку аналитики и топиков и очередей.

Практически все БД стремятся стать мультимодельными и как минимум, поддерживать возможность работать с документами. 

  1. БД всё больше интегрируются в облака. Рост интеграции сейчас превосходит прогнозы, которые давали, например, в 2017-2018 годы. Облака сильно упрощают использование БД, потому что все вопросы, касающиеся отказоустойчивости, масштабируемости, облако берет на себя.

Ещё одна интересная тенденция. Если раньше в опенсорсе самыми активными контрибьюторами были крупные энтерпрайз-компании, то сейчас облака аккумулируют у себя экспертизу и становятся такими же активными контрибьюторами. Мы получаем очень большой рынок, который развивает существующие опенсорс-продукты.

Облака привели к тому, что появились новые финансовые модели предоставления услуги. Например, в рамках Serverless-модели пользователь уже не сталкивается с понятием выделение «ресурса». Можно заплатить только за выполненные запросы, используемое для хранения место и больше ни о чём не думать.

Если говорить про новое течение Distributed SQL, то эти СУБД становятся всё более привычными и лёгкими в использовании за счет того, что начинают поддерживают стандартные интерфейсы. Они стремятся к тому, чтобы быть одновременно и аналитическими, и транзакционными БД. 

Но, конечно, всегда надо понимать, что это продукты, которые пишутся с нуля. За набор присущих им ключевых свойств приходится чем-то платить. В данном случае это урезанный набор функциональности по сравнению с традиционными СУБД.

В завершение хочется выдвинуть гипотезу, что скоро выбирать БД будет сложнее. Если сейчас обзор разных типов СУБД сделать ещё достаточно просто, потому что пересечение между ними не такое сильное, то дальше всё будет намного сложнее, потому что все эти платформы начнут копировать функциональность и повторять друг друга. Тогда уже мы действительно столкнемся с настоящей битвой якодзун.