Отображение моделей данных NoSQL в объектные спецификации

реклама
Отображение моделей данных
NoSQL в объектные спецификации
Н. А. Скворцов
Институт проблем информатики РАН
[email protected]
RCDL’2012, Переславль-Залесский
15 октября 2012 г.
План
• Общие черты и разновидности моделей данных NoSQL
• Отображение информационных ресурсов в моделях
NoSQL в унифицирующую модель
– Отображение модели «ключ-значение»
• на примере Oracle NoSQL
– Отображение модели с колоночным хранением
• на примере Cassandra
– Отображение документной модели
• на примере MongoDB
• Общие проблемы и подходы к отображению моделей
разных классов NoSQL с выявлением структуры данных
• Подходы к отображению моделей при невыявляемых
схемах данных
Основные принципы
баз данных NoSQL
•
•
•
•
•
Технологии NoSQL направлены на горизонтальное масштабирование и доступ
к данным сверхбольших объёмов
Отказ от хранения данных по кортежам
Организация данных с помощью
Доступ к данным в узле только по ключам
Горизонтальное масштабирование и репликация по узлам по хеш-значениям
– распределённые хеш-таблицы (DHT)
•
Ограниченный язык манипулирования данными
– CRUD (create/read/update/delete)
•
Сдвиг обработки с этапа запросов на этап обновления данных
– Возможные разновидности запросов известны заранее
– Материализованные взгляды под возможные запросы формируются на этапе
обновления
•
Оптимизация обновлений и буферизация вставок
– журнальная структура (log-structured)
– eventual consistency – операции чтения и запросы используют уже имеющиеся данные,
даже если эти данные ожидают обновления
– BASE-транзакции (Basically Available, Soft state, Eventually consistent)
Разновидности моделей NoSQL
• Хранилища ключ-значение
– Пары ключ-значение
– Составные ключи
– Произвольные значения
• Базы данных с колоночным хранением
– С ключом связан набор именованных колонок со значениями
– Имитация табличной структуры на основ пар ключ-значение
• Документные базы данных
– Значения могут содержать вложенные наборы пар ключ-значение
– Иерархические структуры на основе пар ключ-значение
• Иногда (не в данной работе) к NoSQL относят также
– хранилища триплетов (RDF)
– графовые системы баз данных
– объектно-ориентированные системы баз данных
Необходимость отображения
NoSQL в объектную модель
• Решение задач над множественными информационными
ресурсами
– Задачи выражена в объектной модели
• Разрешение модельной неоднородности между
спецификациями задачи и ресурсов
– Отображение моделей ресурсов в унифицирующую модель
• Отображение моделей NoSQL в объектную модель не всегда
очевидно
– Наряду со структурированными данными могут присутствовать
слабоструктурированные и неструктурированные
– Схема базы чаще всего не имеет спецификации, а предполагается
неявно
– Схема может быть нефиксированной, изменяемой динамически,
может содержать сложные переплетения экземпляров данных и
структурных элементов
Отображение модели
«ключ-значение»
На примере Oracle NoSQL
СУБД «ключ/значение» Oracle NoSQL
• Элемент хранения - пара
– составной ключ: major keys и minor keys
– произвольное неструктурированное значение
• Ключ: “Smith/John/-/phonenumber/home”
– Major key: список из 1 или более компонентов (“Smith/John”) –
влияет на выбор узла хранения по хеш-значению;
– Minor key: список из 0 или более компонентов
(“phonenumber/home”) – все значения гарантированно на одном
узле, вместе с marjor key определяет уникальный ключ
• Значение: “555 5555“
– Значение – строка байтов
– Может быть сериализацией любого набора данных,
структурированных или неструктурированных, с любой
семантикой
Операции
• Операции записи
– put(key, value), putIfAbsent, putIfPresent, putIfVersion
– delete(key), multiDelete
• Subrange (keyFirst, keyLast)
• Depth.CHILDREN_ONLY
• Операции чтения
– get(key)
– multiGet
• также MultiGetIterator, StoreIterator (по major key)
• Subrange (keyFirst, keyLast)
• Depth.CHILDREN_ONLY
– multiGetKeys
• Subrange (keyFirst, keyLast)
• Depth.CHILDREN_ONLY
Пример
List<String> majorComponents = new ArrayList<String>();
List<String> minorComponents = new ArrayList<String>();
majorComponents.add("Smith");
majorComponents.add("Bob");
minorComponents.add("phonenumber");
minorComponents.add("home");
Key myKey = Key.createKey (majorComponents, minorComponents);
String data = “555 5555";
Value myValue = Value.createValue (data.getBytes());
kvstore.put(myKey, myValue);
ValueVersion vv = kvstore.get(myKey);
Value v = vv.getValue();
String data = new String(v.getValue());
Фиксированные и
нефиксированные ключи
{“Smith/Bob/-/phonenumber/home”: “555 5555”}
{“Smith/Bob/-/phonenumber/mobile”: “333 3333”}
Smith
└ Bob
└ phonenumber
├ home: “555 5555”
└ mobile: “333 3333”
•
Разделение на major key и minor key влияет только на физическую привязку
данных к узлу
– Оно не влияет на отображение в объектную модель
•
В любом месте составного ключа могут быть данные, а могут быть
фиксированные имена в структурах данных
– Фиксированные (“phonenumber”, “home”, “mobile”)
– Нефиксированные (“Smith”, “Bob”)
– Дочерние компоненты ключей (“home”, “mobile”) – связанные с единственным
значением родительского компонента (“phonenumber”)
Отображение структур
Oracle NoSQL
1.
2.
3.
4.
5.
6.
Связанные по структуре ключей пары отображаются в классы (Class1, Class2, …).
Если компонент ключа имеет единственное значение в подобных ключах, то он становится
именем класса.
Для нефиксированные значений компонентов ключей создаются атрибуты класса (majorKey1,
majorKey2, …, minorKey1, minorKey2, …).
Тип значений атрибутов строковый. Возможно задание и преобразование типов.
Для фиксированных значений компонентов создаётся атрибутом с именем, соответствующим
значению компонента ключа.
Для дочерних фиксированных компонентов ключей создаются абстрактные типы данных,
одноимённый со значением родительского компонента, с атрибутами, соответствующими
значениям дочернего компонента.
Значение пары отображается в значение атрибута, соответствующего последнему
фиксированному значению, у которого нет дочерних компонентов. Либо, если такового нет, то
значение пары отображается в атрибут value.
Типом значения атрибута является фрейм СИНТЕЗ. Возможно задание и преобразование типов.
Если семантика и структура значения не известна, и разобрать его в виде фрейма невозможно,
задаётся фрейм – значение типа битовой строки.
Набор всех атрибутов типа, образованных компонентами ключей, указывается как уникальный.
Если с атрибутом, соответствующим фиксированному значению компонента ключа, связано
значение, соответствующее значению в паре, то включать его в набор излишне.
Отображённый пример
Smith
└ Bob
└ phonenumber
├ home: “555 5555”
└ mobile: “333 3333”
• majorKey1, majorKey2
определяют уникальное
значение, так как home и
mobile фиксированные
значения компонента ключа
{ class1; in: class;
instance_section: {
majorKey1: String;
majorKey2: String;
phonenumber: Phonenumber;
key: { unique;
{ majorKey1, majorKey2 } };
}}
{ Phonenumber; in: type;
home: String;
mobile: String;
}
Отображение операций: get
majorComponents.add("Smith");
majorComponents.add("Bob");
minorComponents.add("phonenumber");
minorComponents.add("home");
Key myKey = Key.createKey
(majorComponents,
minorComponents1);
ValueVersion vv = kvstore.get(myKey);
Value v = vv.getValue();
q([v]) :class1([majorKey1, majorKey2,
v : phonenumber.home]) &
majorKey1=”Smith” &
majorKey2=”Bob”
Отображение операций: multiget
majorComponents.add("Smith");
majorComponents.add("Bob");
Key myKey =
Key.createKey(majorComponents);
SortedMap<Key, ValueVersion> x =
kvstore.multiGet(myKey);
q(x) :class1(x/class1.inst) &
x.majorKey1=”Smith” &
x.majorKey2=”Bob”
• В классе результата будет один
объект
– из нефиксированных значений
компонентов ключа и значений
пар будут сформированы все
значения атрибутов в объектеэкземпляре класса class1.
Отображение операций:
multiget и KeyRange
majorComponents.add("Smith");
Key myKey =
Key.createKey(majorComponents);
KeyRange kr = new KeyRange(
"Bob", true, "Patricia", true);
SortedMap<Key, ValueVersion> x =
kvstore.multiget(myKey, kr);
q(x) :class1(x/class1.inst) &
x.majorKey1=”Smith” &
x.majorKey2>=”Bob” &
x.majorKey2<=”Patricia”
Отображение операций:
multiGetKeys
majorComponents.add("Smith");
Key myKey =
Key.createKey(majorComponents);
SortedSet<Key> k =
kvstore.multiGetKeys(myKey);
q([majorKey1, majorKey2]) :class1([majorKey1, majorKey2]) &
majorKey1=”Smith”
• Так как у фиксированных
ключей нет дочерних
нефиксированных, то в
результат они не включаются,
хотя операция их возвращает
– В языке запросов должны быть
средства запросов к схеме
Вспомогательные пары
•
Данные дублируются с другой
структуризацией для возможности
поиска по другим критериям
{ “Smith/Bob/-/phonenumber/home”:
“555 5555” }
{ “555 5555”: [”Smith”, ”Bob”] }
majorComponents.add("555 5555");
Key myKey = Key.createKey
(majorComponents);
ValueVersion vv = kvstore.get(myKey);
q([majorKey1, majorKey2]) :class1([majorKey1, majorKey2,
v : phonenumber.home]) &
v=”555 5555”
Неструктурируемые пары
{“majorkey1/…/majorkeyM/-/
minorkey1/…/ minorkeyN/”
: “a value”}
{ db; in: class;
instance_section: {
minorKey: {sequence;
type_of_element: String};
majorKey: {sequence;
type_of_element: String};
value: Bitstring;
key: { unique;
{ minorKey, majorKey } };
}}
Отображение модели с
колоночным хранением
На примере Cassandra
Основные термины Cassandra
• Column (колонка) – множество пар ключзначение (key-value)
• Column Family (семейство колонок) – содержит
множество колонок, у каждой из которых есть
название, значение, и временная метка, и на
которые ссылаются с помощью ключей строк
• Keyspace (пространство ключей) – содержит
набор семейств колонок
• SuperColumn (суперколонки) – колонки,
состоящие из набора подколонок
Колонки и суперколонки
Операции чтения
• get(): извлечь по имени колонки
• multiGet(): по имени колонки для множества
ключей
• getSlice(): по имени колонки или ряду имён
– возвращаются колонки
– возвращаются суперколонки
• multiGetSlice(): ряд колонок для множества ключей
• getCount(): количество колонок или суперколонок
• getRangeSlice(): ряд колонок для диапазона ключей
Операции записи
• insert(): добавить/обновить колонку (по
ключу)
• batchInsert(): добавить/обновить
множество колонок (по ключу)
• remove(): удалить колонку
• batchMutate(): как batchInsert(), но может и
удалять
Отображение
1.
2.
3.
4.
5.
Семейство колонок отображается в класс, ключ семейства
становится атрибутом id, с ним связано свойство уникальности.
Если имена колонок фиксированные (в именах не данные, а
названия, одни и те же для всех ключей), то имена колонок
отображаются в одноимённые атрибуты. Значения колонок,
связанные с одним значением ключа отображаются в значения
атрибутов у объекта с id, соответствующим значению ключа
семейства.
Если имена колонок нефиксированные (являются данными), то для
пар имя-значение создаётся абстрактный тип данных с двумя
атрибутами, а семейство колонок отображается в класс с атрибутом
id и атрибутом, значением которого является лист значений
созданного типа.
Ключи, имена колонок и значения вспомогательных семейств
(материализованных взглядов на основные семейства)
отображаются в атрибуты уже существующего класса, если все
колонки присутствуют в основных семействах
Подколонки отображаются в отдельный абстрактный тип данных.
Пример
users:
{ "1": { "firstName": "John",
"lastName": "Smith",
"phoneNumbers":
{ "home": "555 5555",
"mobile": "333 3333"}
},
"2": …
}
{ users; in: class;
instance_section: {
id: String;
firstName: String;
lastName: String;
phoneNumbers: PhoneNumbers;
key: { unique; id }; }}
{ PhoneNumbers; in: type;
home: String;
mobile: String; }
get({“firstName”, “lastName”}, "1“)
q([firstName, lastName]) :users(x/[id, firstName, lastName]) & x.id="1"
Особенные случаи
•
Вспомогательные колонки
{ “555 5555": { "firstName": "John",
"lastName": "Smith", }
•
•
Отображается в уже созданную
структуру
Данные в именах колонок
Неограниченное количество колонок
{ “1": { "home": “555 5555”,
“mobile”: “333 3333”,
“in S.-Petersburg": “222 2222",
…}
}
id: String;
phonenumber: {sequence;
type_of_element: PhoneNumber};
{ Phonenumber; in: type;
home: String;
mobile: String;
}
Отображение документной
модели
На примере MongoDB
Отображение документальной
модели
•
Вложенные структуры пар ключ-значение (JSON)
users:
{ "firstName": "John",
"lastName": “Smith",
"phoneNumbers":
{ "home": "555 5555",
“mobile": "333 3333”
}
}
•
Вторичные индексы
db.things.ensureIndex({'lastName':1});
db.users.find({'lastName': 'Smith'},
{'phoneNumbers.home':1});
•
{ users; in: class;
instance_section: {
firstName: String;
lastName: String;
phoneNumbers: PhoneNumbers;
}}
{ PhoneNumbers; in: type;
home: String;
mobile: String;
}
Неограниченно вложенные иерархии
отображаются в базу фреймов
q([v]) :- users([lastName,
v : phonenumber.home)
& lastName="Smith«
Общие проблемы и решения
•
Значения ключей могут быть фиксированными (имена атрибутов) или нефиксированными (данные)
–

•
Это определяет эксперт
В случае выявляемой схемы данных c фиксированные ключи отображаются в атрибуты типов, нефиксированные – в
значения атрибутов
Слабая структурированность моделей NoSQL
–
–
–
не определяется схема данных в явном виде
значения в парах «ключ-значение» могут быть произвольными и неструктурированными
при отсутствии схемы структура пар может изменяться динамически
•
–
нет ограничений по длине составных ключей или по вложенности пар «ключ-значение» (в случае документных баз)
•


•
системы могут использоваться для решения задач, изначально рассчитанных на использование нефиксированных и
неограниченных структур (например, иерархических)
При возможности выявляения схемы это необходимо делать в соответствии с семантикой данных
В этих случаях используется подход к отображению моделей данных, предполагающий отображение только языка
манипулирования данными
Возможные разновидности запросов, используемых при решении задач над данным должны быть
известны заранее
–
–
–


•
с помощью операции put (или Create) можно в любой момент ввести новую структуру пар «ключ-значение»
Данные, по которым необходимо организовать поиск, должны оказаться ключами в некоторых парах «ключ-значение»,
а искомые данные – значениями
Для этого могут формироваться вспомогательные пары, дублирующие данные в разной организации ключей и значений
Материализованные взгляды в этих парах формируются на этапе обновления
Вспомогательные пары при отображении не образуют новые классы, а отображаются в типы и классы, образованные
основными парами
•
В случае отображения в реляционную модель в соответствии с дополнительными парами образуются вторичные ключи
Разрешены запросы со сравнением по атрибутам, являющимся ключами в дополнительных парах, при обратном
отображении они отображаются в операции над дополнительными парами
Если схема не выявляется


Данные (ключи и значения) отображаются во фреймы (в слоты, значения слотов, вложенные фреймы, с учётом разбора
составных ключей и значений)
Запросы производятся к базе фреймов и переписываются в операции
Скачать