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

Как мы сжимаем данные в больших проектах

Как мы сжимаем данные в больших проектах - 1

Привет! Меня зовут Александр Кленов, и я работаю в Tarantool. В апреле вышел Tarantool 2.10 Enterprise Edition – обновленная версия платформы in-memory вычислений. В версии 2.10 появилось несколько новых функций, о которых уже немного рассказывали на Хабре [1]

В этой статье я хочу подробнее остановиться на одной из фичей — сжатии данных в оперативной памяти. Далее я расскажу, как ей пользоваться, что может, а чего не может данный механизм, как его применять и какие существуют особенности.

Как включить сжатие

Все просто — нужно указать в настройках поля, что его надо сжимать: compression = '<вариант>'.

Доступно два варианта сжатия:

  • zstd,

  • lz4.

Например:

local space = box.schema.space.create(space_name, { if_not_exists = true })
space:format({
    {name = 'uid', type = 'string'},
    {name = 'body', type = 'string', compression = 'zstd'},
})

Если это не новый, а существующий проект с данными, понадобится также вызвать фоновую миграцию:

box.schema.func.create('noop', {
    is_deterministic = true, body = 'function(t) return t end'
})

space:upgrade{func = 'noop', format = {
    {name = 'uid', type = 'string'},
    {name = 'body', type = 'string', compression = 'zstd'},
}}

Подробнее смотрите в документации [2]

Каких результатов можно добиться за счет сжатия?

Итак, мы подключили фичу. Давайте посмотрим, что хорошего она может нам дать. В качестве примера возьмем реальный проект – данные крупного телеком-оператора. Это 100 тысяч  различных JSON-документов общим объемом 316 МБ.

По очереди мы применим разные варианты сжатия. CPU в нашем случае = 3,6 ГГц. Для лучшего сравнения методов добавим сжатие с помощью внешней библиотеки ZLIB.

Тестирование показало следующие результаты:

Метод

Время упаковки и записи в таблицу, сек

Время чтения и распаковки, сек

Конечный размер спейса, МБ

Коэффициент сжатия

Сжатие, %

ZSTD

61.222

0.889

113.234

2.790

64.16

LZ4

7.701

0.963

186.499

1.694

40.97

ZLIB*

16.119

3.528

121.958

2.590

61.40

* Внешняя библиотека ZLIB

Как мы сжимаем данные в больших проектах - 2
Как мы сжимаем данные в больших проектах - 3

Исходник теста можно посмотреть на Гитхабе [3].

Ключевой вывод – все три метода работают по-разному. ZSTD сжимает эффективно, но медленно и при этом достаточно быстро распаковывает документы. LZ4 – быстро сжимает и разжимает, но коэффициент сжатия уступает остальным методам. Сжатие с использованием ZLIB сопоставимо с результатами ZSTD и при этом не слишком медленно, однако проседает скорость разжатия.

Сжатие в кластере

В реальных продуктовых проектах экземпляры Tarantool разделены по ролям. Для нашего примера важно выделить две:

  • маршрутизатор (router),

  • хранилище (storage).

Сжатие данных в кластере – не такая линейная задача, как сжатие одного узла. Это можно организовать разными способами в зависимости от места сжатия и разжатия. У каждого способа есть свои плюсы и минусы. Рассмотрим три варианта.

Вариант первый — быстрый эффект

Как мы сжимаем данные в больших проектах - 4

Сжимаем и разжимаем в хранилище. Этот вариант обеспечивает описываемая в статье функциональность. Подходит, если нужно быстро и при ничтожных изменениях проекта высвободить кучу места.

Звучит здорово, но это вызывает дополнительную нагрузку на ЦП хранилища и его замедление. Более того, каждая реплика будет повторять то же самое сжатие. Как следствие — снижение производительности кластера. Хранилище со встроенным упаковщиком дороже масштабировать. То есть вместо одного дополнительного упаковщика придется поставить рядом хранилище с упаковщиком.

Посмотреть пример, как сделать, можно здесь:

Локальная переменная enable_zlib в роутере должна иметь значение = false. В хранилище в 11-й строке нужно указать тип сжатия.

Такой вариант подойдет – если срочно нужно освободить место. То есть как временное решение. Если нагрузка позволяет – это может быть постоянным решением. Это про быстро и дешево.

Если вы хотите сохранить производительность кластера, потребуется больше усилий.

Вариант второй — максимальная производительность

Как мы сжимаем данные в больших проектах - 5

Сжимаем и разжимаем на роутере. Это дает больше производительности, но встроенное сжатие никак не поможет — придется подключать внешнюю библиотеку и реализовывать дополнительную логику работы с ней. В мастер и на реплики тогда будут попадать уже упакованные данные.

В качестве примера можно использовать пару роутер [4]-хранилище [5], приведенную выше. Только переменная enable_zlib в роутере должна иметь значение = true. В хранилище тип сжатия не указываем.

Плюсы:

  • не нагружаем хранилище;

  • уменьшаем трафик между роутером и стораджем;

  • легче масштабируем — ставим нужное количество роутеров с упаковщиком до требуемой производительности.

Минусы:

  • нужно реализовать логику упаковщика на роутере;

  • появляется задержка на роутере на сжатие и распаковку.

Если хочется большего, то можно управлять сжатием в виде отдельной роли. Это позволит регулировать число роутеров отдельно от числа инстансов сжатия. Более гибко регулировать утилизацию ресурсов. В этом случае можно рассмотреть третий вариант.

Вариант третий — ленивое сжатие

Как мы сжимаем данные в больших проектах - 6

