Разное

Key value хранилище: Битва key-value хранилищ / Хабр

Содержание

PHP и различные виды NoSQL / Хабр



В последнее время набирают популярность различные NoSQL базы данных. Эта статья начиналась как изучение особенностей графитовой графовой базы данных Neo4j. Но, в процессе подбора информации, мне захотелось систематизировать информацию о NoSQL решениях и о графовых базах данных, в частности.
В ходе этого небольшого исследования, были выбраны для подробного рассмотрения СУБД, успешно применяющиеся в области Web. И, поскольку в тегах присутствует «PHP», я выбирал СУБД, которые уже можно использовать с этим языком.

Статья получилась объемной, для удобства навигации предлагаю воспользоваться оглавлением:

  1. Виды NoSQL
  2. Key-value stores
  3. Bigtable stores
  4. Graph Stores
  5. Document Stores
  6. Некоторые выводы
Виды NoSQL

Все NoSQL СУБД разделяются на несколько категорий:

  • Key-value stores / Хранилища типа «ключ-значение»
  • Column Family (Bigtable) stores / Масштабируемые распределенные хранилища
  • Graph Stores / Графовые СУБД
  • Document Stores / Документо-ориентированные СУБД

Ниже на рисунке схематично обозначены объемы используемых данных и сложность этих данных в этих видах NoSQL

В каждом разделе я старался располагать СУБД в порядке увеличения функциональности. Возможно, получилось несколько субъективно.

Существуют базы данных, объединяющие в себе несколько категорий, например, OrientDB. Согласно официальному описанию по ссылке выше, она одновременно и графовая, и документо-ориентированная. Иногда её относят даже к Key-value stores и Column Family stores. Подробнее о ней позже в разделе графовых СУБД.

Рассмотрим подорбнее каждую категорию:

Key-value stores / Хранилища типа «ключ-значение»

Key-value stores — то самое направление, в котором NoSQL решения показывают свое превосходство над SQL.
И многие считают именно это направление наиболее востребованным в краткосрочной и долгосрочной перспективе.
Вот, например, автор оригинальной версии открытой СУБД MySQL, Майкл Видениус, так считает.
Key-value NoSQL очень популярны и они быстро и хорошо развиваются, видимо, из-за большого их количества и сильной конкуренции. Наибольшее количество NoSQL баз данных, которые были изучены в процессе написания статьи, относились именно к key-value stores.

На хабре есть статья по поводу key-value хранилищ для PHP, с которой я не во всем согласен. Общий выбор представленных в ней хранилищ (Voldemort, Scalaris, MemcacheDB, ThruDB, CouchDB ) мне показался уже не так актуальным спустя почти пять лет, которые прошли с момента публикации статьи. А описанная там CouchDB — совсем не key-value хранилище, а документо-ориентированная СУБД (см. секцию про документо-ориентированные СУБД).

MemcacheDB

Описание: тот же memcached, только с бэкграундом в виде BerkeleyDB.
Производительность: разработчиками представлены результаты тестов, по результатам которых средняя производительность в одном потоке составляет 18868 w/s (операций записи в секунду) и 44444 r/s (операций чтения в секунду). Тестировали на сервере Dell 2950III, который даже в самой слабой комплектации представляет собой нехилый аппарат.
Установка: все собирается из исходников. В PHP пользуемся обычным Memcached из PECL.
Лицензия: BSD-like License — использование бесплатно для коммерческих и некоммерческих проектов.

Redis

Описание: На хабре есть вводная статья с блэкджеком бенчмарком и ссылками. Присутствуют транзакции (хабростатья о них) и репликации. На подходе версия 3.0, в которой появится Redis-Cluster и значительно увеличится быстродействие. Есть приятный интерактивный обучатор.
Производительность: ~110.000 w/s, ~81.000 r/s на среднем железе.
Установка: сам Redis и клиент для PHP рекомендуют собирать из исходников. Клиентов существует немало (список), от себя рекомендовал бы phpredis за хорошее описание и поддержку всего (или почти всего) существующего функционала Redis.
Лиценция: BSD license — все бесплатно, но если что-то сломалось, то никаких претензий к разработчикам.

Tarantool

Описание: In-memory хранилище. Противопоставляется Redis, от которого отличается, по словам разработчиков, увеличенным быстродействием, благодаря тому, что все данные находятся в памяти. Есть встроенный механизм очередей. Есть хорошая хабростатья, описывающая основные возможности.
Установка: на Ubuntu ставится с помощью apt-get и капельки волшебства (официальная страница), клиент для PHP собираем из исходников (github)
Производительность: на уровне с Redis, результаты тестов противоречивые: Tarantool быстрее Redis у своего разработчика, Tarantool на уровне с Redis у обычного человека
Лиценция: Simplified BSD — все бесплатно.

Riak

Описание: база данных с сильным упором на отказоустойчивость и распределенность. Упор этот настолько силен, что компания-разработчик рекомендует выделить под Riak не менее пяти серверов для того, чтобы иметь возможность оценить его способности. На первый взгляд — это key-value хранилище, но в нем присутствует поиск по всем полям, вторичные ключи, MapReduce. Транзакции отсутствуют. Подробная и обстоятельная хабростатья.
Установка: много способов вплоть до установки из пакетов для Debian/Ubuntu. Для PHP есть PECL пакет, а так же официальный PHP-client.
Производительность: ей уделено не самое важное место, но есть упоминания о 2.500 операций в секунду.
Лиценция: Apache 2 License — бесплатно для простых людей, но для коммерческого использования цены за одну копию Riak Enterprise начинаются от $2,800/год.

Aerospike

Описание: масштабируемое хранилище для огромных объемов данных с минимальной задержкой (latency). Транзакции по умолчанию, поддержке ACID выделена отдельная страница. В версии 3 появились вторичные индексы. Впечатляет количество собственных технологий масштабирования, репликации и кластеризации (ссылка). Для себя эту систему запомнил как мощный промышленный Memcached.
Установка: Aerospike устанавливается из дистрибутива, официальный клиент для PHP существует только для Aerospike2, собирается из исходников.
Производительность: заявлена скорость от 180,000 до 400,000 операций в секунду с задержкой в микросекунды (источник).
Лиценция:

  • Community Edition — бесплатный вариант с ограничениями: максимум два сервера по 200Гб данных на каждом;
  • Enterprise Edition — триал 30 дней, никаких ограничений. По слухам, стоимость составляет от $50.000 за датацентр.
FoundationDB

Описание: позиционируется как комплексное и максимально простое в установке и настройке решение. Легкая масштабируемость, легкое управление — ключевые слова, которые цепляют. Пользователям предлагают «бескомпромисные ACID транзакции». Возможность использовать различные модели данных — key/value, document и даже SQL. Эта СУБД показалась мне особенно интересной, когда прочитал про ее производительность.
Производительность: 3,750,000 r/s*. *Чтение рандомных записей из оперативной памяти(кэша). На официальном сайте в разделе performance есть много интересных тестов, самый «медленный» из которых показывает результат ~235,000 операций в секунду (50/50 операций чтения и записи). Задержка чтения менее 2ms, задержка коммита менее 15ms. Результаты получены на кластере из 24 машин, в каждой по 16Gb RAM, 2x200Gb SSD, тестовая база состояла из 2млн key-value записей, все операции были транзакционными с максимальным уровнем изоляции и тройной репликацией.
Установка: и тут все просто: DEB-пакет для Ubuntu, PEAR-пакет для PHP.
Лиценция:

  • Community License — бесплатное использование. Никаких ограничений при разработке и тестировании, но максимум 6 запущенных процессов на production, т.е. по одному процессу на шести серверах, по два на трех и т.д.;
  • Enterprise License — без ограничений, от $99 до $199, в зависимости от качества поддержки.

Некоторые интересные проекты не были включены в этот список из-за отсутствия поддержки PHP. Так же не были включены проекты Voldemort, Scalaris, ThruDB. Из-за слабой производительности, либо скудной документации и из-за того, что с 2009 года в них ничего не изменилось к лучшему.


