Подход к моделированию хранилища документов

В 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.