Привет! Меня зовут Денис, я бэкенд-тимлид в 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

Скрытый текст
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"}})

Скрытый текст
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

Скрытый текст
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

Скрытый текст
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"}})

Скрытый текст
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

Скрытый текст
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"}})

Скрытый текст
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 можно посмотреть в репозитории.