Column Family (Bigtable) stores / Масштабируемые распределенные хранилища

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

HBase

Описание: Open Source разработка на основе оригинального дизайна Google Bigtable от Apache. Разработана в рамках проекта Hadoop. Используется самим Facebook в качестве основы сервиса обмена сообщениями. У HBase выбор производится по одному проиндексированному полю. Присутствует частичная поддержка ACID, получается, что транзакционность вроде бы есть, но поддерживается она не самым очевидным способом.
Установка: устанавливается с помощью волшебной пилюли по имени Thrift, процесс установки и использования хорошо описан в этой хабростатье.
Производительность: полевые испытания с необычной методикой измерения производительности: на кластере из 7 серверов (16Gb RAM, 8x core CPU, HDD) проводились операции в таблице с 3 милиардами записей. Было запущено 300 процессов чтения/записи одновременно, измерялось время, затраченное на операцию. В результате, среднее время записи составило 10ms, чтения — 18ms.
Лиценция: Apache License 2.0 — использования в любых целях бесплатно.

Hypertable

Описание: интересная разработка, похожая на HBase. Имеет чуть большую производительность и значительно более привычный по синтаксису запросов HQL. Пример запроса:

select * from QueryLogByUserID where row =^ '003269359' AND "2008-11-13 05:00:00" <= TIMESTAMP < "2008-11-13 06:00:00"

Транцакции отсутствуют, о чем четко сказано в первых строчках документации на официальном сайте.
Установка: с PHP соединяем с помощью Thrift и официального ThriftClient(github).
Производительность: несколько графиков на официальном сайте. Как упоминалось выше, производительность похожа на HBase.
Лиценция: GNU General Public License Version 3. — использования в любых целях бесплатно. За дополнительную плату предлагается круглосуточная поддержка.

Cassandra

Описание: Распределенное хранилище, изначально разрабываемое в Facebook, впоследствии переданное в Apache. В отличие от вышеупомянутых, Cassandra является распределенной децентрализованной хэш-таблицой (DHT) и основана на Amazon’s Dynamo. Имеет язык запросов CQL, очень похожий на SQL с некоторыми ограничениями. Можно строить запросы с выборкой по нескольким колонкам, добавлять вторичные индексы. В версии 2.0 появились «транзакции», которые работают по принципу «compare-and-swap».

Синтаксис транзакционного запроса будет приметно такой:

  • Добавление записи
    INSERT INTO users (login, email, name, login_count)
    values ('jbellis', '[email protected]', 'Jonathan Ellis', 1)
    IF NOT EXISTS
    
  • Обновление записи
    UPDATE users
    SET reset_token = null, password = ‘newpassword’
    WHERE login = ‘jbellis’
    IF reset_token = ‘some-generated-reset-token’
    

Установка: есть несколько способов установить взаимодействие между PHP и Cassandra(тот же Trift, Cassandra-PHP-Client-Library, cassandra-pdo). Последний вариант показался мне наиболее приятным.
Производительность: хорошие сравнительные тесты с графиками, по результатам которых, на 8 серверах при соотношении 50/50 операций чтения/записи Cassandra совершает около 9.000 операций в секунду. HBase делает около 2.500 при тех же условиях.
Лиценция: Apache License 2.0 — использования в любых целях бесплатно.

Существуют другие BigTable решения, например, Stratosphere, HPCC, Cloudera, Cloudata. Они не рассмотрены подробно по разным причинам, например: отсутствие поддержки PHP, низкая распространенность, плохая документация.


Graph Stores / Графовые СУБД

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

В графовой СУБД структура хранимых данных может выглядеть примерно так:

Если занести в графовую СУБД все фильмы и связать с каждым снимавшихся в нем актеров, можно легко найти

фильмы, 
в которых снимались актеры, 
которые когда-либо снимались с актерами из "Матрицы", 
и никогда не снимались с актерами из "Пиратов Карибского Моря"
Neo4j

Описание: наиболее успешная и востребованная разработка в области графовых СУБД. Она полностью поддерживает ACID. Просто устанавливается и без особых усилий масштабируется. У нее уже сформировалось развитое комьюнити, по большинству возникающих вопросов можно быстро найти ответы. О ее возможностях в связке с PHP можно прочитать в этой статье.
Установка: ставится из своего репозитория, для PHP используется клиент Neo4jPHP
Производительность: ввиду особой специфики, мне показалось странным приводить конкретные показатели скорости чтения/записи. Она позволяет выбрать сложно связанные данные и делает это в разы быстрее, чем реляционные СУБД.
Лиценция:

  • Community Edition — GPL-licensed open source, бесплатное использование
  • Commercial Subscription — имеем высокопроизводительный кэш, расширенные возможности горизонтального масштабирования, поддержку и еще некоторые плюшки. Стоимость варьируется от $0 (если вы — стартап из трех человек с годовым оборотом проекта менее $100.000) до бесконечности (для очень больших компаний)


В этом разделе я описал только одну СУБД, а ее наиболее интересный конкурент, OrientDB, находится ниже. Как оказалось, существует на так много графовых СУБД для Web и для PHP в частности.

Есть еще Titan, который в качестве back-end использует HBase, BerkleyDB или Cassandra. Информации по его этому чуду не очень много, о способах подружить его с PHP еще меньше.

Стоит вспомнить и о FlockDB от Twitter, который можно подключить к php c помощью клиента, работающего на базе Thirt. Но, опять же, связи с небольшим количеством информации об этой СУБД, сложно составить о ней полное и объективное мнение.


Document Stores / Документо-ориентированные хранилища

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

MongoDB

Описание: пожалуй, самая популярная документо-ориентированная NoSQL СУБД. Данные хранятся в формате JSON/BSON. Хорошее масштабирование, репликации, индексы, Map-Reduce. Транзакции представлены в виде compare-and-swap.
Установка: MongoDB из репозитория, php-client из PECL.
Производительность: чуть выше были сравнительные тесты, в которых были результаты и по MongoDB.
Лиценция: GNU AGPL — open source, использование бесплатно.

CouchDB

Описание: разработка от Apache. Во многом похожа на MongoDB. Отличается отсутствием блокировки при операциях чтения, и более сложной в настройке технологией шардинга.
Установка: CouchDB из репозитория, для php клиента есть несколько вариантов(PHPillow, PHP Object Freezer, PHP-on-Couch, расширение из PECL).
Производительность: по результатам одного теста, она заметно медленнее MongoDB
Лиценция: Apache 2.0 — использование бесплатно.

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

OrientDB

Описание: документ-ориентированная и, в то же время, графовая CУБД.

Её ближайший конкурент как документ-ориентированной — MongoDB. Этому сравнению посвящена отдельная страница.

Главные преимущества OrientDB:

  • полная поддержка ACID
  • возможность использовать внешние ключи в документах(так же, как в реляционных СУБД)
  • три типа используемых индексов (SB-Tree, Hash, MVRB-Tree) против B-Tree в MongoDB
  • большая производительность(OrientDB выполняет 150.000 w/s на обычном железе)
  • простой язык запросов, схожий с SQL

Отдельно хочу отметить язык запросов, сравните, как выглядят идентичные update-запросы:

  • MongoDB
    db.product.update( { “stock.qty”: { $gt: 2 } }, { $set: { price: 9.99 } } )
    
  • OrientDB
    UPDATE product SET price = 9.99 WHERE stock.qty > 2
    

Основной ее конкурент как графовой — Neo4j. И должен сказать, что освоить графовые возможности в OrientDB куда сложнее, чем в Neo4j. Первые представления об этом можно получить в этой статье.
Установка: с установкой нужно немного поколдовать, вот есть вполне рабочий мануал, а в качестве PHP-клиента рекомендована эта библиотека.
Производительность: обещают 150.000 w/s, также есть cравнение графовых СУБД
Лиценция:

  • Community Edition — Apache 2 license open source, бесплатное использование для любых целей, включая коммерческое
  • Enterprise Edition — расширенная поддержка и такие плюшки как Query Profiler, Metrics recording, Live Monitor with configurable alerts за £1,000 за первый сервер и £500 за каждый последующий. Для стартапов вдвое дешевле.
