
Redis — это высокопроизводительное хранилище «ключ-значение» в оперативной памяти, известное своей невероятной скоростью. Фактически, один сервер Redis может обрабатывать до 100 000 запросов в секунду (QPS). Такая скорость часто удивляет, особенно если учесть, что Redis в основном работает по однопоточной модели обработки запросов. Так почему же Redis работает так быстро, несмотря на однопоточный подход? Давайте рассмотрим ключевые факторы, влияющие на производительность Redis.
Понимание модели потоков Redis
Во-первых, важно уточнить, что Redis не является строго однопоточным. Хотя основной поток обработки запросов выполняется одним потоком, в Redis есть и другие рабочие потоки, выполняющие определённые задачи в фоновом режиме. Однако для большинства повседневных операций, таких как обработка клиентских запросов и управление структурами данных, Redis использует однопоточную модель. Однопоточная обработка является основой скорости и эффективности Redis. Давайте рассмотрим основные причины этого.
Почему Redis работает так быстро?
Работа в памяти
Основная причина высокой скорости Redis заключается в том, что он работает исключительно в памяти. В отличие от традиционных баз данных, которые хранят данные на диске, Redis хранит все свои данные в памяти. Доступ к памяти на несколько порядков быстрее, чем доступ к диску, что позволяет Redis практически мгновенно считывать и записывать данные.
Latency numbers
Кроме того, Redis использует простую модель данных «ключ-значение». Внутри он использует хеш-таблицы для управления данными, что обеспечивает сложность поиска ключей O(1). Это означает, что независимо от количества ключей доступ к данным в Redis осуществляется очень быстро.
Расширенные типы данных, оптимизированные для операций в памяти

Redis предлагает ряд типов данных, таких как строки, хэши, списки, множества и отсортированные множества, каждый из которых оптимизирован для конкретных случаев использования. Эти типы данных позволяют разработчикам использовать наиболее эффективные структуры для своих конкретных задач, обеспечивая максимально возможную скорость выполнения операций.
Например, множества и отсортированные множества можно использовать для таких операций, как ранжирование и индексирование, а хэши эффективно хранят объекты. Эти операции выполняются полностью в памяти и потребляют минимум ресурсов процессора, что повышает скорость Redis.
Мультиплексирование ввода-вывода с неблокирующим вводом-выводом и управлением клиентскими подключениями

Одним из ключевых аспектов однопоточной модели Redis является то, как она обрабатывает несколько клиентских подключений. Redis использует мультиплексирование ввода-вывода с неблокирующим вводом-выводом — метод, который позволяет одному потоку эффективно управлять несколькими операциями ввода-вывода.
Мультиплексирование ввода-вывода с использованием таких механизмов, как select, poll, epoll в Linux, kqueue в Mac OS и evport в Solaris, позволяет Redis одновременно прослушивать несколько сокетов. Эти механизмы имеют временную сложность O(1) и могут обрабатывать сотни тысяч файловых дескрипторов, что значительно повышает эффективность Redis. Если ни одна из этих функций не доступна в текущей среде, Redis будет использовать select в качестве альтернативы, но у неё более низкая временная сложность O(n), и она может одновременно обрабатывать только 1024 файловых дескриптора, что делает её менее подходящей.
В этой модели поток отслеживает эти сокеты, определяя, какие из них готовы к чтению или записи. Когда сокет становится активным, Redis обрабатывает запрос, работает с данными в памяти и записывает ответ обратно в сокет. Такой подход позволяет Redis обрабатывать несколько одновременных подключений с помощью одного потока без создания нового потока для каждого подключения.
Важнейшим фактором производительности Redis является управление подключениями в клиентской библиотеке. Клиентские библиотеки часто используют мультиплексирование для управления подключениями. При мультиплексировании несколько потоков приложения используют одно подключение к Redis. Этот метод снижает затраты на создание и уничтожение подключений, как в моделях с пулом подключений.
Преимущества мультиплексирования: мультиплексирование позволяет клиенту обрабатывать большое количество потоков, не создавая для каждого из них новое соединение. Оно также обеспечивает неявное конвейерное выполнение команд, когда команды отправляются в Redis без ожидания отдельных ответов, что сокращает задержки.
Недостатки мультиплексирования: однако у мультиплексирования есть свои ограничения. Некоторые команды Redis, известные как операции, блокирующие клиента (например, BLPOP, BRPOP), могут задерживать весь трафик между клиентом и Redis при использовании в мультиплексированной системе. Кроме того, отправка или получение больших фрагментов данных может временно блокировать конвейер, замедляя обработку команд.
Задачи, не требующие больших затрат процессора

