- PVSM.RU - https://www.pvsm.ru -

В этой статье я хочу сравнить Redis и Tarantool. У меня нет цели сделать громогласный вывод «Tarantool лучше!» или «Redis круче!». Я хочу понять их сходства и отличия, разобраться, для каких задач какую технологию выбрать. Потому что это очень близкие на первый взгляд вещи, и вопросы про их отличия я вижу часто.
Для этого мы посмотрим на технологии в трёх частях:
Смело переходите сразу к наиболее интересной вам части. Или даже сразу к итоговой табличке сравнения, которую я прикладываю в заключении.
Поехали!
Redis и Tarantool — это in-memory технологии. Их ещё называют «резидентными БД», но я буду писать короче — «в памяти» или in-memory. Так что такое БД в памяти?
Это база, которая весь объём данных хранит целиком в оперативной памяти. Размер такой базы лимитирован ёмкостью оперативной памяти узла, что может ограничивать нас в количестве данных, но увеличивает скорость на порядок.
Если данных слишком много, БД в памяти способны сохранять данные на диск. Можно перезагрузить узел и не потерять информацию. Стереотип про ненадёжность БД в памяти сильно устарел, их можно использовать как основное хранилище в production. Например, Mail.ru Cloud Solutions использует Tarantool как основную БД для хранения метаинформации в своём объектном хранилище [1].
БД в памяти нужны для высокой скорости доступа к данным, условно от 10 000 запросов в секунду. Например, запросы к ленте новостей Кинопоиска в день релиза Снайдерката «Лиги Справедливости», Яндекс.Маркет перед Новым Годом или Delivery Club вечером в пятницу.
Кеш. БД в памяти традиционно используют как кеш для более медленных баз данных. Это логично, память быстрее диска (даже SSD). Но в жизненном цикле любого кеша случаются перезагрузки, падения, сетевая недоступность, нехватка памяти и прочие инфраструктурные беды.
С течением времени кеши стали уметь в персистентность, резервирование и шардирование.
Шардирование — это большая система. Резервирование — это надёжная система. Вместе с персистентностью получается кластерное хранилище данных. Можно положить туда терабайты информации и крутить их на скорости 1 000 000 RPS.

OLTP. Расшифровывается как Online Transaction Processing, обработка транзакций в реальном времени. In-memory решения подходят для такого типа задач благодаря своей архитектуре. OLTP — это большое количество коротких on-line транзакций (INSERT, UPDATE, DELETE). Главное в OLTP-системах — быстро обработать запросы и обеспечить целостность данных. Эффективность чаще всего определяется количеством RPS.
Разобрались с основами, давайте переходить на следующий уровень.
Это самый любимый запрос про БД в памяти — а насколько вы быстрые? «Сколько миллионов РПС можно снять с одного ядра?» Проведём простой синтетический тест, в нём максимально приблизим настройки баз данных. Скрипт на Go наполняет хранилище случайными ключами со случайными значениями.
MacBook Pro 2,9 GHz Quad-Core Intel Core i7
Redis version=6.0.9, bits=64
Tarantool 2.6.2
Redis
redis_test.go
package main
import (
"context"
"fmt"
"log"
"math/rand"
"testing"
"github.com/go-redis/redis"
)
func BenchmarkSetRandomRedisParallel(b *testing.B) {
client2 := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379", Password: "", DB: 0})
if _, err := client2.Ping(context.Background()).Result(); err != nil {
log.Fatal(err)
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
key := fmt.Sprintf("bench-%d", rand.Int31())
_, err := client2.Set(context.Background(), key, rand.Int31(), 0).Result()
if err != nil {
b.Fatal(err)
}
}
})
}
Tarantool
tarantool>
box.cfg{listen='127.0.0.1:3301', wal_mode='none', memtx_memory=2*1024*1024*1024}
box.schema.user.grant('guest', 'super', nil, nil, {if_not_exists=true,})
box.schema.space.create('kv', {if_not_exists=true,})
box.space.kv:create_index('pkey', {type='TREE', parts={{field=1, type='str'}},
if_not_exists=true,})
tarantool_test.go
package main
import (
"fmt"
"math/rand"
"testing"
"github.com/tarantool/go-tarantool"
)
type Tuple struct {
_msgpack struct{} `msgpack:",asArray"`
Key string
Value int32
}
func BenchmarkSetRandomTntParallel(b *testing.B) {
opts := tarantool.Opts{
User: "guest",
}
pconn2, err := tarantool.Connect("127.0.0.1:3301", opts)
if err != nil {
b.Fatal(err)
}
b.RunParallel(func(pb *testing.PB) {
var tuple Tuple
for pb.Next() {
tuple.Key = fmt.Sprintf("bench-%d", rand.Int31())
tuple.Value = rand.Int31()
_, err := pconn2.Replace("kv", tuple)
if err != nil {
b.Fatal(err)
}
}
})
}
Запуск Чтобы полностью прогрузить базы данных, используем больше потоков.
go test -cpu 12 -test.bench . -test.benchtime 10s
goos: darwin
goarch: amd64
BenchmarkSetRandomRedisParallel-12 929368 15839 ns/op
BenchmarkSetRandomTntParallel-12 972978 12749 ns/op
Результаты. Среднее время запроса к Redis составило 15 микросекунд, к Tarantool — 12 микросекунд. Это даёт Redis 63 135 RPS, Tarantool — 78 437 RPS.
Тест нужен, чтобы показать уровень производительности БД в памяти, а не для замера, кто быстрее. Каждый из вас может измерить так, что быстрее окажется нужный вариант, я это тоже понимаю.