Некоторые выводы

В ходе написания статьи, я нашел много интересной полезной и полезной информации, и рад поделиться ей с хабровчанами.

Мне очень понравились такие решения как FoundationDB, Neo4j, OrientDB. Хочется посвятить каждой из них отдельную статью.

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

Основы работы с zmq в python, создание простого key/value хранилища / Хабр

Введение

Разберем пример простого key/value хранилища, например такого как memcache. Устроен он просто — данные хранятся в памяти, в структуре hashmap. Доступ к ним осуществлятся через tcp-сокет. В питоне hashmap — это обычный dict. Для доступа будем использовать zeromq.

Настройка

Для установки этого пакета в debian/ubuntu достаточно ввести в консоли
sudo apt-get install libzmq-dev
sudo pip install zmq

Код

Напишем класс для работы с нашим сервером:
Тип используемого zmq-сокета — REQ(REQuest, запрос), посылаем запрос — ждем ответ.
Чтобы хранить и передавать по сети любой тип данных, используем стандартный модуль pickle. «Протокол» работы — кортеж из трех значений: (команда, ключ, данные)

import zmq
import pickle

class SuperCacher:
    def __init__(self):
        context = zmq.Context()
        self.socket = context.socket(zmq.REQ)
        self.socket.connect('tcp://127.0.0.1:43000')

    def get(self, key):
        self.socket.send(pickle.dumps(('get', key, None)))
        return pickle.loads(self.socket.recv())

    def set(self, key, data):
        self.socket.send(pickle.dumps(('set', key, data)))
        return self.socket.recv() == b'ok'
Использование

cache = SuperCacher()
cache.set(‘key’, ‘value’)
cache.get(‘key’)

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

Теперь напишем сам сервер.

На этот раз используется сокет REP(REPly, ответ) — ждем запрос, шлем ответ. Разбираем запрос, отвечаем либо ‘ok’ в случае записи, либо данными / None в случае чтения.

import pickle
import json
import zmq

def run_daemon():
    memory = {}

    context = zmq.Context()
    socket = context.socket(zmq.REP)
    socket.bind('tcp://127.0.0.1:43000')

    while True:
        try:
            command, key, data = pickle.loads(socket.recv())
            if command == 'set':
                memory[key] = data
                socket.send(b'ok')
            elif command == 'get':
                result = memory.get(key, None)
                socket.send(pickle.dumps(result))
        except Exception as e:
            print(e)

if __name__ == '__main__':
    run_daemon()

Чтобы протестировать все вместе, запускаем сервер командой
python daemon.py

В соседней вкладке запускаем python в интерактивном режиме.

>>> from lib import SuperCacher
>>> cache=SuperCacher()
>>> cache.set('key', 'value')
True
>>> cache.get('key')
'value'

О чудо, оно работает! Теперь можно смело писать в своем резюме «разработка key-value хранилища c использованием протокола zmq»

история, виды, примеры, применение Big Data

NoSQL – это подход к реализации масштабируемого хранилища (базы) информации с гибкой моделью данных, отличающийся от классических реляционных СУБД. В нереляционных базах проблемы масштабируемости (scalability) и доступности (availability), важные для Big Data, решаются за счёт атомарности (atomicity) и согласованности данных (consistency) [1].

Зачем нужны нереляционные базы данных в Big Data: история появления и развития

NoSQL-базы оптимизированы для приложений, которые должны быстро, с низкой временной задержкой (low latency) обрабатывать большой объем данных с разной структурой [2]. Таким образом, нереляционные хранилища непосредственно ориентированы на Big Data. Однако, идея баз данных такого типа зародилась гораздо раньше термина «большие данные», еще в 80-е годы прошлого века, во времена первых компьютеров (мэйнфреймов) и использовалась для иерархических служб каталогов. Современное понимание NoSQL-СУБД возникло в начале 2000-х годов, в рамках создания параллельных распределённых систем для высокомасштабируемых интернет-приложений, таких как онлайн-поисковики [1].

Вообще термин NoSQL обозначает «не только SQL» (Not Only SQL), характеризуя ответвление от традиционного подхода к проектированию баз данных. Изначально так называлась опенсорсная база данных, созданная Карло Строззи, которая хранила все данные как ASCII-файлы, а вместо SQL-запросов доступа к данным использовала шелловские скрипты [3]. В начале 2000-х годов Google построил свою поисковую систему и приложения (GMail, Maps, Earth и прочие сервисы), решив проблемы масштабируемости и параллельной обработки больших объёмов данных. Так была создана распределённые файловая и координирующая системы, а также колоночное хранилище (column family store), основанное на вычислительной модели MapReduce. После того, как корпорация Google опубликовала описание этих технологий, они стали очень популярны у разработчиков открытого программного обеспечения. В результате этого был создан Apache Hadoop и запущены основные связанные с ним проекты. Например, в 2007 году другой ИТ-гигант, Amazon.com, опубликовав статьи о своей высокодоступной базе данных Amazon DynamoDB. Далее в эту гонку NoSQL- технологий для управления большими данными включилось множество корпораций: IBM, Facebook, Netflix, eBay, Hulu, Yahoo! и другие ИТ-компаний со своими проприетарными и открытыми решениями [1].

Многообразие NoSQL-решений

Какие бывают NoSQL-СУБД: основные типы нереляционных баз данных

Все NoSQL решения принято делить на 4 типа:

  1. Ключ-значение (Key-value) – наиболее простой вариант хранилища данных, использующий ключ для доступа к значению в рамках большой хэш-таблицы [4]. Такие СУБД применяются для хранения изображений, создания специализированных файловых систем, в качестве кэшей для объектов, а также в масштабируемых Big Data системах, включая игровые и рекламные приложения, а также проекты интернета вещей (Internet of Things, IoT), в т.ч. индустриального (Industrial IoT, IIoT). Наиболее известными представителями нереляционных СУБД типа key-value считаются Oracle NoSQL Database, Berkeley DB, MemcacheDB, Redis, Riak, Amazon DynamoDB, которые поддерживают высокую разделяемость, обеспечивая беспрецедентное горизонтальное масштабирование, недостижимое при использовании других типов БД [2].
  2. Документно-ориентированное хранилище, в котором данные, представленные парами ключ-значение, сжимаются в виде полуструктурированного документа из тегированных элементов, подобно JSON, XML, BSON и другим подобным форматам [4]. Такая модель хорошо подходит для каталогов, пользовательские профилей и систем управления контентом, где каждый документ уникален и изменяется со временем [2].  Поэтому чаще всего документные NoSQL-СУБД используются в CMS-системах, издательском деле и документальном поиске. Самые яркие примеры документно-ориентированных нереляционных баз данных – это CouchDB, Couchbase, MongoDB, eXist, Berkeley DB XML [1].
  3. Колоночное хранилище, которое хранит информацию в виде разреженной матрицы, строки и столбцы которой используются как ключи. В мире Big Data к колоночным хранилищам относятся базы типа «семейство столбцов» (Column Family). В таких системах сами значения хранятся в столбцах (колонках), представленных в отдельных файлах. Благодаря такой модели данных можно хранить большое количество атрибутов в сжатом виде, что ускоряет выполнение запросов к базе, особенно операции поиска и агрегации данных [4]. Наличие временных меток (timestamp) позволяет использовать такие СУБД для организации счётчиков, регистрации и обработки событий, связанных со временем: системы биржевой аналитики, IoT/IIoT-приложения, систему управления содержимым и т.д. Самой известной колоночной базой данных является Google Big Table, а также основанные на ней Apache HBase и Cassandra. Также к этому типу относятся менее популярные ScyllaDB, Apache Accumulo и Hypertable [1].
  4. Графовое хранилище представляют собой сетевую базу, которая использует узлы и рёбра для отображения и хранения данных [4]. Поскольку рёбра графа являются хранимыми, его обход не требует дополнительных вычислений (как соединение в SQL). При этом для нахождения начальной вершины обхода необходимы индексы. Обычно графовые СУБД поддерживают ACID-требования и специализированные языки запросов (Gremlin, Cypher, SPARQL, GraphQL и т.д.) [1]. Такие СУБД используются в задачах, ориентированных на связи: социальные сети, выявление мошенничества, маршруты общественного транспорта, дорожные карты, сетевые топологии [3]. Примеры графовых баз: InfoGrid, Neo4j, Amazon Neptune, OrientDB, AllegroGraph, Blazegraph, InfiniteGraph, FlockDB, Titan, ArangoDB.

