Если матрицу показаний разворачивать по вертикали, то записи реляционных таблиц будут соответствовать номерам датчиков, а поля — моментам времени. В общей сложности получится совокупность из 2 592 000 полей, разбитых по таблицам. Очевидно, что работать с такой базой очень сложно.
Таким образом, классический реляционный подход не дает эффективного решения задачи.
В рассмотренных примерах информация хотя и представима в терминах реляционной модели, но обладает спецификой, которую можно эффективно использовать для оптимизации работы реляционной СУБД. Однако по мере роста объемов базы рано или поздно наступает предел таких оптимизаций.
Основные концепции NoSQL
Важной вехой для высоконагруженных систем стало развитие Интернета, ряд сервисов которого (DNS-серверы, поисковые машины, социальные сети и т.д.) изначально должны обрабатывать большие массивы информации и отвечать на огромное число запросов. Это требует не только максимального учета любой специфики обрабатываемой информации, но и перехода на распределенные вычисления. Никакой сколь угодно мощный сервер в принципе не способен в одиночку обеспечить нужную производительность. В итоге основными принципами NoSQL стали отказ от реляционной модели для учета специфики обрабатываемых данных, а также хорошая горизонтальная масштабируемость до сотен и тысяч серверов для обеспечения скорости работы. Однако это выявило еще одну проблему, сформулированную в виде теоремы CAP (Consistence, Availability, Partition tolerance — «согласованность, доступность, устойчивость к разделению»): в распределенной вычислительной системе невозможно одновременно выполнить требования по согласованности данных, доступности системы и устойчивости к разделению. Под последним требованием понимается то, что система не распадается на несколько изолированных секций, внутри которых выполняются требования по согласованности и доступности.
Нестрогое доказательство теоремы CAP основано на простых рассуждениях. Пусть распределенная система состоит из N серверов, каждый из которых обрабатывает запросы некоторого числа клиентских приложений (рис. 1). При обработке запроса сервер должен гарантировать актуальность информации, содержащейся в отсылаемом ответе на запрос, для чего предварительно нужно выполнить синхронизацию содержимого его собственной базы с другими серверами. Таким образом, серверу необходимо ждать полной синхронизации либо генерировать ответ на основе не синхронизированных данных. Возможен и третий вариант, когда по каким-либо причинам синхронизация производится только с частью серверов системы. В первом случае оказывается не выполненным требование по доступности, во втором — по согласованности, в третьем — по устойчивости к разделению.
Иногда распределенные системы классифицируют по видам выполняемых требований CAP: CA — система удовлетворяет требованиям по согласованности и доступности, CP — по согласованности и устойчивости, AP — по доступности и устойчивости. В любом из трех случаев не будет выполнено свойство ACID (Atomicity, Consistency, Isolation, Durability — «атомарность, согласованность, изолированность, долговечность»), обычно строго соблюдаемое в реляционных СУБД, а ему противопоставляется свойство BASE (Basically Available, Soft State, Eventually consistent). При сбое в некоторых узлах системы отказ получает только часть приложений, взаимодействующих с вышедшими из строя узлами. В ходе взаимодействия используются протоколы без состояния, что снижает нагрузку на отдельные узлы и позволяет ее перераспределять. Наконец, допустима временная несогласованность данных в разных узлах системы при условии, что информация будет синхронизирована через некоторый обозримый промежуток времени. BASE используется для наиболее общего описания требований к распределенным NoSQL-системам, подпадающих под утверждение теоремы CAP и не удовлетворяющих требованиям ACID.
Системы NoSQL
Существует множество различных NoSQL-систем обработки данных, в котором можно выделить следующие основные классы: хранение XML-документов, хранение пар «ключ – значение», хранение графов, хранение кортежей произвольной длины, Triple Storages, многомерные данные и т. д.
Хранилища XML-документов (BaseX, eXist) представляют собой средства для работы с большим количеством XML-документов или с документами большого размера. Информация содержится не в текстовом виде, а в некотором внутреннем формате, позволяющем быстро выполнять операции поиска (запросы XPath и XQuery) и изменения документов (XSLT, eXtensible Stylesheet Language Transformations). При загрузке документа проводится его синтаксический анализ, результаты которого помещаются в базу, а при извлечении документа его текст восстанавливается на основе содержимого внутренних структур базы.
Формат XML — не единственный способ текстового представления структурированной информации: по аналогии с XML-хранилищами существуют СУБД (Apache Couch DB и MongoDB) для работы с данными, представленными в виде JSON (Java Script Object Notation ) или BSON (Binary JSON).
Хранение пар «ключ – значение» заключается в реализации двух операций: запись информации по ключу и чтение по ключу — при этом одному ключу может соответствовать сразу несколько значений. Востребованность данной функциональности при построении высоконагруженных систем привела к тому, что появилось целое множество соответствующих СУБД, которые, в зависимости от способа реализации, можно разделить на постоянные (CDB), редко изменяющиеся (Appache Cassandra, membase, MemcacheDB) и часто изменяющиеся (memcached).
Постоянные СУБД не поддерживают изменения информации «на лету» — база данных создается один раз и затем используется продолжительное время в режиме чтения. Это, во-первых, позволяет провести глубокий анализ содержимого базы на этапе ее создания и обеспечить высокую степень компрессии данных, что впоследствии позволит минимизировать ввод/вывод и тем самым ускорить операции поиска. Во-вторых, если база не изменяется, то упрощается и ускоряется многопользовательская работа, поскольку не требуется выполнять никаких блокировок.
Системы для работы с часто изменяющимися данными всю информацию содержат только в оперативной памяти и вообще не используют ввода/вывода. Такой подход популярен при реализации механизмов кэширования, но принципиально не подходит при построении основного хранилища, поскольку любая авария питания приведет к потере данных. Наконец, системы с редко изменяющимися данными являются некоторым промежуточным звеном, обеспечивая возможность корректировки базы, достаточно высокую скорость поиска и сохранение информации на диске.
Часть хранилищ пар «ключ – значение» основаны на хэш-таблицах, а часть — на B-деревьях (BerkleyDB и MemcacheDB), что помимо выполнения операции поиска дает возможность упорядочивать ключи по возрастанию или убыванию.
В общем случае системы рассматривают ключ и значение как массив байтов, причем длина ключа может достигать нескольких килобайтов, а значения — нескольких мегабайтов. Однако часто некоторые СУБД предоставляют дополнительные возможности для структурирования ключей и значений. Так, Apache Cassandra позволяет разбивать значения на несколько колонок и работать с каждой из них отдельно, а BigTable, напротив, использует трехкомпонентные ключи (ключ столбца, ключ строки и временная метка), но значения рассматривает как массив байтов. Такое устройство ключа позволяет использовать BigTable как средство работы с двумерными таблицами большого размера. Примерно по такой же схеме работают хранилища кортежей произвольной длины.
Системы хранения графов и Triple Storages ориентированы на работы с семантическими данными, представленными в виде узлов и дуг. Отличительной чертой Triple Storages является то, что они ориентированы на поддержку стандартов SemanticWeb.
Все эти классы систем NoSQL очень разнородны, и пока отсутствует единый стандарт требований к ним, однако такая стандартизация станет необходима по мере создания новых высоконагруженных систем и повышения требований к скорости обработки и стоимости разработки. Наличие единых стандартов облегчит использование нескольких NoSQL СУБД в одном проекте, упростит интеграцию СУБД между собой, ускорит миграцию на новые СУБД, сделает возможным создание универсальных инструментальных средств NoSQL, позволит заранее осуществлять подготовку специалистов по NoSQL и т. д.
В 2011 году был сделан первый шаг в направлении стандартизации — анонсирован язык запросов UnQL (Unstructured Query Language) для работы с неструктурированной информацией. Для удобства прикладных разработчиков синтаксис и семантика языка во многом схожи с SQL, что вполне естественно — благодаря поддержке UnQL каждая NoSQL-СУБД может стать гибким, удобным и легко используемым инструментом, и она по-прежнему будет основываться на собственных технических решениях, дающих преимущество в каких-либо условиях эксплуатации.
В качестве примера можно указать систему для хранения пар «ключ – значение». Если все СУБД поддерживают UnQL, то прикладной разработчик может подобрать оптимальную, практически не меняя кода приложения. Например, если необходима организация кэширования, то лучше использовать СУБД, ориентированную на работу в памяти (например, membase). Если необходима работа с редко изменяющимися данными, то имеет смысл выбрать СУБД, предназначенную для работы именно в таких условиях (например, CDB). Если помимо поиска необходима сортировка ключей, то подойдет система, основанная на B-деревьях (например, BerkleyDB).
Что лучше?
Реляционные СУБД не спешат мириться со своими недостатками и поддерживают все новые типы данных, используемые в системах NoSQL. Простейшие примеры такого развития (поддержка типов BLOB и полнотекстового поиска) уже упоминались, а более сложный пример — поддержка XML-документов и XPath-запросов. Эта функциональность ключевая для специализированных СУБД (BaseX, eXist), ориентированных на работу с XML, но она же почти полностью присутствует в некоторых реляционных СУБД (например, в Oracle).