Хранение NoSQL данныхв Oracle и PostgreSQL
Аркаев Андрей
Чем занимаемся
Naumen Contact Center
→ Звонок (вх/исх)→ Распределение на оператора→ Заполнение анкеты→ Сохранение в БД→ Отчёты
Анкета
Наши требования
3 млн звонков с анкетами в сутки 20 атрибутов на каждую анкету сложная иерархия форм анкеты
(сотни разных анкет и форм) PostgreSQL, Oracle >= 10g Скорость открытия 2с 1000 операторов
Хранение атрибутов в отдельной таблице
anketa: id card_id creationDate …
attr_card: card_id
attr: card_id property_id str_value1 str_value2 date_value dbl_value int_value clob_value
Хранение атрибутов в отдельной таблице
anketa: id other_value creationDate …
attr: ank_id (→anketa.id) property_id str_value1 str_value2 date_value dbl_value int_value clob_value
Плюсы и минусы
+ схема данных фиксирована+ можно использовать для разных классов
- много данных- сложно партиционировать- значения хранятся в конкретном виде- много null-значений- большие индексы
Хранение атрибутов столбцами
anketa: Id creationDate ... NewAttr1 NewAttr2 NewAttr3 ...
anketa: id ... NewAttr5000 ...
Хранение атрибутов столбцами
anketa: Id creationDate ... NewAttr1
anketa_type1 obj_id (->anketa.id) NewAttr2 NewAttr3
anketa_type2 obj_id (->anketa.id) NewAttr4 NewAttr5
Плюсы и минусы
+ любой тип данных+ индексы на нужные столбцы+ партицион-е
- ограничено количество атрибутов- тяжелое удаление- неудобные запросы- блокировки схемы
Что использовать
PostgreSQL >= 9.4 : json или jsonb Oracle >=10g : xmltype (clob) Oracle 11g : xmltype (binary xml) MsSQL 2014 : xml MySQL : -
JSON
{ "form1": { "nickname": "Superman", "name": "Кларк Кент", "alias": [ "Человек из стали",
"Последний сын Криптона", "Человек завтрашнего дня" ],
"height": 190, "weight": 106, "alien": true }, … }
Получение в PostgreSQL
Создание столбца:
alter table test_table add column attrData jsonb; Получение значения:
SELECT attrData->’form3’->>’title’FROM test_table
XML
<?xml version="1.0" encoding="UTF-8"?><root> <form1> <nickname>Superman</nickname> <name>Кларк Кент</name> <alias>Человек из стали</alias> <alias>Последний сын Криптона</alias> <alias>Человек завтрашнего дня</alias> <height>190</height> <weight>106</weight> <alien>true</alien> </form1> ...
Получение в Oracle
Создание столбца:
alter table test_tableadd column attrData xmltype;
Получение значения:SELECT extractValue(jsonData,'/root/form3/title'),extract(jsonData,'...').getStringVal(),extract(jsonData,'...').getNumVal(),extract(jsonData,'...').getClobVal()FROM test_table
Плюсы и минусы
+ не нужны join+ любое количество атрибутов+ удобнее получать значения ч/з SQL+ история+ партицион-е
- разный синтаксис у разных БД- тип данных: строка- нет FK- Oracle 4Кб
Получение массивов в PostgreSQL
SELECT attrData→'form1'→'alias'FROM test_table
["Человек из стали", "Последний сын Криптона", "Человек завтрашнего дня"]
Получение массивов в Oracle
select alias.COLUMN_VALUE FROM test_table, TABLE( XMLSequence( extract(attrData, '/root/form1/alias'))) alias
"Человек из стали" "Последний сын Криптона" "Человек завтрашнего дня"
Индексы
PostgreSQL Gin-индекс
Oracle ctxsys.ctxxpath XMLIndex (Oracle 11g)
Индекс в PostgreSQL
CREATE INDEX idxginp ON test_table USING gin (attrData jsonb_path_ops) SELECT * FROM test_table WHERE attrData @> '{"form1": { "nickname" : "Superman"}}'
Индекс в Oracle
CREATE INDEX idx_attrdata ON test_table(attrData) indextype is ctxsys.ctxxpath SELECT * FROM test_table WHERE existsNode(attrData, '/root/form1/nickname="Superman"')=1
Результаты PostgreSQL
После
До
0 10 20 30 40 50 60 70
ИндексыАтрибутыАнкета
Результаты Oracle
После 11g
После
До
0 10 20 30 40 50 60 70
ИндексыАтрибутыАнкета
Итоги перехода
1. Быстрая выборка и обновление2. Скорость выборки 1.6c → 0.35c3. Меньшая деградация скорости БД4. Ускорение импорта в 3 раза5. Снижение нагрузки на CPU и
требования к железу6. Уменьшение размера БД7. “Человеческий” формат данных
Планы на будущее
1. Партиционирование2. Шардинг3. Возможность перейти на честные
NoSQL БД