Виды NoSQL-СУБД

Чем хороши и плохи нереляционные базы данных: главные достоинства и недостатки

По сравнению с классическими SQL-базами, нереляционные СУБД обладают следующими преимуществами:

  • линейная масштабируемость – добавление новых узлов в кластер увеличивает общую производительность системы [1];
  • гибкость, позволяющая оперировать полуструктирированные данные, реализуя, в. т.ч. полнотекстовый поиск по базе [2];
  • возможность работать с разными представлениями информации, в т.ч. без задания схемы данных [1];
  • высокая доступность за счет репликации данных и других механизмов отказоустойчивости, в частности, шаринга – автоматического разделения данных по разным узлам сети, когда каждый сервер кластера отвечает только за определенный набор информации, обрабатывая запросы на его чтение и запись. Это увеличивает скорость обработки данных и пропускную способность приложения [5].
  • производительность за счет оптимизации для конкретных видов моделей данных (документной, графовой, колоночной или «ключ‑значение») и шаблонов доступа [2];
  • широкие функциональные возможности – собственные SQL-подобные языки запросов, RESTful-интерфейсы, API и сложные типы данных, например, map, list и struct, позволяющие обрабатывать сразу множество значений [2].

Обратной стороной вышеуказанных достоинств являются следующие недостатки:

  • ограниченная емкость встроенного языка запросов [5]. Например, HBase предоставляет всего 4 функции работы с данными (Put, Get, Scan, Delete), в Cassandra отсутствуют операции Insert и Join, несмотря на наличие SQL-подобного языка запросов. Для решения этой проблемы используются сторонние средства трансляции классических SQL-выражений в исполнительный код для конкретной нереляционной базы. Например, Apache Phoenix для HBase или универсальный Drill.
  • сложности в поддержке всех ACID-требований к транзакциям (атомарность, консистентность, изоляция, долговечность) из-за того, что NoSQL-СУБД вместо CAP-модели (согласованность, доступность, устойчивость к разделению) скорее соответствуют модели BASE (базовая доступность, гибкое состояние и итоговая согласованность) [1]. Впрочем, некоторые нереляционные СУБД пытаются обойти это ограничение с помощью настраиваемых уровней согласованности, о чем мы рассказывали на примере Cassandra. Аналогичным образом Riak позволяет настраивать требуемые характеристики доступности-согласованности даже для отдельных запросов за счет задания количества узлов, необходимых для подтверждения успешного завершения транзакции [1]. Подробнее о CAP-и BASE-моделях мы расскажем в отдельной статье.
  • сильная привязка приложения к конкретной СУБД из-за специфики внутреннего языка запросов и гибкой модели данных, ориентированной на конкретный случай [5];
  • недостаток специалистов по NoSQL-базам по сравнению с реляционными аналогами [5].

Подводя итог описанию основных аспектов нереляционных СУБД, стоит отметить некоторую некорректность запроса «NoSQL vs SQL» в связи с разными архитектурными подходами и прикладными задачами, на которые ориентированы эти ИТ-средства. Традиционные SQL-базы отлично справляются с обработкой строго типизированной информации не слишком большого объема. Например, локальная ERP-система или облачная CRM. Однако, в случае обработки большого объема полуструктурированных и неструктурированных данных, т.е. Big Data, в распределенной системе следует выбирать из множества NoSQL-хранилищ, учитывая специфику самой задачи. В частности, для самостоятельных решений интернета вещей (Internet of Things), в т.ч. промышленного, отлично подходит Cassandra, о чем мы рассказывали здесь. А в случае многоуровневой ИТ-инфраструктуры на базе Apache Hadoop стоит обратить внимание на HBase, которая позволяет оперативно, практически в режиме реального времени, работать с данными, хранящимися в HDFS.

Нереляционные СУБД находят больше областей приложений, чем традиционные SQL-решения

Источники

  1. https://ru.wikipedia.org/wiki/NoSQL
  2. https://aws.amazon.com/ru/nosql/
  3. https://ru.bmstu.wiki/NoSQL
  4. https://tproger.ru/translations/types-of-nosql-db/
  5. https://habr.com/ru/sandbox/113232/

Пять студентов и три распределённых key-value хранилища / Хабр

Или как мы писали клиентскую C++ библиотеку для ZooKeeper, etcd и Consul KV

В мире распределённых систем существует ряд типовых задач: хранение информации о составе кластера, управление конфигурацией узлов, детекция сбойных узлов, выбор лидера и другие. Для решения этих задач созданы специальные распределённые системы — сервисы координации. Сейчас нас будут интересовать три из них: ZooKeeper, etcd и Consul. Из всей богатой функциональности Consul мы сосредоточимся на Consul KV.

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

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


Нам удалось создать библиотеку, предоставляющую общий интерфейс для работы с ZooKeeper, etcd и Consul KV. Библиотека написана на C++, но есть планы по портированию на другие языки.

Модели данных

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

ZooKeeper

Ключи организованы в дерево и называются узлами (znodes). Соответственно, для узла можно получить список его детей. Операции создания znode (create) и изменения значения (setData) разделены: читать и изменять значения можно только у существующих ключей. К операциям проверки существования узла, чтения значения и получения детей можно привязывать watches. Watch — это одноразовый триггер, который срабатывает, когда версия соответствующих данных на сервере изменяется. Для обнаружения отказов служат эфемерные узлы. Они привязываются к сессии создавшего их клиента. Когда клиент закрывает сессию или перестаёт оповещать ZooKeeper о своём существовании, эти узлы автоматически удаляются. Поддерживаются простые транзакции – набор операций, которые либо все успешно выполняются, либо не выполняются, если хотя бы для одной из них это невозможно.

etcd

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

В etcd нет стандартной операции compare-and-set, но есть кое-что получше — транзакции. Конечно, они есть во всех трёх системах, но в etcd транзакции особенно хороши. Они состоят из трёх блоков: check, success, failure. Первый блок содержит набор условий, второй и третий – операции. Транзакция выполняется атомарно. Если все условия верны, то выполняется блок success, иначе – failure. В версии API 3.3 блоки success и failure могут содержать вложенные транзакции. То есть можно атомарно исполнять условные конструкции почти произвольного уровня вложенности. Подробнее узнать о том, какие существуют проверки и операции, можно из документации.

Watches здесь тоже существуют, хотя они устроены немного сложнее и являются многоразовыми. То есть после установки watch’а на диапазон ключей вы будете получать все обновления в этом диапазоне, пока не отмените watch, а не только первое. В etcd аналогом клиентских сессий ZooKeeper являются leases.

Consul KV

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


Вместо watch’ей в Consul существуют блокирующие HTTP запросы. По сути это обычные вызовы метода чтения данных, для которых наряду с остальными параметрами указывается последняя известная версия данных. Если текущая версия соответствующих данных на сервере больше указанной, ответ возвращается сразу, иначе – когда значение изменится. Здесь также есть сессии, которые в любой момент можно прикреплять к ключам. Стоит заметить, что в отличие от etcd и ZooKeeper, где удаление сессий приводит к удалению связанных ключей, здесь есть режим, при котором сессия просто от них открепляется. Доступны транзакции, без ветвлений, но со всевозможными проверками.

