Привет! Меня зовут Денис, я бэкенд-тимлид в 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-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)
? Redis
Код сценариев k6
import redis from 'k6/experimental/redis'; import exec from 'k6/execution'; export const options = { discardResponseBodies: true, scenarios: { test: { executor: 'constant-vus', exec: 'set_keys', vus: 100, duration: '120s', }, test: { executor: 'constant-vus', exec: 'get_keys', vus: 100, duration: '120s', }, test: { executor: 'constant-vus', exec: 'del_keys', vus: 100, duration: '120s', }, }, }; const client = new redis.Client({ addrs: new Array('host:port’), password: '', db: 0, }); export function set_keys() { client.set(exec.vu.iterationInInstance + exec.vu.idInInstance * 100, exec.vu.iterationInInstance + exec.vu.idInInstance * 100); } export function get_keys() { client.get(exec.vu.iterationInInstance + exec.vu.idInInstance * 100); } export function del_keys() { client.del(exec.vu.iterationInInstance + exec.vu.idInInstance * 100); }
Сценарии 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 |
? Tarantool
Для проведения сценария создадим таблицу в Тарантуле вида ключ-значение, где ключ – это число, а значение – строка. Для поиска по ключу нам понадобится первичный индекс:
Первичный индекс
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"}})
Код сценариев k6
import tarantool from "k6/x/tarantool"; import exec from 'k6/execution'; const conn = tarantool.connect(“host:port”); 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).
? Redis
Код сценариев к6
import redis from "k6/experimental/redis"; import exec from "k6/execution"; export const options = { discardResponseBodies: true, scenarios: { test_incr: { executor: "constant-vus", exec: "incr", vus: 50, duration: "120s", }, test_decr: { executor: "constant-vus", exec: "decr", vus: 50, duration: "120s", }, }, }; const client = new redis.Client({ addrs: new Array("host:port"), password: "", db: 0, }); export function incr() { client.incr("id"+exec.vu.idInInstance); } export function decr() { client.decr("id"+exec.vu.idInInstance); }
Среднее 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 |
? Tarantool
Скрипт инициализации tarantool
box.cfg{listen="127.0.0.1:3301"} box.schema.space.create("test") box.space.test:format({{name="name", type="string"}, {name="value", type="integer"}}) box.space.test:create_index("primary", {parts={"name"}})
Код сценариев к6
import tarantool from "k6/x/tarantool"; import exec from 'k6/execution'; const conn = tarantool.connect("host:port"); 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)
? Redis
Код сценариев к6
import redis from 'k6/experimental/redis'; import exec from 'k6/execution'; export const options = { discardResponseBodies: true, scenarios: { test_add: { executor: 'constant-vus', exec: 'add', vus: 100, duration: '120s', }, test_mem: { executor: 'constant-vus', exec: 'members', vus: 100, duration: '120s', }, }, }; const client = new redis.Client({ addrs: new Array('host:port'), password: '', db: 0, }); export function add() { client.sadd('test'+exec.vu.idInInstance*1000, (exec.vu.iterationInInstance + exec.vu.idInInstance * 100).toString()); } export function members() { client.smembers('test'+exec.vu.idInInstance*1000); }
Время выполнения запросов
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"}})
Сценарий к6
import tarantool from "k6/x/tarantool"; import exec from 'k6/execution'; const conn = tarantool.connect(“host:port”); 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)
? Redis appendfsync: everysec
Время выполнения запросов
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 |
? Redis appendfsync: Always
Время выполнения запросов
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 |
? Redis appendfsync: No
Время выполнения запросов
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 |
? Tarantool wal_mode Write
Время выполнения запросов
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 |
? Tarantool wal_mode: None
Время выполнения запросов
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 |
? Tarantool wal_mode: Fsync
Среднее RPS
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 |
|
appendfsync: no |
wal_mode: None |
Set_keys |
24541 |
26335 |
Get_keys |
31999 |
33017 |
Del_keys |
35179 |
34273 |
|
appendfsync: Always |
wal_mode: Fsync |
Set_keys |
23773 |
25055 |
Get_keys |
29467 |
31342 |
Del_keys |
30741 |
28758 |
Сравнительная таблица по медиане и перцентилям 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 |
|
appendfsync: no |
wal_mode: None |
||||
Set_keys |
2.26ms |
8.81ms |
12.95ms |
3.28ms |
7.85ms |
10.36ms |
Get_keys |
1.74ms |
6.81ms |
9.98ms |
2.66ms |
5.9ms |
8.23ms |
Del_keys |
1.63ms |
5.8ms |
8.46ms |
2.32ms |
5.17ms |
7.25ms |
|
appendfsync: always |
wal_mode: fsync |
||||
Set_keys |
2.35ms |
9.17ms |
13.28ms |
3.21ms |
8.31ms |
10.85ms |
Get_keys |
1.93ms |
7.01ms |
10.32ms |
2.58ms |
5.69ms |
7.85ms |
Del_keys |
1.88ms |
6.8ms |
10.02ms |
2.74ms |
6.38ms |
8.84ms |
Вывод по сценарию
При логировании каждой записи (appendfsync always в redis и wal_mode fsync в tarantool) tarantool показал лучший результат.
Сценарий 5. Вторичные индексы
KV в tarantool (id, value) со вторичным индексом на value.
Для добавления вторичных индексов в Redis использовался модуль Redisearch, скомпилированный под Ubuntu и запущенный через loadmodule
? Tarantool
Скрипт инициализации
box.cfg{listen="127.0.0.1:3301"} box.schema.space.create("test") box.space.test:format({{name="id", type="unsigned"}, {name="value", type="string"}}) box.space.test:create_index("primary", {parts={"id"}}) box.space.test:create_index("secondary", {parts={"value"}})
Время выполнения запросов
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 |
? Redis
Время выполнения запросов
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 узел
? Сценарий 6.1 Redis master-slave
Время выполнения запросов
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 |
? 6.2 Tnt-master-slave
Время выполнения запросов
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.
? Кластер Redis
Время выполнения запросов
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 |
? Кластер Tarantool
Топология кластера
Реализована пользовательская роль, реализующая следующие функции:
Код
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
|
Redis |
Tarantool |
||||
med |
p(90) |
p(95) |
med |
p(90) |
p(95) |
|
Set_keys |
2.31ms |
6.5ms |
8.62ms |
4.35ms |
8.34ms |
11ms |
Get_keys |
2.01ms |
6.01ms |
7.05ms |
3.4ms |
6.16ms |
8.69ms |
Del_keys |
1.91ms |
5.06ms |
6.89ms |
3.65ms |
6.5ms |
8.73ms |
Вывод по сценарию 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 можно посмотреть в репозитории.
ссылка на оригинал статьи https://habr.com/ru/articles/730518/
Добавить комментарий