Redis предназначен для операций, не требующих больших вычислительных мощностей. Большинство команд Redis предполагают простые манипуляции с данными в памяти, что относительно легко с точки зрения использования процессора. Основные узкие места в Redis обычно связаны с памятью и пропускной способностью сети, а не с процессором.
Вот почему однопоточной модели обычно достаточно. Если требуется более высокая производительность, Redis рекомендует развернуть несколько экземпляров и сформировать кластер, а не использовать многопоточность в рамках одного экземпляра. Такой подход позволяет использовать многоядерные процессоры без ущерба для простоты и эффективности конструкции Redis.
Преимущества однопоточной модели
Однопоточный характер Redis имеет свои преимущества:
Отсутствие переключения контекста: поскольку всё работает в одном потоке, Redis позволяет избежать накладных расходов, связанных с переключением контекста между несколькими потоками, что может снизить производительность.
Отсутствие блокировок: при обработке команд только одним потоком нет необходимости в блокировках при доступе к общим ресурсам. Это устраняет потенциальную конкуренцию за блокировки, сокращая задержки и повышая пропускную способность.
Простота разработки и отладки: однопоточную модель проще разрабатывать, тестировать и поддерживать, что снижает вероятность ошибок, связанных с параллелизмом.
Эти преимущества соответствуют цели Redis — предоставлять простую, эффективную и высокопроизводительную базу данных.
Многопоточная оптимизация в Redis
Хотя Redis в основном обрабатывает запросы в одном потоке, он использует дополнительные потоки для выполнения определённых фоновых задач. Например:
Асинхронное освобождение памяти: начиная с Redis 4.0, был представлен механизм асинхронного освобождения памяти. При удалении больших ключей Redis позволяет освобождать память в фоновом потоке, не блокируя основной поток этой трудоёмкой операцией.
Анализ протокола: в Redis 6.0 появилась многопоточная обработка для анализа данных запроса по протоколу, особенно в сценариях с высокой параллельностью. Это снижает нагрузку на однопоточную обработку входящих запросов, повышая производительность. Однако фактическая обработка команд и манипулирование данными остаются однопоточными.
Эти оптимизации показывают, что Redis не придерживается жёсткой однопоточной модели. Вместо этого он выборочно использует многопоточность для разгрузки задач, которые в противном случае могли бы замедлить работу основного потока, повышая общую производительность.
Потенциальные недостатки однопоточной обработки
Хотя однопоточная модель обладает множеством преимуществ, у неё есть и недостатки:
Блокировка операций: если обработка одного запроса занимает много времени, весь сервер Redis может быть заблокирован, что приведет к задержке последующих запросов. Это может произойти, если команда включает большие массивы данных или сложные вычисления.

Узкие места в памяти и сети: производительность Redis ограничена доступной памятью и пропускной способностью сети. В сценариях с высокой нагрузкой на сервер память и задержка в сети могут стать узкими местами, особенно при недостатке памяти или сетевых ресурсов.
Проблемы с мультиплексированием: при настройке мультиплексированного соединения некоторые команды, блокирующие клиента, могут задерживать весь трафик между клиентом и сервером Redis, создавая узкое место. Кроме того, передача очень больших объемов данных может временно блокировать конвейер соединения, снижая производительность.
Чтобы устранить эти недостатки, важно избегать операций в Redis, которые занимают много времени, например, одновременного извлечения слишком большого объёма данных или использования команд с высокой временной сложностью.
Заключение
Redis обеспечивает высокую скорость работы благодаря:
Хранению данных в оперативной памяти
Широкому спектру типов данных
Использованию мультиплексирования ввода-вывода
Преимуществам однопоточной модели.
Несмотря на то, что для повышения производительности в определённых фоновых задачах используется многопоточность, основная обработка запросов остаётся однопоточной. Такой подход сводит к минимуму накладные расходы, связанные с многопоточностью и блокировкой, что делает Redis невероятно быстрой и эффективной базой данных.
В сценариях, когда производительности одного экземпляра Redis недостаточно, рекомендуется использовать несколько узлов Redis в кластере для эффективного задействования многоядерных процессоров. Redis демонстрирует, что простота в сочетании с продуманной архитектурой может обеспечить исключительную производительность.
Модель Redis — отличный пример целенаправленной оптимизации, показывающий, как сбалансировать однопоточные и многопоточные конструкции, а также эффективно управлять клиентскими подключениями для достижения максимальной производительности.
Redis, кеши - важные составляющие при проектирование и эволюции нагруженных систем.
Больше информации о System Design, а также о прохождение интервью, разборе Клеппмана, проведению архитектурных кат, получение cheet sheets на моём канале, посвященному Архитектуре, System Design, Highload бэкэнду.
Зайти - System Design World.
Удачи!