Сводим всё воедино

Самой строгой моделью данных обладает ZooKeeper. Выразительные запросы на диапазоны, доступные в etcd, невозможно эффективно эмулировать ни в ZooKeeper, ни в Consul. Пытаясь вобрать всё лучшее от всех сервисов, мы получили интерфейс почти эквивалентный интерфейсу ZooKeeper со следующими существенными исключениями:

  • sequence, container и TTL nodes не поддерживаются
  • ACL не поддерживаются
  • метод set создаёт ключ, если он не существовал (в ZK setData возвращает ошибку в этом случае)
  • методы set и cas разделены (в ZK это по сути одно и то же)
  • метод erase удаляет вершину вместе с поддеревом (в ZK delete возвращает ошибку, если у вершины есть дети)
  • для каждого ключа существует только одна версия – версия значения (в ZK их три)

Отказ от sequential узлов связан с тем, что в etcd и Consul для них нет встроенной поддержки, а поверх получившегося интерфейса библиотеки они могут быть легко реализованы пользователем.

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

Тонкости реализации

Рассмотрим подробнее некоторые аспекты реализации интерфейса библиотеки в разных системах.

Иерархия в etcd

Поддержание иерархического вида в etcd оказалось одной из самых интересных задач. Запросы на диапазоны позволяют легко получить список ключей с указанным префиксом. Например, если вам нужно всё, что начинается с "/foo", вы запрашиваете диапазон ["/foo", "/fop"). Но это возвращало бы целиком всё поддерево ключа, что может быть неприемлемо, если поддерево большое. Сначала мы планировали использовать механизм преобразования ключей, реализованный в zetcd. Он предполагает добавление в начало ключа одного байта, равного глубине узла в дереве. Приведу пример.

"/foo" -> "\u01/foo"
"/foo/bar" -> "\u02/foo/bar"

Тогда получить всех непосредственных детей ключа "/foo" можно, запросив диапазон ["\u02/foo/", "\u02/foo0"). Да, в ASCII "0" стоит сразу после "/".

Но как в таком случае реализовать удаление вершины? Получается, нужно удалить все диапазоны вида ["\uXX/foo/", "\uXX/foo0") для XX от 01 до FF. И тут мы упёрлись в лимит числа операции внутри одной транзакции.

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

"/very" -> "/\u00very"
"/very/long" -> "/very/\u00long"
"/very/long/path" -> "/very/long/\u00path"

Тогда удаление ключа "/very" превращается в удаление "/\u00very" и диапазона ["/very/", "/very0"), а получение всех детей – в запрос ключей из диапазона ["/very/\u00", "/very/\u01").

Удаление ключа в ZooKeeper

Как я уже упомянул, в ZooKeeper нельзя удалить узел, если у него есть дети. Мы же хотим удалять ключ вместе с поддеревом. Как быть? Мы делаем это оптимистично. Сначала рекурсивно обходим поддерево, получая детей каждой вершины отдельным запросом. Затем строим транзакцию, которая пытается удалить все узлы поддерева в правильном порядке. Разумеется, между чтением поддерева и удалением в нём могут произойти изменения. В таком случае транзакция провалится. Более того, поддерево может измениться ещё в процессе чтения. Запрос детей очередного узла может вернуть ошибку, если, например, эта вершина уже была удалена. В обоих случаях мы повторяем весь процесс заново.

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

set в ZooKeeper

В ZooKeeper отдельно существуют методы, которые работают со структурой дерева (create, delete, getChildren) и которые работают с данными в узлах (setData, getData).При этом все методы имеют строгие предусловия: create вернёт ошибку, если узел уже создан, delete или setData – если его ещё не существует. Нам же был нужен метод set, который можно вызывать, не задумываясь о наличии ключа.

Один из вариантов – применить оптимистичный подход, как при удалении. Проверить, существует ли узел. Если существует, вызвать setData, иначе – create. Если последний метод вернул ошибку, повторить всё сначала. Первое, что стоит заметить, – это бессмысленность проверки на существование. Можно сразу вызвать create. Успешное завершение будет означать, что узла не существовало и он был создан. В противном случае create вернёт соответствующую ошибку, после чего нужно вызвать setData. Конечно, между вызовами вершина может быть удалена конкурирующим вызовом, и setData тоже вернёт ошибку. В этом случае можно повторить всё заново, но стоит ли?

Если оба метода вернули ошибку, то мы точно знаем, что имело место конкурирующее удаление. Представим, что это удаление произошло после вызова set. Тогда какое бы значение мы не пытались установить, оно уже стёрто. Значит можно считать, что set выполнился успешно, даже если на самом деле ничего записать не удалось.

Более технические тонкости

В этом разделе мы отвлечёмся от распределённых систем и поговорим про кодинг.
Одним из основных требований заказчика была кроссплатформенность: в Linux, MacOS и Windows должен поддерживаться хотя бы один из сервисов. Изначально мы вели разработку только под Linux, а в остальных системах начали тестировать позже. Это вызвало массу проблем, к которым некоторое время было совершенно непонятно как подступиться. В итоге сейчас в Linux и MacOS поддерживаются все три сервиса координации, а в Windows – только Consul KV.

С самого начала для доступа к сервисам мы старались использовать готовые библиотеки. В случае с ZooKeeper выбор пал на ZooKeeper C++, который в итоге так и не удалось скомпилировать в Windows. Это, впрочем, неудивительно: библиотека позиционируется как linux-only. Для Consul единственным вариантом оказался ppconsul. В него пришлось добавить поддержку сессий и транзакций. Для etcd полноценной библиотеки, поддерживающей последнюю версию протокола, так и не нашлось, поэтому мы просто сгенерировали grpc клиент.

Вдохновившись асинхронным интерфейсом библиотеки ZooKeeper C++, мы решили тоже реализовать асинхронный интерфейс. В ZooKeeper C++ для этого используются примитивы future/promise. В STL они, к сожалению, реализованы весьма скромно. Например, нет метода then, который применяет переданную функцию к результату future, когда он становится доступен. В нашем случае такой метод необходим для преобразования результата в формат нашей библиотеки. Чтобы обойти эту проблему, пришлось реализовать свой простой пул потоков, поскольку по требованию заказчика мы не могли использовать тяжёлые сторонние библиотеки, такие как Boost.

Наша реализация then работает следующим образом. При вызове создаётся дополнительная пара promise/future. Новый future возвращается, а переданный помещается вместе с соответствующей функцией и дополнительным promise в очередь. Поток из пула выбирает из очереди несколько futures и опрашивает их, используя wait_for. Когда результат становится доступным, вызывается соответствующая функция, и её возвращаемое значение передаётся в promise.

Этот же пул потоков мы использовали для выполнения запросов к etcd и Consul. Это означает, что с нижележащими библиотеками могут работать несколько разных потоков. ppconsul не является потокобезопасным, поэтому обращения к нему защищены блокировками.

С grpc можно работать из нескольких потоков, но есть тонкости. В etcd watches реализованы через grpc streams. Это такие двунаправленные каналы для сообщений определённого типа. Библиотека создаёт единственный stream для всех watches и единственный поток, который обрабатывает поступающие сообщения. Так вот grpc запрещает производить параллельные записи в stream. Это значит, что при инициализации или удалении watch’а нужно дождаться, пока завершиться отправка предыдущего запроса, перед тем как посылать следующий. Мы используем для синхронизации условные переменные.

Итог

Смотрите сами: liboffkv.

Наша команда: Раед Романов, Иван Глушенков, Дмитрий Камальдинов, Виктор Крапивенский, Виталий Иванин.

Марш против RDBMS или проекты распределенных хранилищ (key-value stores) / Хабр