Сжимаем на отдельном экземпляре, разжимаем на роутере. То есть пишем в хранилище как есть с признаком «не упакован». Далее реализуем отдельную роль «упаковщик», которая проходит по мастер-хранилищам и упаковывает все не упакованное. Если данные поступают быстро и надо паковать быстрее — добавляем ещё экземпляры упаковщика. На реплики будут попадать уже упакованные данные.

Посмотреть пример реализации, чтобы понять идею, можно здесь [6].

Его можно нагрузить тем же нагрузочным тестом. Результаты он покажет как при записи без сжатия, потому что сжатие происходит не при записи, а в отдельном параллельном процессе.

Плюсы:

  • нет задержки на роутере на запись данных;

  • процесс сжатия масштабируется максимально выгодно;

  • меньше экземпляров кластера;

  • не нагружаем хранилище как в первом варианте.

Минусы:

  • дополнительный трафик в кластере — между хранилищем и упаковщиком;

  • некоторая дополнительная нагрузка на хранилище — плюс по одной операции чтения и записи;

  • нужно реализовать отдельную роль — упаковщик;

  • нужно реализовать логику распаковки на роутере.

Это может быть полезно:

  • если вы хотите быстро записывать данные - сразу записывать не сжимая;

  • регулировать скорость сжимания - либо уменьшить число инстансов, либо наоборот - увеличить, и через это более гибко расходовать свои ресурсы.

Эффект переноса сжатия на роутер

На фоне предложенных вариантов хочется проследить эффект от переноса сжатия с хранилища на роутер, как наиболее производительный. Давайте соберем маленький кластер на два экземпляра — роутер и хранилище. И попробуем нагрузить его при разных способах упаковки.

Реализация стенда:

Максимальная продолжительность испытаний выбрана 30 секунд. Потому, что с одной стороны не слишком долго ждать выполнения тестов, с другой – этого достаточно для тестирования нагрузки.  За это время мы генерируем 100 тысяч запросов на запись на роутер от лица 10 виртуальных пользователей. Консольный вывод результатов можно посмотреть здесь [9]. Ниже результаты сведены в одну таблицу.

Показатель Сценарий

Без сжатия

ZLIB

ZSTD

LZ4

Запросов выполнено, шт

100000

100000

46964

100000

Длительность выполнения запросов, сек

14.1

27.8

30.0

15.7

K6 data_received, MB

23

23

11

23

K6 data_sent, MB

341

342

160

341

http_req_blocked, µs

2.46

1.81

2.12

2.17

http_req_connecting, ns

13

7

20

16

http_req_duration, ms

0.716

2.27

5.78

0.934

http_req_failed, %

0

0

0

0

http_req_receiving, µs

31.1

25.59

25.8

28.71

http_req_sending, µs

20.08

16.78

19.42

19.22

http_req_waiting, ms

0.665

2.22

5.74

0.886

RPS

7076

3595

1565

6351

iteration_duration, ms

1.4

2.77

6.38

1.56

Итак мы видим, что по ключевым параметрам, таким как число запросов в секунду (RPS), задержке (http_req_duration) и общему времени выполнения запроса (iteration_duration) - вариант со сжатием на роутере выгодно отличается от сжатия в хранилище.

В первую очередь это происходит потому, что сжатие методом ZSTD происходит дольше, чем методом ZLIB. Также свой вклад вносит то, что данные от роутера до хранилища идут уже сжатые и таким образом в само хранилище нужно записывать меньше данных. Также плюсом идёт то, что не нужно нагружать хранилище сжатием.

Выводы

Tarantool развивается и обрастает новыми фичами. Встроенный в 2.10EE механизм сжатия полезен, но не является панацеей. Порядок действий зависит от конкретной ситуации — отсюда и возникают рекомендации по применению.

Если у нас нет желания или возможности использовать внешнюю библиотеку - мы можем использовать встроенное сжатие методом LZ4. Данный метод позволяет высвободить 40% места почти без просадки по RPS (6351 LZ4 против 7076 без сжатия). При этом включается фактически «одной галочкой» — на мой взгляд весьма неплохо.

Если требуется жать сильнее, или производительнее, или дешевле — нужны дополнительные усилия, которые описаны выше.

Скачать Tarantool можно на официальном сайте [10], а получить помощь — в Telegram-чате [11].

Автор:
1div0

Источник [12]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/klaster/376258

Ссылки в тексте:

[1] рассказывали на Хабре: https://habr.com/ru/company/vk/blog/663240/

[2] смотрите в документации: https://www.tarantool.io/en/enterprise_doc/tuple_compression/

[3] на Гитхабе: https://github.com/a1div0/habr-post-tnt-compress/blob/master/one_instance.lua

[4] роутер: https://github.com/a1div0/habr-post-tnt-compress/blob/master/router.lua

[5] хранилище: https://github.com/a1div0/habr-post-tnt-compress/blob/master/storage.lua

[6] здесь: https://github.com/a1div0/habr-post-tnt-compress/tree/master/variant-3

[7] Модуль для нагрузочного обстрела на запись модуль: https://github.com/a1div0/habr-post-tnt-compress/blob/master/k6-test-write.js

[8] вспомогательный модуль для генерации данных: https://github.com/a1div0/habr-post-tnt-compress/blob/master

[9] здесь: https://github.com/a1div0/habr-post-tnt-compress/blob/master/k6-results.md

[10] на официальном сайте: https://www.tarantool.io/ru/download?utm_source=habr&utm_medium=articles&utm_campaign=2021

[11] в Telegram-чате: https://t.me/tarantoolru?utm_source=habr&utm_medium=articles&utm_campaign=2021

[12] Источник: https://habr.com/ru/post/672760/?utm_source=habrahabr&utm_medium=rss&utm_campaign=672760