Хранениеjson-документов
в TarantoolДроздов Андрей. Mail.Ru Group
Agenda
Как хранить json-документы в Tarantool
Стратегии работы с версиями
Как это используется в production
Бенчмарки и сравнения
Проблема
ПроблемаВиртуализация данных
Обогащение данных от источника
Интеграция с legacy проектами
Объединение данных от нескольких источников
Очень большие нагрузки
Все данные — JSON
Проблема
С чего начали?
Схемы Avro{ "First": "John", "Last": "Doe" }
{ "name": "Person", "type": "record", "fields": [
{ "name": "First", "type": "string" },
{ "name": "Last", "type": "string" } ] }
Хранится как: ["John", "Doe"]
[ "John", "Doe", "Was here!" ]
{ "First": "John", "Last": "Doe" "Notes": "Was here!" }
V2 V2
Versions
[ "John", "Doe", "Was here!" ]
{ "First": "John", "Last": "Doe" }
V2 V1
Versions
[ "Jane", "Doe" ]
{ "First": "Jane", "Last": "Doe" "Notes": "" }
V1 V2
Versions
Compiler
Apache implementation (too slow)
Code generation?
LuaJIT
LLVM
RPS (×1000)
500
1000
1500
2000
Baseline (C) Codegen (LuaJIT)
Interpreter
JIT
RPS (×1000)
500
1000
1500
2000
2500
3000
3500
Baseline (C) Codegen (LuaJIT) Codegen (LLVM)
Ключевые понятия
compile(text): Schema
flatten(schema_in, schema_out, json): Tuple
unflatten(schema_in, schema_out, tuple): JSON
xflatten(schema_in, schema_out, json): UpdateQuery
Миграции
add — добавление поля в структуру
delete — удаление поля из документа
hide — удаление поля без удаления данных
rename — переименование поля при помощи alias
Стратегия read
Стратегии write
Все запросы выполнять в версии клиента?
Минусы
Версии не возрастают, один и тот же запрос может вернуть разные ответы
Стратегия insert
Стратегия update
Стратегия update
Стратегия update
Стратегия update
[ "Jane", "Doe" ]
{ "First": "Andrey", }
Храним в версии 1
Запрос на обновление
Стратегия update
[ "Jane", "Doe" ]
{ "First": "Andrey", }
[ "Jane", "Doe", "" ]
Храним в версии 1
Запрос на обновление
Приводим к max[ "Jane", "Doe" ]
Стратегия update
[ "Jane", "Doe" ]
{ "First": "Andrey", }
[ "Jane", "Doe", "" ]
[ "Andrey", "Doe", "" ]
Храним в версии 1
Запрос на обновление
Приводим к max Храним в версии 2
Результат xflatten
[ "=", 1, "Andrey" ]
[ "Jane", "Doe" ]
Стратегия update
Плюсы: Версии будут приводиться к актуальной
Минусы: Теряем данные из удаленных полей
Лечение:
• Восстанавливаем значения при помощи defaults
• Hide вместо удаления полей
Дополнительно
• Валидация данных по схеме без преобразований
• Развертка полей JSON в tuple (имена, типы данных)
REST API?
REST API
Примеры архитектур
Одноуровневая система с репликацией
Отдельно приложение, отдельно хранение
Хранение по ttl
Совместное хранение в памяти и на диске (vinyl)
Хранение отдельно
Устаревание данных
Подводные камни
• Нужно описать логику преобразования версий
• Нужно написать обертки под каждый тип запроса
• Нужно связывать авро схему и индексы в tuple
• Удобно иметь связи между схемами
• Удобно создавать простые API автоматически (без lua)
Tarantino
Что это дает?• Автоматическая настройка tarantool
• Вся конфигурация - один json-файл
• Версионирование запросов на лету
• Хранится только актуальная версия
• Иерархические связи между схемами
• Не нужно программировать на lua
Пример конфигурации{ "memory": 30, "port": 3301, "index": [ "user":["uid"], "device":["uid", "sno"] ], "relations": { "user": ["device"] } "api": { "1": { "user":{}, "device":{} ] } }
Что произойдет внутри?box.cfg{ slab_alloc_arena=30, listen=3301 wal_mode=«write» } box.schema.create_space(«user») box.space.user:create_index(…) box.schema.create_space(«device») box.space.device:create_index(…)
Что произойдет снаружи?• /api/v1/user/1
• /api/v1/user?limit=100&offset=0
• /api/v1/device/1/1
• /api/v1/device?limit=100&offset=0
• /api/v1/user/1/?related=prefetch
Join{ "uid": 1, "First": «John", "Last": «Doe" "device": [ { "uid": 1, "sno": 1, "name":"myD", } ] }
BenchИспользуем 4 физических ядра
Одинаковая модель данных (2 кб на запрос)
• GO: Go-restful + Mongodb (Mora)
• NodeJS: Express + Mongoose
• Python: Django Rest Framework (nginx + uwsgi)
• Tarantool: Tarantino (avro + memtx)
node js: express + mongo
Чтениеnode js: express + mongo
Чтениеnode js: express + mongo
Записьnode js: express + mongo
Записьnode js: express + mongo
go restful + mongo
Чтениеgo restful + mongo
Чтениеgo restful + mongo
Записьgo restful + mongo
Записьgo restful + mongo
go httprouter
go httprouter
Django rest framework
Django rest framework
Tarantino
• Одно ядро
• Преобразование схемы в каждом запросе
Чтениеtarantino (avro + tarantool memtx)
Чтениеtarantino (avro + tarantool memtx)
Записьtarantino (avro + tarantool memtx)
Записьtarantino (avro + tarantool memtx)
Результаты
0
7500
15000
22500
30000
NodeJS (4 ядра) GO (4 ядра) Tarantool (1 ядро)
Чтение Запись
Производительность 4 ядра
Чтение: 100000 rps Запись: 60000 rps
tarantino (avro + tarantool memtx)
Use cases
• Легковесные restful сервисы
• Бэкенды для мобильных приложений
• Выкатывание нескольких версий приложения одновременно
• Scientific приложения (анализ данных) - хранение грязных и чистых данных в разных версиях
Спасибо за внимание
• http://tarantool.org
• https://github.com/tarantool/tarantino
• https://github.com/tarantool/avro-schema
• https://github.com/tarantool/nginx_upstream_module