Вот вы часто создаете проекты? И, наверное, везде применяете базу данных, в частности, MySQL (а кто-то и PostgreSQL). Но вот что интересно, по опыту да и просто после чтения описания различных архитектур видно, что далеко не везде в проекте нужны ключевые особенности баз данных, во многих случаях базу используют просто как некоторое хранилище обычных данных. Например, в системах кеширования базы обычно не применяются, более того, кеширование как раз используют для того, чтобы избежать лишних запросов. А что используют для кеширования наиболее часто? Memcached. А что это такое? Это распределенная система хранения данных на основе хеш-таблицы. В общих чертах, это просто хранилище пар ключ-значение, над которыми можно производить только основные операции — запись, чтение, удаление и проверку на присутствие. Да-да, нет никаких фильтров, выборок, сортировки, самый максимум — система тегов для выборки одним запросом всех связанных записей. И во многих случаях такого функционала вполне достаточно.

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

Вторым критерием или особенностью является распределённость. Для СУБД это часто решается достаточно сложно или при помощи сторонних средств. Хранилища данных строятся на основе DHT (Distributed Hash Table) и изначально готовы к распределенной работе, обеспечивая масштабируемость и устойчивость к отказам отдельных узлов. В одних системах это решается за счет среды (например, если хранилище работает поверх Erlang VM), вторые используют встроенные средства распределенной работы (например, JGroups для систем на Java), либо собственные решения, как Memcached.

Немаловажна и полная готовность таких систем для работы в Cloud-среде, не даром именно такое хранилище работает у Amazon (S3 и SimpleDB). Всем известный BigTable от Google также, по большей части, как раз система хранения и обработки пар ключ/значение. Из-за простоты и даже тривиальности API (но не всегда и внутреннего устройства, хотя оно и проще чем у стандартных SQL DB) решения отлично масштабируются (как на чтение, так и на запись), в том числе и динамически, без перерыва в работе. Так что если у вас есть или будет кластер, присмотритесь к таким решениям. Но есть один момент, о котором стоит упомянуть — очень часто такие системы работают только с хранением данных в памяти, если же требуется постоянное хранение, используются бек-енд системы, в том числе и хранение в обычной реляционной базе данных, хотя это часто может налагать ограничения на данные и их параметры (а также замедляет работу).

Для чего же можно такое применить? Да везде, где у вас есть потребность хранить большое (практически неограниченное) количество данных, которые могут быть разбиты на отдельные независимые блоки. Это могут быть отдельные статьи, фотографии, видео или другие большие бинарные объекты, записи в логе, профайлы пользователей, сессионные данные (кстати, мы раньше анонсировали свою экспериментальную открытую разработку, сессионный сервер на Java для распределенного хранения сессий РНР приложений, аналогичное решение есть в промышленном Zend Platform). В большинстве случаев все ограничивается либо набором бинарных данных, либо текстовой строкой с данными или кодом в сериализированном виде, поэтому данные можно как использовать дальше в программе обработки, либо сразу отдать клиенту — именно так делает плагин для Nginx, который смотрит в Memcached и, если там есть запрашиваемый контент, отдает напрямую, минуя вообще обращение к вашему скрипту. Сейчас я, к примеру, проектирую чат-сервер, там как раз в качестве основного хранилища данных будет использован распределенный кеш (Java-система, использующая кеш с репликацией через JGroups), который по сути такое же хранилище данных в виде ключ и значение.

Ладно, хватит теории, посмотрим, какие существуют системы хранения на рынке (конечно, open source).

  • Project Voldemort — один из интереснейших проектов (планирую рассказать о нем как то подробнее). Написан на Java, реализован шардинг (partitioned) и репликация данных. Если требуется постоянное хранение, используется BerkleyDB или MySQL, можно и свое хранилище дописать, система хранения основана на плагинах, потому это достаточно просто. К сожалению, похоже, что есть только API для Java-приложений (либо использование Facebook Thrift протокола для других клиентов). Из данных можно хранить структурированные данные (массивы), blob (двоичные пакеты данных) и обычный текст. Определенные сложности есть при горячем масштабировании, добавление новых нод в кластер не самое простое действие.
  • Scalaris — транзакционная система хранения, основанная на Erlang, при этом работает только с данными в памяти, не используя постоянного хранения на диске. Для отказоустойчивости используют шардинг и репликацию, а также «non-blocking Paxos commit protocol» (надо будет подробнее изучить, что это такое). Сервер имеет API для Java, Erlang и встроенный JSON RPC для взаимодействия с другими клиентами. Может масштабироваться достаточно легко и в любое время (платформа Erlang-а прекрасно для этого приспособлена).
  • MemcacheDB — мы уже писали ранее об этой системе, использует только репликацию и хранилище на диске с использованием BerkeleyDB. Наверное, простейший из всех проектов, как в установке так и в использовании, а если мы и так используете инфраструктуру на основе Memcached, то это система идеально в нее вписывается.
  • ThruDB — проект, основанный на Apache Thrift framework (открытом проекте развития протокола Thrift, разработанного Facebook). На самом деле это даже не один проект, а целое семейство сервисов для построения инфраструктуры (которые, в свою очередь, базируются на других открытых разработках) — собственно, сам сервис хранения данных с бекендами на MySQL, Amazon S3, BerkeleyDB, а также сервис очередей сообщений, сервис масштабирования, система хранения документов и даже сервис индексирования и поиска данных (использует CLucene, порт Java Lucene на С). Клиентские библиотеки есть для разных языков, в принципе, достаточно порта протокола Thrift. Очень интересное решение, если вам необходимы многие из перечисленных функций сразу.
  • Apache CouchDB — документо-ориентированная система хранения данных на базе Erlang, использующая RESTful HTTP/JSON API для взаимодействия с клиентами. Для распределённости используется инкрементная двухсторонняя репликация и автоматическое разрешение конфликтов. Кстати, может использовать JavaScript в качестве языка запросов документов. Для устойчивости используется хранение данных на диске (собственный формат) и репликации на других нодах. Исходя из сетевой природы протокола, база может работать с любым клиентом и платформой, которая может сформировать JSON HTTP запрос, включая напрямую запросы от веб-страницы (JS).

В список не вошли ещё несколько систем — например, Hadoop HBase, Cassandra, Hypertable, Dynomite, Kai, Ringo.

Интересно заметить, что в основном для такого рода систем используют или специализированные языки и платформы (Erlang здесь почти вне конкуренции) или уже ставшие классикой и мейнстримом серьезные системы вроде Java, и лишь в редких случаях базируется на собственных разработках на С/С++.

Разрабатываете высокопроизводительную систему, не обязательно веб? Нуждаетесь в специфическом хранении данных, при этом хотите получать их самым простым способом, масштабировать «в шырь и ввысь», даже не останавливая ни на секунду работы? При этом данных может быть много, но все они простые и сводятся к строкам или сериализированным структурам и двоичным блокам? Надо надежное хранение данных, распределённое и устойчивое к сбоям? Если хоть на один из этих вопросов ответ «да», вам стоит посмотреть хоть пару проектов из списка, возможно именно они позволят вашему проекту выдержать нагрузки и уверенно развиваться.

P.S. Оригинальная статья, которая подтолкнула меня на написание — там хорошая сравнительная таблица систем.

HyperDex — новое опенсорсное NoSQL key-value хранилище, заточенное на очень быстрый поиск

Авторы позиционируют HyperDex как распределённое, отказоустойчивое, легко-маштабируемое, заточенное на очень быстрый поиск NoSQL key-value хранилище.

Главная фича — новый принцип хранения объектов в многомерном эвклидовом пространстве (рис. слева), используя гиперпространственное хэширование (hyperspace hashing) (на который, кстати, авторы сейчас получают патент), которое позволяет выполнять большинство типичных задач от 2 до 13 раз быстрее, чем в MongoDB, Redis, Cassandra.


О проекте

HyperDex появился в недрах факультета компьютерных наук Корнелльского Университета силами 3-х авторов.

Один из авторов заявил о проекте на Hacker News & Slashdot лишь вчера, 22 февраля.

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

Написан на C++; 39,750 LoC; Сорсы на гитхабе (лицензия 3-clause BSD license)

