Эта статья представляет собой блог автора Redis antirez.
Оригинальный адрес:antirez.com/news/128
У нас есть новая структура данных в Redis 5 под названием «Потоки». (Сахуа) Как только Streams был запущен, он привлек внимание всех больших шишек в сообществе. Поэтому я решил через некоторое время провести опрос сообщества, обсудить сценарии его использования и записать результаты в блог (блог автора Redis). Сегодня я хочу поговорить о другом вопросе: я подозреваю, что многие пользователи думают, что вариант использования Streams такой же, как у Kafka. На самом деле, основой дизайна этой структуры данных также является производство и потребление сообщений, но вы должны думать, что Redis Streams просто лучше справляется с такими вещами. Потоки — отличная модель и «ментальная модель», которые могут помочь нам лучше проектировать системы, но потоки Redis, как и другие структуры данных Redis, являются более общими и могут использоваться для решения более разнообразных проблем. Поэтому в этом блоге мы сосредоточимся на характеристиках Redis Streams как структуры данных и полностью проигнорируем его блокирующие операции, группы потребителей и весь контент, связанный с сообщениями.
Потоки — это файлы CSV на стероидах.
Если вы хотите записать серию структурированных данных, и уверены, что база данных достаточно большая, вы можете сказать: мы открываем файл для приложений, и каждая запись представляет собой элемент данных CSV:
time=1553096724033,cpu_temp=23.4,load=2.3 time=1553096725029,cpu_temp=23.2,load=2.1
Это кажется таким простым, тогда люди делали это в течение многих лет и продолжают: если вы знаете, что вы делаете, то это станет фиксированным рисунком. Если то же самое происходит в памяти, произойдет тогда? Последовательная память написать сильнее, и будет автоматически удалена некоторые ограничения файла CSV:
- Сложно выполнить пакетный запрос
- Слишком много избыточной информации: каждая запись имеет почти одинаковое время и одинаковые поля. Но удаление полей снизит гибкость, и вы не сможете добавить другие поля.
- Смещение каждой записи — это ее байтовое смещение в файле, и если мы изменим структуру файла, то эти смещения станут недействительными. Таким образом, уникальный идентификатор здесь отсутствует.
- Записи не могут быть удалены, только помечены как недействительные. Если не переписывать лог, и нет сборки мусора, то переписывание лога часто будет вызывать ошибки по разным причинам, поэтому лучше не переписывать.
Однако использование таких записей CSV имеет некоторые преимущества: нет фиксированного формата, поля можно изменять, генерация проще, а формат хранения относительно компактен. Мы сохранили его преимущества и убрали ограничения, поэтому разработали гибридную структуру данных, такую как Redis Sorted Set — Redis Streams. Они выглядят как базовые структуры данных, но для того, чтобы получить такой эффект, внутри есть несколько представлений.
Потоки 101 (потоки — фундаментальная часть)
Редис потоков Radix дерево представляет собой инкрементное узловое узел соединителя соединения сжатия. (Хорошо, что трудно понять концепцию оригинальных слов, опубликованных: потоки Redis представлены как дельта-сжатые узлы макроса, которые связаны вместе с деревом Radix). Его роль - быстро найти случайного элемента, получить ряд значений, удалите старые значения для создания максимального размера потока. Для программистов наш интерфейс и очень похож на файл CSV:
> XADD mystream * cpu-temp 23.4 load 2.3
"1553097561402-0"
> XADD mystream * cpu-temp 23.2 load 2.1
"1553097568315-0"
Как видно из этого примера, команда XADD автоматически генерирует и возвращает идентификатор записи. Он монотонно увеличивается и состоит из двух частей: -, время указывается в миллисекундах, а количество представляет собой приращение количества записей, сгенерированных за одну миллисекунду.
Таким образом, первая концепция, абстрагированная от упомянутого выше файла «добавить в CSV», заключается в том, что если звездочка используется в качестве параметра идентификатора команды XADD, идентификатор записи получается с сервера. Этот идентификатор является не только уникальным идентификатором записи, но также связан со временем, когда запись присоединилась к потоку. Команда XRANGE может извлекать или извлекать отдельные элементы данных в пакетах.
> XRANGE mystream 1553097561402-0 1553097561402-0
1) 1) "1553097561402-0"
2) 1) "cpu-temp"
2) "23.4"
3) "load"
4) "2.3"
В этом примере, чтобы получить один элемент данных, я использовал тот же идентификатор, что и начальное и конечное значения. Однако я могу получить любой диапазон элементов данных и ограничить количество результатов параметром COUNT. Я также могу установить начальные и конечные параметры на метки времени, чтобы получить элементы данных за определенный период времени.
> XRANGE mystream 1553097560000 1553097570000
1) 1) "1553097561402-0"
2) 1) "cpu-temp"
2) "23.4"
3) "load"
4) "2.3"
2) 1) "1553097568315-0"
2) 1) "cpu-temp"
2) "23.2"
3) "load"
4) "2.1"
Из соображений экономии места мы не будем показывать больше Streams API. мы связаныДокументация, и заинтересованные студенты могут прочитать его. Пока нам нужно сосредоточиться только на основном использовании: XADD для добавления данных, XRANGE (или XREAD) для чтения данных. Давайте посмотрим, почему я говорю, что потоки — это мощная структура данных.
теннисист
На днях я моделировал приложение с другом, который недавно изучал Redis: приложение для отслеживания местных теннисных кортов, игроков и игр. Очевидно, что плеер — это маленькая модель, в Redis достаточно одного хэша, ключ может быть в виде player:
> XADD club:1234.matches * player-a 1 player-b 2 winner 1
"1553254144387-0"
С помощью этой простой операции мы можем получить следующую информацию:
- Уникальный идентификатор совпадения: ID в потоке
- Нет необходимости создавать объект, представляющий совпадение
- Разверните статус игры или проверьте, ведется ли игра в указанное время.
До того, как появились потоки, нам нужно было создать отсортированный набор, а счетом было время. Элементами отсортированного набора являются идентификаторы совпадений (хранящиеся в хэше). Это не только увеличивает рабочую нагрузку, но и создает больше потерь памяти, чем вы думаете.
Потоки теперь выглядят как режим добавления, дробное время, отсортированный набор небольших элементов хэша. Короче говоря, это революция в среде моделирования Rediscover.
использование памяти
Приведенный выше пример — это не просто проблема режима затвердевания: по сравнению со старым режимом Sorted Set+Hash, Streams сделал хорошую оптимизацию для экономии памяти, но найти ее не так-то просто.
Предположим, мы хотим записать 1 миллион игр,
Использование памяти Sorted Set + Hash составляет 220 МБ (242RSS).
Использование памяти потоком составляет 16,8 МБ (18.11RSS).
Это не просто разница на порядок (на самом деле разница в 13 раз), это значит, что наша старая модель была такой расточительной, а новая работает идеально. Потоки Redis также творят другую магию: узлы макросов могут содержать несколько элементов, и они кодируются с использованием данных, называемых пакетами списков. listpacks будет кодировать целое число в двоичной форме, даже если это семантическая строка. Кроме того, мы использовали дельта-сжатие и сжатие того же поля. Мы можем запрашивать по идентификатору или времени, потому что узлы макроса связаны с деревом счисления. Кардинальные листья спроектированы так, чтобы использовать очень мало памяти. Все использует очень мало памяти, но, что интересно, пользователь семантически не видит деталей реализации, которые делают потоки более эффективными.
Теперь давайте проведем простой расчет: если я сохраняю 1 миллион записей и использую 18 МБ памяти, то 10 миллионов — это 180 МБ, 100 миллионов — 1,8 ГБ, а 1 миллиард данных — только 18 ГБ памяти.
последовательно
Следует отметить еще одну важную вещь: на мой взгляд, приведенный выше пример, который мы использовали для записи теннисного матча, сильно отличается от использования Redis Streams в качестве временного ряда. Да, логически мы по-прежнему регистрируем класс событий, но существенная разница заключается в разнице между регистрацией и созданием записи и ее сохранением в объекте. При работе с временными рядами мы просто регистрируем внешнее событие, фактически не отображая объект. Вы можете подумать, что это различие неважно, но это не так. Для пользователей Redis важно отметить, что если вам нужно хранить упорядоченную серию объектов и присваивать идентификатор каждому объекту, вам необходимо использовать Redis Streams.
Тем не менее, Instant — это простой временной ряд и большой вариант использования, потому что до Streams Redis был немного безнадежен, когда дело дошло до этого варианта использования. Эффективный по памяти и гибкий поток — важный инструмент для разработчиков.
в заключении
Потоки очень гибкие и имеют много вариантов использования, я хочу, чтобы язык был как можно короче, чтобы убедиться, что приведенный выше пример и анализ памяти легче понять. Возможно, большинство читателей поняли это. Однако в моих беседах с другими за последний месяц я почувствовал, что потоки и потоковое вещание по-прежнему очень тесно связаны. Например, эта структура данных может использоваться только для обработки потоков, что не так.