Для надёжности хранения данных используют две основные техники:
И Redis, и Tarantool содержат эти функции. Технические подробности мы рассмотрим далее.
Масштабирование может рассматриваться для двух задач:
Redis
Узлы Redis можно соединить друг с другом асинхронной репликацией. Такие узлы будем называть репликационной группой, или replica set. Управлением такой репликационной группой занимается Redis Sentinel.
Redis Sentinel — это один или несколько объединенных в кластер специальных процессов, которые следят за узлами Redis. Они выполняют четыре основные задачи:
В случае, когда данные необходимо расшардировать на несколько узлов, Redis предлагает open source-версию Redis Cluster. Она позволяет построить кластер, состоящий из нескольких репликационных групп. Данные в кластере шардируются по 16 384 слотам. Диапазоны слотов распределяются между узлами Redis.
Узлы в кластере общаются по отдельному открытому порту, чтобы понимать состояния соседей. Приложение при работе с Redis Cluster должно использовать специальный коннектор.
Tarantool
Tarantool также содержит в себе оба механизма масштабирования: репликацию и шардирование. Основной инструмент управления масштабированием — Tarantool Cartridge. Он объединяет узлы в репликационные группы. В этой ситуации вы можете построить одну такую репликационную группу и использовать её аналогично Redis Sentinel. Tarantool Cartridge может управлять несколькими репликационными группами и шардировать данные между ними. Шардирование выполняется с помощью библиотеки vshard.
Различия
Администрирование
Корзины шардирования
Ребалансировка корзин (решардинг)
Маршрутизация запросов
Инфраструктура
В Redis основная схема данных — ключ-значение. Но в значениях могут быть разные структуры. На стороне сервера нет механизма для задания правил. Мы не можем указать, в каком ключе какой тип данных должен использоваться и какая именно структура должна быть у значения. Валидацией схемы должен заниматься или коннектор, или клиентское приложение.
В Tarantool на стороне сервера можно использовать валидацию по схеме данных:
В Redis ключом может быть только строка. В Redis можно хранить и манипулировать следующими типами данных:
В Tarantool можно хранить и манипулировать следующими типами данных:
Типы данных Redis лучше подходят для счётчиков событий, в том числе уникальных, для хранения небольших готовых витрин данных. А типы данных Tarantool лучше подходят для хранения объектов и/или документов, как в SQL и NoSQL СУБД.
Redis и Tarantool содержат в себе механизм ограничения занимаемой памяти. Когда клиент попытается добавить ещё данные, когда лимит уже был исчерпан, базы ответят ошибкой. И Redis, и Tarantool в этой ситуации продолжат выполнять запросы на чтение.
Перейдём к другому механизму, когда мы можем настроить алгоритм удаления «больше ненужных» данных. Redis содержит в себе несколько механизмов вытеснения:
Все механизмы могут быть настроены либо на весь объем данных, либо только на те объекты, которые помечены как вытесняемые.
В Tarantool для вытеснения данных можно использовать расширения expirationd или indexpiration, или создать собственную фоновую процедуру, которая будет проходить по индексу (например, с таймштампом) и удалять ненужные данные.
В Redis можно это сделать с помощью операторов:
SCAN;
Операции возвращают страницы с результатами. Для получения каждой новой страницы, необходимо передать «идентификатор» предыдущей. Операции поддерживают фильтрацию по шаблону. Для этого используется параметр MATCH. Фильтрация происходит на момент выдачи страницы, поэтому некоторые страницы могут оказаться пустыми. Это не будет означать, что страниц больше не осталось.
В Tarantool доступна гибкая схема итерации по ключам. Можно итерироваться в прямом и обратном направлении. В процессе можно дополнительно фильтровать значения. Можно сместиться на определённое значение ключа, затем проходить по следующим ключам в сторону возрастания или убывания. Направление прохода на лету менять нельзя.
Например:
results = {}
for _, tuple in box.space.pairs('key', 'GE') do
if tuple['value'] > 10 then
table.insert(results, tuple)
end
end
return results
Redis
У Redis нет вторичных индексов. Есть некоторые трюки, чтобы их имитировать:
Tarantool
В Tarantool можно строить произвольное количество вторичных индексов для данных:
HASH, TREE, RTREE, BITSET.Вывод
Вторичные ключи и удобные итераторы позволяют строить в Tarantool реляционные модели хранения данных. В Redis такую модель построить невозможно.
Механизм транзакций позволяет выполнить несколько операций атомарно. И Redis, и Tarantool поддерживают транзакции. Пример транзакции в Redis:
> MULTI
OK
> INCR foo
QUEUED
> INCR bar
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1
Пример транзакции в Tarantool:
do
box.begin()
box.space.kv:update('foo', {{'+', 'value', 1}})
box.space.kv:update('bar', {{'+', 'value', 1}})
box.commit()
end
Персистентность данных обеспечивается двумя механизмами:
И Redis, и Tarantool содержат оба механизма персистентности.
Redis
Redis периодически сбрасывает все данные из памяти на диск. Происходит это по-умолчанию каждые 60 секунд (настраивается). Redis использует механизм ОС fork для «копирования» текущих данных в памяти, затем информация сохраняется на диск. Если происходит аварийное завершение, то Redis восстановит состояние из последнего сохранения. Если последний снапшот был сделан давно, то данные, пришедшие после снапшота, будут утеряны.
Журнал операций используется для сохранения всей приходящей в базу информации. Каждая операция сохраняется в журнал на диске. Так, при запуске Redis восстанавливает своё состояние из снапшота и затем донакатывает оставшиеся транзакции из журнала.
Tarantool
Tarantool периодически сохраняет текущие in-memory данные на диск и записывает каждую операцию в журнал.
И в Redis, и в Tarantool каждый из механизмов может быть выключен. Для надёжного хранения данных оба механизма надо включить. Для максимального быстродействия можно отключить снапшотинг и журналирование, заплатив персистентностью. Слабоумие и отвага!
Различия
Для снапшотинга в Redis используется механизм ОС fork. Tarantool использует внутренний readview всех данных, это работает быстрее чем fork.
В Redis по умолчанию включён только снапшотинг. В Tarantool включён снапшотинг и журнал.
Redis хранит и использует только по одному файлу для снапшотов и журнала операций. Tarantool по-умолчанию хранит два файла снапшотов (можно настроить и больше) и консистентно дополняющее неограниченное количество журналов операций. При повреждении снапшот-файла Tarantool сможет загрузиться из предыдущего. Для Redis необходимо наладить механизм бекапов.
В Tarantool, в отличие от Redis, снапшоты и журналы образуют единый механизм отображения данных в файловой системе. Это значит, что в Tarantool и в файлах снапшотов и в журналах хранится полная метаинформация о транзакции, кто её сделал и когда. Она одного формата и взаимодополняющая.
Troubleshooting
Если повреждён файл журнала в Redis:
redis-check-aof --fix
Если повреждён файл журнала в Tarantool:
tarantool> box.cfg{force_recovery=true}
Хранимые процедуры — это код, выполняющийся рядом с данными. И Redis, и Tarantool предлагают Lua для создания хранимок. С точки зрения пользователя это очень простой язык. Он создавался для людей, для которых программирование будет инструментом решения задач в предметной области.
C точки зрения разработчика базы данных:
Различия
Реализация
Таймаут задач
Runtime
Вывод
Репликация — это механизм копирования объектов с одного узла на другой. Бывает асинхронная и синхронная.
И Redis, и Tarantool поддерживают асинхронную репликацию. Только Tarantool умеет в синхронную репликацию.
На практике бывают ситуации, когда мы хотим дождаться репликации объекта. И в Redis, и в Tarantool есть способы для этого:
псевдокод:
local netbox = require('net.box')
local replica = netbox.connect(...)
local replica_vclock, err = replica.eval([[
return box.info().vclock
]])
while not vclock_compare(box.info().vclock, replica_vclock) do
fiber.sleep(0.1)
end
Синхронная репликация
В Redis нет синхронной репликации. Начиная с Tarantool 2.6 синхронная репликация доступна [2].
И Redis, и Tarantool поддерживают коннекторы для популярных языков программирования:
Полные списки:
И Redis, и Tarantool плохо подходят для решения OLAP-задач. Online analytical processing имеет дело с историческими или архивными данными. OLAP характеризуется относительно низким объёмом транзакций. Запросы часто очень сложны и включают агрегацию.
В обоих случаях данные хранятся построчно, и это снижает эффективность алгоритмов агрегации в сравнении с базами с колоночным хранением.
Redis и Tarantool — однопоточные базы данных, что не позволяет распараллелить аналитические запросы.
Redis
Модули Redis представлены в трёх категориях:
Enterprise-модули:
Сертифицированные:
Все модули, отсортированные по количеству звёзд на Github: https://redis.io/modules [3]
Tarantool
Модули представлены в двух категориях:
Redis — это классный продвинутый кеш, но его нельзя брать как основное хранилище. Tarantool — это мультипарадигменная база данных, можно брать как основное хранилище. Tarantool поддерживает:
У Redis ниже порог входа. У Tarantool выше потолок в production.
Сравнение одной таблицей:
| Redis | Tarantool | |
|---|---|---|
| Описание | Продвинутый кэш в памяти. | Мультипарадигменная СУБД с сервером приложений. |
| Модель данных | Key-value | Key-value, документы, реляционная |
| Сайт | redis.io [6] | www.tarantool.io [7] |
| Документация | redis.io/%C2%ADdocumentation [8] | www.tarantool.io/ru/doc/latest [9] |
| Разработчик | Salvatore Sanfilippo, Redis Labs | mail.ru [10] Group |
| Текущий релиз | 6.2 | 2.6 |
| Лицензия | The 3-Clause BSD License | The 2-Clause BSD License |
| Язык реализации | C | C, C++ |
| Поддерживаемые ОС | BSD, Linux, MacOS, Win | BSD, Linux, MacOS |
| Схема данных | Key-value | Гибкая |
| Вторичные индексы | Нет | Есть |
| Поддержка SQL | Нет | Для одного инстанса, ANSI SQL |
| Foreign keys | Нет | Есть, с помощью SQL |
| Триггеры | Нет | Есть |
| Транзакции | Оптимистичные блокировки, атомарное выполнение. | ACID, read commited |
| Масштабирование | Шардинг по фиксированному диапазону. | Шардинг по настраиваемому количеству виртуальных бакетов. |
| Многозадачность | Да, сериализация сервером. | Да, кооперативная многозадачность. |
| Персистентность | Снапшоты и журналирование. | Снапшоты и журналирование. |
| Концепция консистентности | Eventual ConsistencyStrong eventual consistency with CRDTs | Immediate Consistency |
| API | Открытый протокол RESP redis.io/topics/protocol [11] | Открытый бинарный протокол (на базе MsgPack) www.tarantool.io/en/doc/latest/dev_guide/internals/box_protocol [12] |
| Язык скриптов | Lua | Lua |
| Поддерживаемые языки | C, C#, C++, Clojure, Crystal, D, Dart, Elixir, Erlang, Fancy, Go, Haskell, Haxe, Java, JavaScript (Node.js), Lisp, Lua, MatLab, Objective-C, OCaml, Pascal, Perl, PHP, Prolog, Pure Data, Python, R, Rebol, Ruby, Rust, Scala, Scheme, Smalltalk, Swift, Tcl, Visual Basic | C, C#, C++, Erlang, Go, Java, JavaScript, Lua, Perl, PHP, Python, Rust |
Автор: michael-filonenko
Источник [17]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/redis/363003
Ссылки в тексте:
[1] https://redis.io/clients: https://redis.io/clients
[2] https://tarantool.io/ru/download/connectors: https://tarantool.io/ru/download/connectors
[3] https://redis.io/modules: https://redis.io/modules
[4] https://www.tarantool.io/en/doc/latest/reference/: https://www.tarantool.io/en/doc/latest/reference/
[5] https://www.tarantool.io/en/enterprise_doc/rocksref/#closed-source-modules: https://www.tarantool.io/en/enterprise_doc/rocksref/#closed-source-modules
[6] redis.io: http://redis.io/
[7] www.tarantool.io: http://www.tarantool.io/
[8] redis.io/%C2%ADdocumentation: http://redis.io/%C2%ADdocumentation
[9] www.tarantool.io/ru/doc/latest: http://www.tarantool.io/ru/doc/latest/
[10] mail.ru: http://mail.ru/
[11] redis.io/topics/protocol: https://redis.io/topics/protocol
[12] www.tarantool.io/en/doc/latest/dev_guide/internals/box_protocol: https://www.tarantool.io/en/doc/latest/dev_guide/internals/box_protocol/
[13] Архитектура S3: три года эволюции Mail.ru Cloud Storage: https://habr.com/ru/company/mailru/blog/513356/
[14] Синхронная репликация в Tarantool: https://habr.com/ru/company/mailru/blog/540446/
[15] на официальном сайте: https://www.tarantool.io/ru/download?utm_source=habr&utm_medium=articles&utm_campaign=2021
[16] в Telegram-чате: https://t.me/tarantoolru?utm_source=habr&utm_medium=articles&utm_campaign=2021
[17] Источник: https://habr.com/ru/post/550062/?utm_source=habrahabr&utm_medium=rss&utm_campaign=550062
Нажмите здесь для печати.