Сайт | Полное описание (PDF, 15 стр., англ.)

Мануал по установке (есть пакеты для Debian, Ubuntu, Fedora)

* Важно: HyperDex заводится только на x86_64 платформе.


В двух словах про гиперпространственное хэширование (hyperspace hashing)

HyperDex представляет каждую таблицу в качестве независимого многомерного пространства, где оси — атрибуты таблицы. На примере 1-го рисунка, мы имеем таблицу, содержащую инфу о пользователе с атрибутами «First Name» (ось X), «Last Name» (Y) и «Phone Number» (Z). HyperDex присваивает каждому объекту соответствующие координаты на основе его атрибутов. Далее, объект мэпится к оным координатам методом хэширования каждого его атрибута по соответствующим осям.

В случае, когда атрибутов много, пространство разбивается на подпространства (subspaces)

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


Бенчмарки

Бенчмарки проводились тулзой YCSB (Yahoo! Cloud Serving Benchmark) на кластере из 14 нод (конфа каждой — 2х Intel Xeon 2.5 GHz E5420, 16 GB RAM, 500 GB SATA 3 Gbit 7200 RPM. 64-bit Debian 6 Linux 2.6.32 kernel)

MongoDB 2.0.0

Cassandra 0.7.3

UPD: Сейчас, спустя несколько суток после публикации на HN & Slashdot, видно что эти бенчмарки — основная тема дебатов, в которых участвуют авторы как самого проекта, так и Редиса (antirez) и другие спецы по сравниваемымм продуктам. Очевидно, что каждая БД лучше всего работает по своему фронту задач и «универсальные» бенчы не дают картины для объективного сравнения.

Описание нагрузочных сценариев на графиках (Workloads (англ.)):

  • A. 50/50 чтение-запись (сессии, пара действий с бд)
  • B. 95/5 чтение-запись
  • C. Только чтение
  • D. Добавление новых записей и чтение последних изменений
  • E. Поиск
  • F. Чтение объекта / изменение / запись обратно


                          

Вставка 10 000 000 объектов

                          

Workload B. 95% чтение, 5% запись

на 10К сценарных операций


Поиск. 10К сценарных операций. Важное замечание по этому бенчу: HyperDex ищет не по индексным (non-primary) атрибутам объектов, в то время как остальные — только по primary-key


                          

Линейная маштабируемость. на 32-х нодах HyperDex обрабатывает 3.2 млн. операций в секунду

                          

Сравнение с Redis. Workload E — поиск.

Обсуждение этого бенча в рассылке Redis (agladysh)


Обсуждение с одним из авторов (rescrv) на Hacker News | Slashdot

Контейнеры для хранения значений по ключу / Хабр

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

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

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

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

Протокол хранилища

Итак, первое, что нам необходимо — это протокол для самого хранилища, который поможет абстрагироваться от его типа. Следовательно у нас будет единый интерфейс для работы и c UserDefaults, и со связкой ключей (Keychain), и с каким-либо другим хранилищем типа «ключ-значение». Выглядит этот протокол довольно просто:

protocol KeyValueStorage {
    func value<T: Codable>(forKey key: String) -> T?
    func setValue<T: Codable>(_ value: T?, forKey key: String)
}

Так любое хранилище, соответствующее протоколу KeyValueStorage, должно реализовать два generic-метода: геттер и сеттер значений по ключу в виде строки. При этом сами значения соответствуют протоколу Codable, что позволяет хранить экземпляры типов, имеющих универсальное представление (например, JSON или PropertyList).

Стандартные реализации хранилищ данных не поддерживают тип Codable для значений, например, тот же UserDefaults. Поэтому такое разграничение типа — это побочный бонус, позволяющий хранить и примитивы Swift (числа, строки и т.д.), и целые структуры данных, сохраняя простым интерфейс самого хранилища.

Реализация протокола

Есть два пути для реализации протокола KeyValueStorage:

  • Подписать существующее хранилище под протокол и добавить необходимые методы:
extension UserDefaults: KeyValueStorage {
    func value<T: Codable>(forKey key: String) -> T? {
        // Реализация метода
    }

    func setValue<T: Codable>(_ value: T?, forKey key: String) {
        // Реализация метода
    }
}

  • Обернуть хранилище в отдельный тип, скрыв его поля для внешнего использования:
class PersistentStorage: KeyValueStorage {
    private let userDefaults: UserDefaults

    let suiteName: String?
    let keyPrefix: String

    init?(suiteName: String? = nil, keyPrefix: String = "") {
        guard let userDefaults = UserDefaults(suiteName: suiteName) else {
            return nil
        }

        self.userDefaults = userDefaults
        self.suiteName = suiteName
        self.keyPrefix = keyPrefix
    }

    func value<T: Codable>(forKey key: String) -> T? {
        // Реализация метода
    }

    func setValue<T: Codable>(_ value: T?, forKey key: String) {
        // Реализация метода
    }
}

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

Для реализации самих методов value(forKey:) и setValue(:forKey:) важно предусмотреть совместимость данных. Это необходимо, чтобы значения, сохраненные стандартными средствами UserDefaults, можно было извлечь методами из KeyValueStorage, и наоборот.

Полный пример готового к использованию класса PersistentStorage доступен по ссылке.

Контейнер значения

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

class KeyValueContainer<T: Codable> {
    let storage: KeyValueStorage
    let key: String

    var value: T? {
        get {
            storage.value(forKey: key)
        }

        set {
            storage.setValue(newValue, forKey: key)
        }
    }

    init(storage: KeyValueStorage, key: String) {
        self.storage = storage
        self.key = key
    }
}

Тип значения для контейнера ограничен протоколом Codable точно так же, как и в методах самого хранилища, поэтому вычисляемое свойство value просто проксирует ему вызовы с фиксированным ключом и типом значения.

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

Пример реализации контейнера со значением по умолчанию

class KeyValueContainer<T: Codable> {
    let storage: KeyValueStorage
    let key: String
    let defaultValue: T?

    var value: T? {
        get {
            storage.value(forKey: key) ?? defaultValue
        }

        set {
            storage.setValue(newValue, forKey: key)
        }
    }

    init(storage: KeyValueStorage, key: String, defaultValue: T? = nil) {
        self.storage = storage
        self.key = key
        self.defaultValue = defaultValue
    }
}

Контейнер решает первые две наши проблемы — фиксирует ключ и тип для считываемых и записываемых данных. Так любую попытку записать значение некорректного типа пресечет компилятор еще на этапе сборки:

func doSomething(with container: KeyValueContainer<Int>) {
    container.value = "Text" // Ошибка компиляции
}

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

Уникальность ключей

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

extension KeyValueStorage {
    func makeContainer<T: Codable>(key: String = #function) -> KeyValueContainer<T> {
        KeyValueContainer(storage: self, key: key)
    }
}

Так во все реализации протокола хранилища добавится generic-метод, возвращающий контейнер с указанным ключом. Особый интерес в этом методе представляет параметр key, который по умолчанию имеет значение, равное специальному выражению #function (документация). Это означает, что на этапе сборки вместо литерала #function подставится имя объявления, из которого был вызван метод makeContainer(key:).

Данная конструкция позволяет объявлять контейнеры в расширениях хранилищ, и их ключами будут имена вычисляемых свойств, если метод makeContainer() в них вызван без параметра key:

extension PersistentStorage {
    var foobar: KeyValueContainer<Int> {
        makeContainer()
    }
}

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

Подводя итог

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

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

extension PersistentStorage {
    // Объявление контейнера
    var foobar: KeyValueContainer<Int> {
        makeContainer()
    }
}

// Чтение значения
let foobar = storageInstance.foobar.value

// Запись значения
storageInstance.foobar.value = 123

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

На этом все. Буду рад обратной связи в комментариях. Пока!

Рейтинг DB-Engines — рейтинг популярности магазинов с ключом-значением

Рейтинг> Магазины с ключом-значением

