Хранение json-документов в tarantool / Андрей Дроздов (mail.ru...

Post on 16-Apr-2017

527 Views

Category:

Engineering

7 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Хранение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

• andrey@tarantool.org

top related