Привет! Меня зовут Денис, я бэкенд-тимлид в KTS.
Tarantool и Redis по большей части — два очень разных продукта. Начиная от заложенной в них функциональности и заканчивая протоколом, репликацией и кластерными решениями.
Тем не менее в них много схожего. И Tarantool, и Redis — «однопоточные» базы данных. Однопоточные взято в кавычки, потому что имеется в виду только транзакционный поток, работающий непосредственно с хранилищем. Конечно, есть и сетевые потоки, и потоки репликации, и потоки работы с диском. Также оба эти продукта — in-memory решения, если не брать в расчёт отдельный дисковый vinyl движок в Tarantool.
В статье мы хотим рассмотреть: что, если взять Tarantool как замену Redis? Просядет ли производительность из-за всех «дополнительных» фичей в Tarantool? Насколько хорошо или плохо справится дисковая подсистема с нагрузкой?
Мы взяли типичные кейсы работы с Redis и реализовали такие же механики на Tarantool, начиная от простых K-V операций и заканчивая вторичными ключами и производительностью кластерных решений: для Tarantool это Tarantool Cartridge, для Redis — Redis Cluster.
Выводы из нашего исследования можно прочесть в конце статьи.
Для тестирования использовали Grafana K6 — инструмент для нагрузочного тестирования, который позволяет создавать и запускать тестовые сценарии на JavaScript и анализировать результаты тестирования. Он имеет широкий набор функций для создания тестовых сценариев и может работать с различными протоколами, такими как HTTP, WebSocket, gRPC и т.д.
Grafana K6 можно использовать как в командной строке, так и в интерфейсе Grafana. Удобной особенностью K6 является возможность подключать сторонние расширения для работы с протоколами, которые изначально не поддерживаются К6. Так, из коробки К6 не умеет работать с Tarantool, поэтому мы использовали следующее расширение. Для подключения необходимо пересобрать исполняемый файл К6 с использованием исходного кода нужного дополнения.
За время тестирования мы рассмотрели 7 сценариев:
Сценарий 1: key-value c операциями set/get/delete
Redis
Tarantool
Сравнительные таблицы
Вывод
Сценарий 2: счетчик c операциями incr/decr
Redis
Tarantool
Сравнительные таблицы
Вывод
Сценарий 3: работа со множествами
Redis
Tarantool
Сравнительные таблицы
Вывод
Сценарий 4: работа с диском
Redis appendfsync: everysec
Redis appendfsync: Always
Redis appendfsync: No
Tarantool wal_mode Write
Tarantool wal_mode: None
Tarantool wal_mode: Fsync
Сравнительные таблицы
Вывод
Сценарий 5: вторичные индексы
Redis
Tarantool
Сравнительные таблицы
Вывод
Сценарий 6: влияние репликации на производительность
Redis master-slave
Tnt master-slave
Сравнительные таблицы
Вывод
Сценарий 7: кластер
Redis
Tarantool
Сравнительные таблицы
Вывод
Общие выводы из сравнительного нагрузочного тестирования
Сценарии 1-5 выполнялись на виртуальной машине со следующими характеристиками: Ubuntu, 4CPU, 16GB RAM, 30GB SSD.
Сценарий 6 выполнялся на двух виртуальных машинах Ubuntu, 4CPU 16GB RAM, 30GB SSD.
Все сценарии выполнялись с профилем нагрузки в 100 виртуальных пользователей, длительностью 120 секунд.
Сценарий 1. Простой Key-Value
Берем 3 операции:
установка значения по ключу
чтение
удаление
Создаём 100 пользователей, которые начинают слать запросы. Ключом является id пользователя + номер его запроса, значение сохраняем равное ключу.
Redis:
Дефолтная конфигурация
Tarantool:
Дефолтная конфигурация
Спейс с полями (key string, value string)
Сценарии set_keys, get_keys, del_keys запускались последовательно.
Результаты выполнения сценариевСреднее RPS
Set_keys | 25583 |
Get_keys | 31967 |
Del_keys | 31355 |
Время выполнения запросов
Действие | avg | min | med | max | p(90) | p(95) |
Set_keys | 3.78ms | 75.47µs | 2.19ms | 132.3ms | 8.36ms | 12.36ms |
Get_keys | 2.96ms | 52.6µs | 1.76ms | 132.48ms | 6.36ms | 9.37ms |
Del_keys | 3.09ms | 55.94µs | 1.83ms | 115.23ms | 6.62ms | 9.95ms |
Для проведения сценария создадим таблицу в Тарантуле вида ключ-значение, где ключ – это число, а значение – строка. Для поиска по ключу нам понадобится первичный индекс:
Первичный индексbox.cfg{listen=»127.0.0.1:3301″} box.schema.space.create(«test») box.space.test:format({{name=»name», type=»unsigned»}, {name=»value», type=»string»}}) box.space.test:create_index(«primary», {parts={«name»}})
Код сценариев k6import tarantool from «k6/x/tarantool»; import exec from ‘k6/execution’; const conn = tarantool.connect(“hostort”); export const options = { discardResponseBodies: true, scenarios: { set: { executor: ‘constant-vus’, exec: ‘set’, vus: 100, duration: ‘120s’, }, get: { executor: ‘constant-vus’, exec: ‘get’, vus: 100, duration: ‘120s’, }, del: { executor: ‘constant-vus’, exec: ‘del’, vus: 100, duration: ‘120s’, }, }, }; export function set() { tarantool.replace(conn, «test», [exec.vu.iterationInInstance + exec.vu.idInInstance * 100, (exec.vu.iterationInInstance + exec.vu.idInInstance * 100).toString()]) }; export function get() { tarantool.call(conn, «box.space.test:select», [exec.vu.iterationInInstance + exec.vu.idInInstance * 100]) }; export function del() { tarantool.call(conn, «box.space.test:delete», [exec.vu.iterationInInstance + exec.vu.idInInstance * 100]) };
Сценарии set_keys, get_keys, del_keys запускались последовательно.
Результаты выполнения сценариевСреднее RPS
Set_keys | 26813 |
Get_keys | 33250 |
Del_keys | 30873 |
Время выполнения запросов
Действие | avg | min | med | max | p(90) | p(95) |
Set_keys | 4.14ms | 122.3µs | 3.21ms | 60.34ms | 8.31ms | 10.85ms |
Get_keys | 3.17ms | 89.51µs | 2.58ms | 71.09ms | 5.69ms | 7.85ms |
Del_keys | 3.46ms | 114.76µs | 2.74ms | 61.46ms | 6.38ms | 8.84ms |
Сравнительная таблица по RPS
Redis | Tarantool | |
Set_keys | 25583 | 26813 |
Get_keys | 31967 | 33250 |
Del_keys | 31355 | 30873 |
Сравнительная таблица по медиане и перцентилям времени выполнения запросов
Действие | Redis | Tarantool | ||||
med | p(90) | p(95) | med | p(90) | p(95) | |
Set_keys | 2.19ms | 8.36ms | 12.36ms | 2.94ms | 7.51ms | 9.93ms |
Get_keys | 1.76ms | 6.36ms | 9.37ms | 2.18ms | 4.72ms | 6.7ms |
Del_keys | 1.83ms | 6.62ms | 9.95ms | 2.32ms | 5.29ms | 7.35ms |
Вывод по сценарию 1
Tarantool чуть быстрее в операциях записи и чтения, а Redis — в удалении.
Сценарий 2. Счетчик
Создаем 100 пользователей, которые отправляют запросы на увеличение или уменьшение счетчика. Для каждого пользователя используется свой счетчик.
Используем INCR/DECR в Redis и аналогичные update операции в Tarantool.
Сценарии incr и decr выполняются параллельно.
Конфигурация
Redis:
Дефолтная конфигурация
Tarantool:
Дефолтная конфигурация.
Создаем спейс с полями (key string, value integer).
Среднее RPS
INCR/DECR | 38985 |
Время выполнения запросов
avg | min | med | max | p(90) | p(95) | |
INCR/DECR | 2.53ms | 46.41µs | 1.57ms | 78.78ms | 5.27ms | 7.92ms |
Код сценариев к6import tarantool from «k6/x/tarantool»; import exec from ‘k6/execution’; const conn = tarantool.connect(«hostort»); export const options = { discardResponseBodies: true, scenarios: { incr: { executor: ‘constant-vus’, exec: ‘decr’, vus: 50, duration: ‘120s’, }, decr: { executor: ‘constant-vus’, exec: ‘decr’, vus: 50, duration: ‘120s’, }, }, }; export function setup() { for (let i = 1; i < 51; i++) { tarantool.replace(conn, «test», [i.toString(), 0]); } }; export function incr() { tarantool.call(conn, «box.space.test:update», [exec.vu.idInInstance.toString(), [[«+», 2, 1]]]) } export function decr() { tarantool.call(conn, «box.space.test:update», [exec.vu.idInInstance.toString(), [[«-«, 2, 1]]]) }
Средний RPS
INCR/DECR | 32552 |
Время выполнения запросов
avg | min | med | max | p(90) | p(95) | |
INCR/DECR | 3.06ms | 100.01µs | 2.51ms | 72.29ms | 5.26ms | 7.05ms |
Сравнительная таблица по RPS
Redis | Tarantool | |
INCR/DECR | 38985 | 32552 |
Сравнительная таблица по медиане и перцентилям времени выполнения запросов
Redis | Tarantool | |||||
med | p(90) | p(95) | med | p(90) | p(95) | |
INCR/DECR | 1.57ms | 5.27ms | 7.92ms | 2.51ms | 5.26ms | 7.05ms |
Вывод по сценарию 2
По результатам теста Tarantool незначительно уступил Redis.
Сценарий 3. Работа с множествами
Добавляем и получаем значения из множества, используя команды SADD/SMEMBERS в Redis и реализовываем аналогичные возможности в Tarantool.
Конфигурация
Redis:
Дефолтная конфигурация
Tarantool:
Дефолтная конфигурация.
Создаем спейс kv с полями (key string, element string)
Время выполнения запросов
avg | min | med | max | p(90) | p(95) | |
Set_keys | 4.2ms | 83.29µs | 2.44ms | 142.34ms | 9.5ms | 13.87ms |
Get_keys | 3.33ms | 63.02µs | 2.01ms | 121.31ms | 7.18ms | 10.63ms |
Среднее RPS
Set_keys | 22983 |
Get_keys | 29242 |
Tarantool
Скрипт инициализацииbox.cfg{listen=»127.0.0.1:3301″} box.schema.space.create(«test») box.schema.sequence.create(‘S’,{min=1, start=1}) box.space.test:format({{name=»id», type=»unsigned»},{name=»name», type=»string»}, {name=»value», type=»string»}}) box.space.test:create_index(«primary», {sequence=’S’, parts={«id»}}) box.space.test:create_index(«name», {unique=false, parts={«name»}})
Сценарий к6import tarantool from «k6/x/tarantool»; import exec from ‘k6/execution’; const conn = tarantool.connect(“hostort”); export const options = { discardResponseBodies: true, scenarios: { add: { executor: ‘constant-vus’, exec: ‘add’, vus: 100, duration: ‘120s’, }, members: { executor: ‘constant-vus’, exec: ‘members’, vus: 100, duration: ‘120s’, }, }, }; export function add() { tarantool.insert(conn, «test», [null, (exec.vu.idInInstance*1000).toString(), (exec.vu.iterationInInstance + exec.vu.idInInstance * 100).toString()]) } export function members() { tarantool.call(conn, «box.space.test.index.name:select», [(exec.vu.idInInstance*1000).toString()]) }
Среднее RPS
Set_keys | 22355 |
Get_keys | 29766 |
Время выполнения запросов
avg | min | med | max | p(90) | p(95) | |
Set_keys | 4.45ms | 126.08µs | 3.47ms | 73.07ms | 8.93ms | 11.73ms |
Get_keys | 3.34ms | 104.19µs | 2.71ms | 73.04ms | 5.98ms | 8.33ms |
Сравнительная таблица по RPS
Redis | Tarantool | |
Set_keys | 22983 | 22355 |
Get_keys | 29242 | 29766 |
Сравнительная таблица по медиане и перцентилям iterations_duration
Redis | Tarantool | |||||
med | p(90) | p(95) | med | p(90) | p(95) | |
Set_keys | 2.44ms | 9.5ms | 13.87ms | 3.47ms | 8.93ms | 11.73ms |
Get_keys | 2.01ms | 7.18ms | 10.63ms | 2.71ms | 5.98ms | 8.33ms |
Вывод по сценарию 3
При работе со множествами Tarantool и Redis показали себя одинаково.
Сценарий 4. Работа с диском
Тестируем Сценарий 1, меняя конфигурацию БД. Цель теста — определить производительность работы с диском Redis vs Tarantool.
Конфигурация
Redis:
Для тестов меняем параметр appendfsync (no, everysecond, always)
Tarantool:
Для тестов меняем параметр wal_mode (none, write, fsync)
avg | min | med | max | p(90) | p(95) | |
Set_keys | 3.78ms | 75.47µs | 2.19ms | 132.3ms | 8.36ms | 12.36ms |
Get_keys | 2.96ms | 52.6µs | 1.76ms | 132.48ms | 6.36ms | 9.37ms |
Del_keys | 3.09ms | 55.94µs | 1.83ms | 115.23ms | 6.62ms | 9.95ms |
Среднее RPS
Set_keys | 25583 |
Get_keys | 31967 |
Del_keys | 31355 |
avg | min | med | max | p(90) | p(95) | |
Set_keys | 4.07ms | 79.09µs | 2.35ms | 135.39ms | 9.17ms | 13.28ms |
Get_keys | 3.22ms | 60.93µs | 1.93ms | 129.49ms | 7.01ms | 10.32ms |
Del_keys | 3.13ms | 57.96µs | 1.88ms | 113.63ms | 6.8ms | 10.02ms |
Среднее RPS
Set_keys | 23773 |
Get_keys | 29467 |
Del_keys | 30741 |
avg | min | med | max | p(90) | p(95) | |
Set_keys | 3.94ms | 76.25µs | 2.26ms | 155.91ms | 8.81ms | 12.95ms |
Get_keys | 3.14ms | 63.19µs | 1.74ms | 122.84ms | 6.81ms | 9.98ms |
Del_keys | 2.73ms | 54.07µs | 1.63ms | 110.17ms | 5.8ms | 8.46ms |
Среднее RPS
Set_keys | 24541 |
Get_keys | 31999 |
Del_keys | 35179 |
avg | min | med | max | p(90) | p(95) | |
Set_keys | 3.78ms | 117.33µs | 2.94ms | 53.07ms | 7.51ms | 9.93ms |
Get_keys | 2.7ms | 86.38µs | 2.18ms | 45.22ms | 4.72ms | 6.7ms |
Del_keys | 2.93ms | 108.13µs | 2.32ms | 70.4ms | 5.29ms | 7.35ms |
Среднее RPS
Set_keys | 26813 |
Get_keys | 33250 |
Del_keys | 30873 |
avg | min | med | max | p(90) | p(95) | |
Set_keys | 3.82ms | 76.2µs | 2.2ms | 41.77ms | 7.85ms | 10.36ms |
Get_keys | 3.28ms | 95.29µs | 2.66ms | 57.78ms | 5.9ms | 8.23ms |
Del_keys | 2.9ms | 87.79µs | 2.32ms | 56.83ms | 5.17ms | 7.25ms |
Среднее RPS
Set_keys | 26335 |
Get_keys | 33017 |
Del_keys | 34273 |
Set_keys | 25055 |
Get_keys | 31342 |
Del_keys | 28758 |
Время выполнения запросов
avg | min | med | max | p(90) | p(95) | |
Set_keys | 4.14ms | 122.3µs | 3.21ms | 60.34ms | 8.31ms | 10.85ms |
Get_keys | 3.17ms | 89.51µs | 2.58ms | 71.09ms | 5.69ms | 7.85ms |
Del_keys | 3.46ms | 114.76µs | 2.74ms | 61.46ms | 6.38ms | 8.84ms |
Сравнительная таблица по RPS
Redis | Tarantool | |
appendfsync: everysec | wal_mode Write | |
Set_keys | 25583 | 26813 |
Get_keys | 31967 | 33250 |
Del_keys | 31355 | 30873 |
[TD]
[TD]
appendfsync: no
[/TD][TD]
wal_mode: None
[/TD][TR]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD] [/TD]
[TR]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[/TR]
[TD]
Set_keys
[/TD][TD]
24541
[/TD][TD]
26335
[/TD][/TR]
[TR]
[TD]
Get_keys
[/TD][TD]
31999
[/TD][TD]
33017
[/TD][/TR]
[TR]
[TD]
Del_keys
[/TD][TD]
35179
[/TD][TD]
34273
[/TD][/TR]
[TR]
[TD] [/TD]
[TD]
[TD]
[/TR]appendfsync: Always
[/TD][TD]
wal_mode: Fsync
[/TD][TR]
[TD]
Set_keys
[/TD][TD]
23773
[/TD][TD]
25055
[/TD][/TR]
[TR]
[TD]
Get_keys
[/TD][TD]
29467
[/TD][TD]
31342
[/TD][/TR]
[TR]
[TD]
Del_keys
[/TD][TD]
30741
[/TD][TD]
28758
[/TD][/TR]
Сравнительная таблица по медиане и перцентилям iterations_duration
Redis | Tarantool | |||||
appendfsync: everysec | wal_mode: write | |||||
med | p(90) | p(95) | med | p(90) | p(95) | |
Set_keys | 2.19ms | 8.36ms | 12.36ms | 2.94ms | 7.51ms | 9.93ms |
Get_keys | 1.76ms | 6.36ms | 9.37ms | 2.18ms | 4.72ms | 6.7ms |
Del_keys | 1.83ms | 6.62ms | 9.95ms | 2.32ms | 5.29ms | 7.35ms |
[TD]
[TD]
appendfsync: no
[/TD][TD]
wal_mode: None
[/TD][TR]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD] [/TD]
[TR]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[/TR]
[TD]
Set_keys
[/TD][TD]
2.26ms
[/TD][TD]
8.81ms
[/TD][TD]
12.95ms
[/TD][TD]
3.28ms
[/TD][TD]
7.85ms
[/TD][TD]
10.36ms
[/TD][/TR]
[TR]
[TD]
Get_keys
[/TD][TD]
1.74ms
[/TD][TD]
6.81ms
[/TD][TD]
9.98ms
[/TD][TD]
2.66ms
[/TD][TD]
5.9ms
[/TD][TD]
8.23ms
[/TD][/TR]
[TR]
[TD]
Del_keys
[/TD][TD]
1.63ms
[/TD][TD]
5.8ms
[/TD][TD]
8.46ms
[/TD][TD]
2.32ms
[/TD][TD]
5.17ms
[/TD][TD]
7.25ms
[/TD][/TR]
[TR]
[TD] [/TD]
[TD]
[TD]
[/TR]appendfsync: always
[/TD][TD]
wal_mode: fsync
[/TD][TR]
[TD]
Set_keys
[/TD][TD]
2.35ms
[/TD][TD]
9.17ms
[/TD][TD]
13.28ms
[/TD][TD]
3.21ms
[/TD][TD]
8.31ms
[/TD][TD]
10.85ms
[/TD][/TR]
[TR]
[TD]
Get_keys
[/TD][TD]
1.93ms
[/TD][TD]
7.01ms
[/TD][TD]
10.32ms
[/TD][TD]
2.58ms
[/TD][TD]
5.69ms
[/TD][TD]
7.85ms
[/TD][/TR]
[TR]
[TD]
Del_keys
[/TD][TD]
1.88ms
[/TD][TD]
6.8ms
[/TD][TD]
10.02ms
[/TD][TD]
2.74ms
[/TD][TD]
6.38ms
[/TD][TD]
8.84ms
[/TD][/TR]
Вывод по сценарию
При логировании каждой записи (appendfsync always в redis и wal_mode fsync в tarantool) tarantool показал лучший результат.
Сценарий 5. Вторичные индексы
KV в tarantool (id, value) со вторичным индексом на value.
Для добавления вторичных индексов в Redis использовался модуль Redisearch, скомпилированный под Ubuntu и запущенный через loadmodule
Время выполнения запросов
avg | min | med | max | p(90) | p(95) | |
Set_keys | 3.43ms | 112.54µs | 2.68ms | 48.15ms | 6.63ms | 8.96ms |
Search | 2.73ms | 82.87µs | 2.21ms | 56.86ms | 4.78ms | 6.7ms |
Среднее RPS
Set_keys | 29031 |
Get_keys | 36382 |
avg | min | med | max | p(90) | p(95) | |
Set_keys | 3.66ms | 70.83µs | 2.26ms | 89.86ms | 8.27ms | 12.15ms |
Search | 2.94ms | 62.1µs | 1.86ms | 83.79ms | 6.32ms | 9.41ms |
Среднее RPS
Set_keys | 27122 |
Get_keys | 33801 |
Сравнительная таблица по RPS
Redis | Tarantool | |
Set_keys | 27122 | 29031 |
Search | 33801 | 36382 |
Сравнительная таблица по медиане и перцентилям iterations_duration
Redis | Tarantool | |||||
med | p(90) | p(95) | med | p(90) | p(95) | |
Set_keys | 2.68ms | 6.63ms | 8.96ms | 2.26ms | 8.27ms | 12.15ms |
Get_keys | 2.21ms | 4.78ms | 6.7ms | 1.86ms | 6.32ms | 9.41ms |
Вывод по сценарию 5
Tarantool производительнее на несколько тысяч rps при работе со вторичными индексами и не требует дополнительных модулей для их работы.
Сценарий 6. Влияние репликации на производительность
Тестируем cценарий 1, меняя конфигурацию репликации. Цель теста — определить влияние репликации на производительность БД.
Проводим 2 теста:
Сценарий 6.1 Redis с репликацией на 1 узел
Сценарий 6.2 Tarantool с master-slave репликацией на 1 узел
avg | min | med | max | p(90) | p(95) | |
Set_keys | 3.86ms | 72.13µs | 2.76ms | 78.53ms | 8.12ms | 11.68ms |
Get_keys | 3.08ms | 52.11µs | 1.81ms | 64.77ms | 6.65ms | 9.93ms |
Del_keys | 2.96ms | 50.08µs | 1.74ms | 72.78ms | 6.34ms | 9.39ms |
Среднее RPS
Set_keys | 25766 |
Get_keys | 30689 |
Del_keys | 32378 |
avg | min | med | max | p(90) | p(95) | |
Set_keys | 4.35ms | 129.16µs | 3.32ms | 72.58ms | 8.83ms | 11.61ms |
Get_keys | 3.27ms | 84.55µs | 2.66ms | 41ms | 5.95ms | 8.15ms |
Del_keys | 3.45ms | 99.55µs | 2.78ms | 89.36ms | 6.21ms | 8.53ms |
Среднее RPS
Set_keys | 22883 |
Get_keys | 30452 |
Del_keys | 28830 |
Сравнительная таблица по RPS
Redis | Tarantool | |
Репликация на 1 узел | Репликация на 1 узел (master-slave) | |
Set_keys | 25766 | 22883 |
Get_keys | 30689 | 30452 |
Del_keys | 32378 | 28830 |
Сравнительная таблица по медиане и перцентилям iterations_duration
Redis | Tarantool | |||||
Репликация на 1 узел | Репликация на 1 узел (master-slave) | |||||
med | p(90) | p(95) | med | p(90) | p(95) | |
Set_keys | 2.76ms | 8.12ms | 11.68ms | 3.32ms | 8.83ms | 11.61ms |
Get_keys | 1.81ms | 6.65ms | 9.93ms | 2.2ms | 4.67ms | 6.46ms |
Del_keys | 1.74ms | 6.34ms | 9.39ms | 2.78ms | 6.21ms | 8.53ms |
Вывод по сценарию 6
По результатам теста при репликации master-slave Redis с минимальным перевесом обошёл Tarantool в операциях чтения и удаления.
Сценарий 7. Кластер
В данном сценарии были развернуты кластеры Redis и Tarantool в следующей конфигурации: 3 шарда, в каждом шарде 1 мастер и 2 реплики.
Характеристики виртуальных машин: Ubuntu, 4 cpu, 8 gb RAM, 10 gb ssd. Запускались тесты из сценария 1.
avg | min | med | max | p(90) | p(95) | |
Set_keys | 3.17ms | 231.06µs | 2.31ms | 220.89ms | 6.5ms | 8.62ms |
Get_keys | 2.73ms | 223.4µs | 2.01ms | 124.6ms | 6.01ms | 7.05ms |
Del_keys | 2.61ms | 214.5µs | 1.91ms | 105.32ms | 5.06ms | 6.89ms |
Среднее RPS
Set_keys | 31390 |
Get_keys | 37245 |
Del_keys | 38158 |
Топология кластера
Реализована пользовательская роль, реализующая следующие функции:
Кодfunction del(id) local result, err = crud.delete(‘test’, id) if err ~= nil then return err end return result end function add(id, value) local result, err = crud.insert(‘test’, {id, value}) if err ~= nil then return err end return result end function get(id) local result, err = crud.get(‘test’, id) if err ~= nil then return err end return result end
Для операций с данными использовался модуль crud.
Время выполнения запросов
avg | min | med | max | p(90) | p(95) | |
Set_keys | 5.13ms | 636.41µs | 4.35ms | 75.47ms | 8.34ms | 11ms |
Get_keys | 4ms | 399.47µs | 3.4ms | 39.65ms | 6.16ms | 8.69ms |
Del_keys | 4.22ms | 548.83µs | 3.65ms | 73.67ms | 6.5ms | 8.73ms |
Среднее RPS
Set_keys | 19436 |
Get_keys | 24888 |
Del_keys | 23627 |
Сравнительная таблица по RPS
Redis | Tarantool | |
Set_keys | 31390 | 19436 |
Get_keys | 37245 | 24888 |
Del_keys | 38158 | 23627 |
Сравнительная таблица по медиане и перцентилям iterations_duration
[TD]
[TD]
Redis
[/TD][TD]
Tarantool
[/TD][TR]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[/TR]
[TR]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[TD]
[/TR]
[TD]
med
[/TD][TD]
p(90)
[/TD][TD]
p(95)
[/TD][TD]
med
[/TD][TD]
p(90)
[/TD][TD]
p(95)
[/TD][/TR]
[TR]
[TD]
Set_keys
[/TD][TD]
2.31ms
[/TD][TD]
6.5ms
[/TD][TD]
8.62ms
[/TD][TD]
4.35ms
[/TD][TD]
8.34ms
[/TD][TD]
11ms
[/TD][/TR]
[TR]
[TD]
Get_keys
[/TD][TD]
2.01ms
[/TD][TD]
6.01ms
[/TD][TD]
7.05ms
[/TD][TD]
3.4ms
[/TD][TD]
6.16ms
[/TD][TD]
8.69ms
[/TD][/TR]
[TR]
[TD]
Del_keys
[/TD][TD]
1.91ms
[/TD][TD]
5.06ms
[/TD][TD]
6.89ms
[/TD][TD]
3.65ms
[/TD][TD]
6.5ms
[/TD][TD]
8.73ms
[/TD][/TR]
Вывод по сценарию 7
При запуске в режиме кластера Redis выигрывает.
Общие выводы из сравнительного нагрузочного тестирования
Redis
Имеет важные преимущества. Он популярнее и как следствие, имеет большее комьюнити. В интернете представлено больше информации по вопросам использования Redis, следовательно, у технологии более низкий порог входа.
В основном Redis используется для реализации кеширования, и он хорошо подходит для этой роли: например, в первом сценарии он лишь незначительно уступил Tarantool.
Tarantool
Если требуется полноценное решение для кеширования или хранения данных и взаимодействия с ними, которое можно использовать в качестве основной БД, рекомендуем присмотреться к Tarantool. Он ближе к привычной табличной организации данных, потому что поддерживает реляционную модель и запросы на SQL. При этом в большинстве случаев он способен выдерживать нагрузку, сравнимую с K-V БД вроде Redis.
Tarantool показал себя лучше под нагрузкой в сценариях 1, 4, 5: у него меньше время ответа, он держит большее количество RPS, в нем из коробки реализованы вторичные индексы. В сценарии 3 Tarantool и Redis проявили себя одинаково.
В результате Tarantool:
Немного быстрее работает в режиме key-value и при использовании вторичных индексов
Быстрее работает в режиме полной персистентности
Рассмотрим эти преимущества подробнее.
1. Tarantool немного быстрее работает в режиме key-value и при использовании вторичных индексов
Вторичные индексы часто используются при проектировании структуры базы данных. В случае Tarantool это позволяет производить поиск не только по ключам, но и по значениям.
Например: у нас имеется таблица вида «табельный номер — ФИО сотрудника», где табельный номер является ключом.
В Tarantool при создании вторичного индекса появляется возможность производить поиск не только по табельному номеру, но и по ФИО, что даёт возможность узнать табельный номер конкретного сотрудника
В Redis тоже есть такая возможность, но для этого необходимо устанавливать модуль RediSearch. При этом производительность решения будет уступать Tarantool
2. Tarantool быстрее работает в режиме полной персистентности в in-memory базах
Tarantool показывает большую производительность при сбросе данных на диск в режимах wal_mode=write и wal_mode=fsync — в сравнении с Redis с параметром appendfsync=everysec и appendfsync=always соответственно.
Почему это важно: эти режимы нужны для возможности восстановления данных в случае сбоя. Все операции сохраняются в специальный файл, и в случае неисправности их можно воспроизвести и не потерять данные.
Разница между режимами выше — в частоте записи в файл. В режиме полной персистентности, когда операции записываются в файл сразу после их выполнения с wal_mode=fsync, Tarantool выигрывает у Redis. Это дает возможность выдерживать большую нагрузку при максимальной сохранности данных.
Совсем кратко
Tarantool во многих сценариях показал лучшее или такое же быстродействие по сравнению с Redis. Tarantool можно использовать как в качестве основного хранилища данных, так и для реализации кеширования.
Исходный код тестов для К6 можно посмотреть в репозитории.