Рейтинг DB-Engines ранжирует системы управления базами данных по их популярности. Рейтинг обновляется ежемесячно.

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

Подробнее о методике подсчета баллов.

33.

38.

☐ включает вторичные модели базы данных 65 систем в рейтинге, октябрь 2020 г.
1. 1. 1. Redis Доступна подробная информация, предоставленная поставщиком Пары ключ-значение, многомодельное Хранилище ключей и значений ,
Хранилище документов,
Графическая СУБД,
Поисковая система,
СУБД временных рядов
153,28 +1,43 +10,37
2. 2. 2. Amazon DynamoDB Доступна подробная информация, предоставленная поставщиком Мультимодель Магазин документов ,
Магазин ключевых значений
68.41 +2,23 +8,24
3. 3. 3. Microsoft Azure Cosmos DB Доступна подробная информация, предоставленная поставщиком Мультимодель Хранилище документов ,
Графическая СУБД ,
Хранилище ключей и значений ,
Хранилище с широкими столбцами
32,01 +0,34 +0,68
4. 4. 4. Memcached Ключевое значение 26.09 -0,25 +0.20
5. 5. 5. Hazelcast Доступна подробная информация, предоставленная поставщиком Параллельное значение, многомодельное Хранилище ключевых значений ,
Документ магазин
9,72 +0,25 +2,01
6. 6. etcd Ключевое значение 8,81 -0,04
7. 7. 6. Ehcache Ключевое значение 7,47 +0,15 +1,43
8. 8. 7. Aerospike Доступна подробная информация, предоставленная поставщиком Ключевое значение, мульти- модель Хранилище ключей ,
Хранилище документов
7,00 +0,31 +1,23
9. 9. 8. Riak KV Ключевое значение 5,74 -0.18 +0,18
10. 10. 10. ArangoDB Доступна подробная информация, предоставленная поставщиком Мультимодель Хранилище документов ,
Графическая СУБД ,
Хранилище ключевых значений ,
Поисковая система
5,55 -0,25 +0,67
11. 11. 9. OrientDB Мультимодель Магазин документов ,
Графическая СУБД ,
Магазин ключевого значения
5.47 -0,01 +0,34
12. 12. 11. Ignite Мультимодель Хранилище ключевых значений ,
Реляционная СУБД
5,42 +0,11 +0.98
13. 13. 12. Oracle NoSQL Доступна подробная информация, предоставленная поставщиком Ключ-значение, многомодельное Хранилище ключей-значений ,
Реляционная СУБД
4 .24 +0,07 +0,85
14. 17. 16. Infinispan Ключевые значения 3,85 +0,36 +1,35
15. 14. 17. RocksDB Ключевое значение 3.84 +0.20 +1.48
16. 15. 15. Oracle Berkeley DB Мультимодель Хранилище ключей и значений ,
Собственная СУБД XML
3.75 +0.19 +1.04
17. 16. 13. InterSystems Caché Мультимодель Хранилище ключевых значений ,
Объектно-ориентированная СУБД ,
Реляционная СУБД ,
Хранилище документов
3,71 +0,17 +0,62
18. 18. 14. LevelDB Ключевое значение 3,65 +0,27 +0.74
19. 19. LMDB Ключевое значение 3,27 0,00
20. 20. 19. Oracle Coherence Ключевое значение 2,92 -0,02 +0,64
21. 21. 20. ScyllaDB Доступна подробная информация, предоставленная поставщиком Мультимодель Магазин ключевых значений ,
Широкая колонка магазин
2.82 +0,24 +0,87
22. 22. 18. Amazon SimpleDB Ключевое значение 2,52 -0,05 +0,16
23. 23. 21. GridGain Мультимодель Хранение ключей и значений ,
Реляционная СУБД
2,21 -0,01 +0,64
24. 24. 22. Geode Ключевое значение 1,73 -0,11 +0,21
25. 25. 26. InterSystems IRIS Доступна подробная информация, предоставленная поставщиком Multi-model Document store ,
Key-value store ,
Объектно-ориентированная СУБД ,
Реляционная СУБД
1,68 +0,05 +0,76
26. 26. 23. Tarantool Доступна подробная информация, предоставленная поставщиком Ключ-значение, многомодельное Хранилище ключей-значений ,
Хранилище документов,
Реляционная СУБД
1,51 +0.01 +0.48
27. 29. 24. GT.M Ключевое значение 1,15 +0,00 +0,17
28. 28. 27. WiredTiger Ключ-значение 1.13 -0,11 +0,22
29. 27. 25. NCache Доступна подробная информация, предоставленная поставщиком Ключ-значение, многомодельное Хранилище ключей-значений ,
Документ магазин,
Поисковая система
1,12 -0,14 +0,17
30. 30. 29. FoundationDB Мультимодель Магазин документов ,
Магазин ключей ,
Реляционная СУБД
1.11 +0.06 +0.34
31. 31. 30. XAP Мультимодель Хранилище документов ,
Хранилище ключевых значений ,
Объектно-ориентированная СУБД
1.01 +0.01 +0.26
32. 32. 28. ZODB Ключевое значение 1.00 +0.10 +0.21
33. 32. Tokyo Cabinet Ключевое значение 0.80 -0.01 +0.24
34. 34. 31. WebSphere eXtreme Scale Ключевое значение 0,80 +0,01 +0,13
35. 35. 34. Графический движок Мультимодель Графическая СУБД ,
Хранилище ключевых значений
0.69 +0,03 +0,17
36. 36. 33. BoltDB Ключевое значение 0,69 +0,05 +0,13
37.
37. 38. Tokyo Tyrant Ключевое значение 0,66 +0,06 +0,21
38. 37. 35. c-treeACE

.

php — Какие есть хорошие, быстрые варианты постоянного хранения для данных ключ-> значение?

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

Загрузка…

.

Хранить данные ключ-значение на диске

меню

  • Документы
    • Начать

      • 1. Установить
      • 2. Настройте редактор.
      • 3. Тест-драйв.
      • 4. Напишите свое первое приложение.
      • 5. Узнать больше
      • С другой платформы?

        • Flutter для разработчиков Android
        • Flutter для разработчиков iOS
        • Flutter для разработчиков на React Native
        • Flutter для веб-разработчиков
        • Flutter для Xamarin.Разработчики форм
        • Введение в декларативный интерфейс
      • Обзор языка Dart
      • Создание веб-приложения
    • Образцы и руководства

      • Галерея Flutter [запущенное приложение]
      • Галерея Flutter [репо]
      • Примеры приложений на GitHub
      • Кулинарная книга
      • Codelabs
      • Учебники
    • Развитие

      • Пользовательский интерфейс

        • Введение в виджеты
        • Макеты зданий

          • Макеты во Flutter
          • Руководство
          • Создание адаптивных приложений
          • Понимание ограничений [НОВОЕ]
          • Ограничения коробки
        • Добавление интерактивности
        • Активы и изображения
        • Навигация и маршрутизация
        • Анимации

          • Вступление
          • Обзор
          • Руководство
          • Неявная анимация
          • Анимация героев
          • Поэтапная анимация
        • Расширенный интерфейс

          • Щепки
          • Жесты
          • Заставки
        • Каталог виджетов
      • Данные и бэкэнд

        • Государственное управление

          • Вступление
          • Мыслите декларативно
          • Эфемерное и состояние приложения
          • Простое управление состоянием приложения
          • Параметры
        • Сеть и http
        • JSON и сериализация
        • Firebase
      • Доступность и интернационализация

        • Доступность
        • Интернационализация
      • Интеграция платформы

        • Добавление поддержки iOS App Clip
        • Поддержка Apple Watch
        • Взаимодействие C и C ++
        • Размещение собственных представлений для Android и iOS
        • Web FAQ
        • Написание кода для конкретной платформы
      • Пакеты и плагины

        • Использование пакетов
        • Разработка пакетов и плагинов
        • Программа Flutter Favorites

.

couchdb — Общие сведения о хранилищах ключей и значений

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

Загрузка…

    .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *