Подход к моделированию хранилища документов
В Aerospike версии 4.6 (выпущен в августе 2019 г.) добавлена возможность применять операции список и карта к элементам, вложенным на произвольную глубину. В этом посте мы увидим, как это работает. Я начну с обзора, поэтому, если вы знакомы с Aerospike, можете пропустить следующий раздел.
Обзор
Aerospike - это высокопроизводительная распределенная база данных, ориентированная на строки. Объекты в Aerospike называются записями. Они похожи на строки в реляционных базах данных.
Записи однозначно идентифицируются тройным кортежем (namespace, set, user-key)
. пространство имен объединяет концепции базы данных и табличного пространства реляционной базы данных. set в Aerospike похож на таблицу без схемы. ключ пользователя - это просто уникальный идентификатор записи в этом наборе с точки зрения приложения. Это похоже на то, как первичный ключ однозначно определяет одну строку таблицы в реляционной базе данных. Вся запись хранится непрерывно на носителе, определяемом пространством имен (на SSD, в постоянной памяти или в DRAM).
Запись Aerospike хранит свои данные в одной или нескольких ячейках, которые похожи на столбцы строки в реляционной базе данных, только без схемы. Каждая ячейка содержит значение поддерживаемого типа данных: целое число, двойное число, строка, байты, список, карта, геопространственный. Каждый тип данных имеет API атомарных серверных операций. Например, целочисленный тип данных имеет операцию приращения, которую можно использовать для реализации счетчиков в записи.
Типы данных list и map особенно интересны и гибки. В качестве единиц хранения они могут встраивать в себя другие типы данных, включая вложение других списков и карт. У обоих есть обширные API.
База данных Aerospike выполняет транзакции с одной записью. Множественные операции с одной записью могут эффективно выполняться при блокировке записи, атомарно и изолированно.
Отслеживание рекордов
В этом примере мы будем отслеживать рекорды классических видеоигр с помощью вложенной структуры данных { player: [score, {attribute map}] }
.
Оценки можно добавлять по отдельности или сразу с помощью map_put_items()
, реализации клиентом Python операции add_items()
API карты Aerospike.
На этом этапе можно получить оценки по рангу. Ранг устанавливается на основе правил упорядочивания значений этой карты, которые в данном случае являются списками с кортежем structure[score, {attribute map}]
.
[ 'ETC', [9200, {'dt': '2018-05-01 13:47:26', 'ts': 1525182446891}], 'CPU', [9800, {'dt': '2017-12-05 01:01:11', 'ts': 1512435671573}], 'CFO', [17400, {'dt': '2017-11-19 15:22:38', 'ts': 1511104958197}], 'EIR', [18400, {'dt': '2018-03-18 18:44:12', 'ts': 1521398652483}], 'SOS', [24700, {'dt': '2018-01-05 01:01:11', 'ts': 1515114071923}], 'ACE', [34500, {'dt': '1979-04-01 09:46:28', 'ts': 291807988156}]]
До версии 4.6 Aerospike не было возможности применять операции карты к карте атрибутов, вложенной в значения списка карты scores
. Операции Сложные типы данных (CDT) были ограничены элементами верхнего уровня рассматриваемого списка или карты. Предположим, что карта атрибутов может содержать награды, выигранные игроками.
В более ранних версиях Aerospike нам нужно было бы прочитать значение списка с сервера в приложение, добавить карту наград в карту атрибутов, а затем записать измененный список обратно на сервер. Используя функцию, добавленную в версии 4.6, теперь это можно сделать атомарно. Я создал контекст, который определяет путь к карте атрибутов, а затем применил операцию map_put
в этом месте. Политика карты MAP_WRITE_FLAGS_CREATE_ONLY
гарантирует, что эта награда будет предоставлена один раз. Политика MAP_WRITE_FLAGS_NO_FAIL
заставляет операцию вести себя терпимым образом, если награда 🦄 уже была на месте. Транзакция переходит к следующей операции (если она есть), и клиентской стороне не нужно обрабатывать исключение.
{ 'ACE': [ 34500, { 'awards': {'🏆': 1}, 'dt': '1979-04-01 09:46:28', 'ts': 291807988156}], 'CFO': [ 17400, { 'awards': {'🦄': 1}, 'dt': '2017-11-19 15:22:38', 'ts': 1511104958197}], 'CPU': [9800, {'dt': '2017-12-05 01:01:11', 'ts': 1512435671573}], 'EIR': [ 18400, {'dt': '2018-03-18 18:44:12', 'ts': 1521398652483}], 'ETC': [9200, {'dt': '2018-05-01 13:47:26', 'ts': 1525182446891}], 'SOS': [ 24700, {'dt': '2018-01-05 01:01:11', 'ts': 1515114071923}]}
В приведенном выше разделе кода контекст расширен до большей глубины, так что награда 🏆 инициализируется, если ее не существует, а затем увеличивается. У вас должен быть путь, ведущий к элементу, поэтому простое увеличение без инициализации может привести к сбою. Обратите внимание, что контекстный путь не обязательно должен быть физическим набором изменений направления. Здесь присваивается элементу с наивысшим рангом (-1).
[ 'EIR', [18400, {'dt': '2018-03-18 18:44:12', 'ts': 1521398652483}], 'SOS', [24700, {'dt': '2018-01-05 01:01:11', 'ts': 1515114071923}], 'ACE', [ 34500, {'awards': {'🏆': 2}, 'dt': '1979-04-01 09:46:28', 'ts': 291807988156}]]
Этот код находится в репозитории aerospike-examples / aerospike-models на GitHub.