предисловие
Говорить о текущей основной базе данных NoSql не стоит.Redis
Никто. Из-за чрезвычайно высокой скорости чтения и записи он обычно используется для кэширования горячих данных для увеличения скорости запросов.Redis
справился, но дляRedis
Почему это быстро?» За исключением чтения Багувен, я, кажется, не понимаю этого глубоко.
Сегодня давайте более подробно рассмотрим Redis:
эффективные структуры данных
Существует 6 базовых структур данных Redis, а именно: простые динамические строки, двусвязные списки, сжатые списки, хеш-таблицы, таблицы переходов и целочисленные массивы, Соответствие между ними и типами данных показано на следующем рисунке:
Эта статья временно отсутствует в списке, и в будущем будет проведен углубленный анализ всех вышеперечисленных структур данных на уровне исходного кода.
однопоточный против многопоточного
Вы наверняка сталкивались с этим вопросом при изучении компьютерных операционных систем:Обязательно ли многопоточность быстрее, чем однопоточность?Я верю, что чиновники не попадут в ловушку Ао Бинга, как глупый Нэчжа выше.
Многопоточность иногда быстрее, чем однопоточность, но во многих случаях она не так быстра, как однопоточность. Во-первых, объясните разницу между параллелизмом и параллелизмом с помощью схемы, понятной трехлетнему ребенку:
- Параллелизм: это означает, что только одна инструкция может выполняться одновременно, но несколько инструкций процесса быстро выполняются по очереди, так что это имеет эффект одновременного выполнения нескольких процессов в макросе, но не выполняется в одно и то же время. то же самое время в микро Просто разделите время на несколько сегментов, чтобы несколько процессов могли выполняться попеременно быстро.
- Параллельный: Относится к одновременному выполнению нескольких инструкций на нескольких процессорах одновременно. Таким образом, независимо от микро или макро точки зрения, они выполняются вместе.
Нетрудно обнаружить, что одновременно одновременно выполняется только одна инструкция, но процесс (поток) быстро переключается в ЦП, а скорость чрезвычайно высока, создавая впечатление, что он «выполняется одновременно». ". Фактически одновременно выполняется только одна инструкция. Но на самом деле, если мы используем многопоточность в приложении,Ротация между потоками и переключение контекста занимает много времени.
Talk is cheap,Show me the code
Следующий код демонстрирует время последовательного и одновременного выполнения и накопления операций:
public class ConcurrencyTest {
private static final long count = 1000000000;
public static void main(String[] args) {
try {
concurrency();
} catch (InterruptedException e) {
e.printStackTrace();
}
serial();
}
private static void concurrency() throws InterruptedException {
long start = System.currentTimeMillis();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int a = 0;
for (long i = 0; i < count; i++)
{
a += 5;
}
}
});
thread.start();
int b = 0;
for (long i = 0; i < count; i++) {
b--;
}
thread.join();
long time = System.currentTimeMillis() - start;
System.out.println("concurrency : " + time + "ms,b=" + b);
}
private static void serial() {
long start = System.currentTimeMillis();
int a = 0;
for (long i = 0; i < count; i++)
{
a += 5;
}
int b = 0;
for (long i = 0; i < count; i++) {
b--;
}
long time = System.currentTimeMillis() - start;
System.out.println("serial : " + time + "ms,b=" + b);
}
}
Время выполнения показано в следующей таблице.Нетрудно обнаружить, что когда одновременное выполнение операции накопления не превышает одного миллиона раз, скорость будет медленнее, чем последовательное выполнение операции накопления.
Циклы | Время последовательного выполнения/мс | Параллельное выполнение требует времени | Насколько быстрее параллелизм, чем последовательный |
---|---|---|---|
100000000 | 130 | 77 | примерно 1 раз |
10 миллионов | 18 | 9 | примерно 1 раз |
1 миллион | 5 | 5 | Почти то же самое |
100 000 | 4 | 3 | Почти то же самое |
10000 | 0 | 1 | медленный |
Из-за накладных расходов на создание потока и переключение контекста параллельное выполнение будет медленнее, чем последовательное.
переключатель контекста
Несколько потоков могут выполняться на одноядерном или многоядерном ЦП.Одноядерный ЦП также поддерживает многопоточное выполнение кода.ЦП реализует этот механизм, назначая кванты времени ЦП (возможности) каждому потоку. Чтобы выполнять несколько потоков, ЦП должен постоянно переключать потоки выполнения, чтобы гарантировать, что все потоки имеют возможность выполняться в течение определенного периода времени.
В этот момент период времени выполнения, выделяемый ЦП каждому потоку, называется его временным интервалом. Квант процессорного времени обычно составляет десятки миллисекунд. ЦП выполняет задачи циклически с помощью алгоритма распределения временных интервалов, а текущая задача выполняет временной интервал и переключается на следующую задачу.
Однако состояние предыдущей задачи сохраняется перед переключением, так что состояние этой задачи можно перезагрузить при следующем переключении обратно на эту задачу. такПроцесс задачи от сохранения до перезагрузки — это переключение контекста.
По текущему состоянию многопоточности: В многопоточной среде, когда состояние потока преобразуется из «Выполняемый» в «Невыполняемый» (заблокировано, ожидание, ожидание по времени), контекстная информация соответствующего потока (включая содержимое регистров ЦП и программных счетчиков в определенный момент времени и т. д.) Сохраняется, чтобы, когда соответствующий поток снова переходит в состояние Runnable позже, он мог продолжить работу на основе предыдущего хода выполнения. И поток, входящий в состояние Runnable из нерабочего состояния, может потребовать восстановления ранее сохраненной контекстной информации. Этот процесс сохранения и восстановления контекста потока называется переключением контекста.
на основе памяти
Взяв в качестве примера MySQL, данные и индексы MySQL постоянно хранятся на диске, поэтому, когда мы выполняем команду запроса с использованием оператора SQL, если индекс целевой базы данных не был загружен в память, мы должны сначала индекс загружается в память, а затем дисковые блоки, соответствующие данным, загружаются в память через несколько адресов и дисковых операций ввода-вывода, и, наконец, данные считываются.
Если это механический жесткий диск, то сначала нужно найти местонахождение данных, то есть адрес диска, который необходимо прочитать. Взгляните на эту диаграмму:
Чтобы прочитать данные на жестком диске, первым делом нужно найти нужную дорожку.Дорожка представляет собой кольцо с центральной осью в качестве центра.Во-первых, нам нужно найти дорожку, которую необходимо выровнять, и переместить головку в соответствующий трек.Этот процесс называется поиском.
Затем нам нужно дождаться, пока диск повернется, и пусть головка укажет на начальную позицию данных, которые нам нужно прочитать.Время, затрачиваемое здесь, называется задержкой вращения.Обычно мы говорим о скорости жесткого диска, которая в основном влияет на время, проведенное здесь, и это Направление вращения одностороннее.Если вы пропустите начало данных, вы должны дождаться, пока пластина повернется на следующий оборот, чтобы начать чтение.
Наконец, магнитная головка начинает считывать и записывать данные на диск.Этот принцип фактически аналогичен принципу чтения оптического диска.Поскольку на дорожке есть слой магнитной среды, то при прохождении магнитной головкой определенной положение, магнитная головка воспринимает магнитное состояние в различных положениях.Магнитные сигналы преобразуются в электрические сигналы.
Можно видеть, что является ли это движением магнитной головки или вращением диска, на самом деле это механическое движение по своей сути, поэтому этот жесткий диск называется механическим жестким диском, а эффективность механического движения равна узкое место чтения и записи диска.
Это слишком далеко, давайте вернемся к Redis. Если вы храните данные в памяти, как Redis, и читаете и записываете непосредственно в базу данных, это, естественно, меньше, чем в базу данных на жестком диске. Шаг чтения данных с диска и этот шаг Именно узким местом компьютерной обработки ввода-вывода.
Чтение данных в памяти — это, по сути, передача электрических сигналов, которая намного быстрее, чем механическое движение.
Поэтому можно ответственно сказать, что Redis такой быстрый, конечно, во многом связан с его работой в памяти. Однако это далеко не вся причина.
Redis FAQ
Перед лицом однопоточного Redis у вас могут снова возникнуть вопросы: Ао Бинг, мой многоядерный процессор больше не может работать! Не волнуйтесь, Redis специально ответил на этот вопрос.
ЦП нередко становится узким местом производительности Redis, поскольку Redis обычно ограничен памятью или сетью. Например, использование конвейерного Redis в системе Linux может обслуживать даже 1 миллион запросов в секунду, поэтому, если ваше приложение в основном использует команды O(N) или O(log(N)), оно вряд ли потребует много ресурсов ЦП.
Однако, чтобы максимизировать загрузку ЦП, вы можете запустить несколько экземпляров Redis на одном узле и рассматривать их как разные службы Redis. В некоторых случаях одного узла может быть недостаточно, поэтому, если вы хотите использовать несколько ЦП, вы можете начать думать о некоторых более ранних методах сегментирования.
ты сможешьРазделение страницыУзнайте больше об использовании нескольких экземпляров Redis в .
Однако в Redis 4.0 мы начали делать Redis более многопоточным. В настоящее время это ограничено удалением объектов в фоновом режиме и блокировкой команд, реализованных через модуль Redis. В будущих выпусках мы планируем сделать Redis более многопоточным.
Примечание. Мы всегда говорили, что Redis является однопоточным, но есть только один поток для обработки наших сетевых запросов., когда работает формальный сервер Redis, должно быть более одного потока!
Например, когда Redis сохраняется, он разветвляет дочерний процесс для выполнения операции сохранения.
Четыре модели ввода/вывода
Когда происходит сетевой ввод-вывод (при условии, что он читается), в нем участвуют два системных объекта: один — процесс, вызывающий этот ввод-вывод, а другой — системное ядро.
Когда происходит операция чтения, она проходит две фазы:
① Дождитесь подготовки данных;
②Скопируйте данные из ядра в процесс.
Для решения проблем сетевого ввода-вывода предлагаются 4 модели сетевого ввода-вывода:
- Блокирующая модель ввода-вывода
- Неблокирующая модель ввода-вывода
- Мультиплексная модель ввода-вывода
- Модель асинхронного ввода-вывода
Понятия блокировки и неблокировки описывают способ, которым пользовательские потоки вызывают операции ввода-вывода ядра: блокировка означает, что операция ввода-вывода должна быть полностью завершена перед возвратом в пространство пользователя; неблокировка означает, что операция ввода-вывода возвращает пользователю значение состояния. сразу после вызова, не нужно ждать полного завершения операции ввода-вывода.
Блокирующая модель ввода-вывода
В Linux все сокеты по умолчанию заблокированы.Типичная операция чтения показана на следующем рисунке:
Когда процесс приложения вызывает системный вызов recvfrom, ядро системы начинает первую стадию ввода-вывода: подготовку данных.
Для сетевого ввода-вывода во многих случаях, когда данные не поступили в начале (например, не был получен полный TCP-пакет), ядру системы приходится ждать поступления достаточного количества данных. На стороне пользовательского процесса весь процесс будет заблокирован.
Когда системное ядро ждет, пока данные будут готовы, оно скопирует данные из системного ядра в пользовательскую память, после чего системное ядро вернет результат, а пользовательский процесс будет разблокирован и запущен снова. Таким образом, характеристика блокирующей модели ввода-вывода состоит в том, что обе фазы выполнения ввода-вывода (ожидание данных и копирование данных) блокируются.
Неблокирующая модель ввода-вывода
В Linux ввод-вывод можно сделать неблокирующим, установив сокет. Когда операция чтения выполняется на неблокирующем сокете, поток операции чтения показан на следующем рисунке:
Как видно из рисунка, когда пользовательский процесс выдает операцию чтения, если данные в ядре не готовы, он не будет блокировать пользовательский процесс, а сразу вернет ошибку.
С точки зрения пользовательского процесса, после того, как он инициирует операцию чтения, ему не нужно ждать, а он немедленно получает результат. Когда пользовательский процесс считает, что результатом является ошибка, он знает, что данные не готовы, поэтому он может снова отправить операцию чтения.
Как только данные в ядре готовы и снова получен системный вызов от пользовательского процесса, он немедленно копирует данные в пользовательскую память и возвращает правильное возвращаемое значение.
Следовательно, в неблокирующем вводе-выводе пользовательский процесс фактически должен активно запрашивать у ядра, готовы ли данные. Существенная разница между неблокирующим интерфейсом и блокирующим интерфейсом заключается в том, что он возвращается сразу после вызова.
Мультиплексная модель ввода-вывода
Мультиплексирование ввода-вывода, иногда называемое событийно-управляемым вводом-выводом (Шаблоны проектирования реактора). Его основной принцип заключается в том, что существует функция, которая будет непрерывно опрашивать все сокеты, за которые она отвечает. Когда на определенный сокет поступают данные, он уведомляет пользовательский процесс. Процесс мультиплексирования модели мультиплексирования ввода-вывода показан на рисунке:
Когда пользовательский процесс вызывает select, весь процесс будет заблокирован, и в то же время ядро будет «мониторить» все сокеты, за которые отвечает select.Когда данные в каком-либо сокете будут готовы, select вернется. В это время пользовательский процесс снова вызывает операцию чтения, чтобы скопировать данные из ядра в пользовательский процесс.
Эта модель не сильно отличается от модели блокирующего ввода-вывода, даже хуже. Потому что здесь нужно использовать два системных вызова (select и recvfrom), а для блокировки IO вызывается только один системный вызов (recvfrom). Однако преимущество использования select заключается в том, что он может обрабатывать несколько подключений одновременно. Следовательно, если количество подключений в системе не очень велико, веб-сервер, использующий select/epoll, не обязательно лучше, чем веб-сервер, использующий многопоточный блокирующий ввод-вывод, и задержка может быть больше;Преимущество select/epoll не в том, что он может быстрее обрабатывать одно соединение, а в том, что он может обрабатывать больше соединений.
Если select() обнаруживает, что «читаемое событие» захвачено дескриптором, серверная программа должна вовремя выполнить операцию recv(), подготовить данные для отправки в соответствии с полученными данными и добавить соответствующее значение дескриптора в writefds. чтобы подготовиться к следующему обнаружению select() для «событий, доступных для записи». Точно так же, если select() обнаруживает, что «событие, доступное для записи», захвачено дескриптором, программа должна вовремя выполнить операцию send() и подготовиться к следующему обнаружению «события, доступного для чтения».
На следующем рисунке показана рабочая модель, управляемая событиями.Когда генерируются разные события, обработчик будет обнаруживать и выполнять соответствующие события, как мультиплексор.
Мультиплексирование ввода-вывода — наиболее часто используемая модель ввода-вывода, но ее асинхронность не является «полной», поскольку она использует системный вызов select, который блокирует потоки. Следовательно, мультиплексирование ввода-вывода можно назвать только асинхронным блокирующим вводом-выводом, а не настоящим асинхронным вводом-выводом.
Модель асинхронного ввода-вывода
«Настоящий» асинхронный ввод-вывод требует более сильной поддержки со стороны операционной системы. Ниже показан запущенный процесс модели асинхронного ввода-вывода (Шаблон проектирования Proactor):
После того, как пользовательский процесс инициирует операцию чтения, он может немедленно приступить к другим действиям; с другой стороны, с точки зрения ядра, когда он получает асинхронную операцию запроса чтения, он сначала немедленно возвращается, поэтому это не повлияет на пользовательский процесс Производить любую блокировку.
Затем ядро дождется готовности данных, а затем скопирует данные в пользовательскую память.Когда все это будет сделано, ядро отправит сигнал пользовательскому процессу, возвращая информацию о том, что операция чтения завершена.
Сводная информация о модели ввода-вывода
Вызов блокирующего ввода-вывода заблокирует соответствующий процесс до завершения операции, в то время как неблокирующий ввод-вывод вернется немедленно, пока ядро все еще подготавливает данные.
Разница между ними заключается в том, что синхронный ввод-вывод блокирует процесс при выполнении операций ввода-вывода. В соответствии с этим определением ранее упомянутый блокирующий ввод-вывод, неблокирующий ввод-вывод и мультиплексирование ввода-вывода являются синхронным вводом-выводом. На самом деле настоящая операция ввода-вывода — это системный вызов recvfrom в примере.
Когда неблокирующий ввод-вывод выполняет системный вызов recvfrom, если данные ядра не готовы, процесс в это время не будет заблокирован. Но когда данные в ядре будут готовы, recvfrom скопирует данные из ядра в память пользователя, и процесс в это время заблокирован.
Асинхронный ввод-вывод отличается. Когда процесс инициирует операцию ввода-вывода, он возвращается непосредственно до тех пор, пока ядро не отправит сигнал, чтобы сообщить процессу, что ввод-вывод завершен.В течение всего процесса процесс вообще не блокируется.
Сравнение каждой модели ввода-вывода показано на следующем рисунке:
Приложения в Redis
Сервер Redis — это программа, управляемая событиями, и сервер должен обрабатывать следующие два типа событий:
- файл события: сервер Redis подключается к клиенту (или другому серверу Redis) через сокеты, а событие файла является абстракцией операции с сокетом сервером. Связь между сервером и клиентом (или другими серверами) будет генерировать соответствующие файловые события, и сервер выполнит ряд операций сетевого взаимодействия, отслеживая и обрабатывая эти события.
-
событие времени: некоторые операции на сервере Redis (например,
serverCron
) функция должна выполняться в заданный момент времени, а временные события являются абстракцией сервером таких операций синхронизации.
Мультиплексор ввода-вывода
Все функциональные возможности MEDIS 'I / O Multixer выполняются путем упаковки общегоselect
,epoll
,evport
,kqueue
Эти библиотеки функций мультиплексирования реализованы.
Поскольку Redis реализует один и тот же API для каждой библиотеки мультиплексирования ввода-вывода, базовые реализации программ мультиплексирования ввода-вывода взаимозаменяемы.
Redis используется в исходном коде реализации программы мультиплексирования ввода-вывода.#include
Макрос определяет соответствующие правила, и программа автоматически выберет библиотеку функций мультиплексирования ввода-вывода с наивысшей производительностью в системе в качестве базовой реализации программы мультиплексирования ввода-вывода Redis во время компиляции (ae.c
документ):
/* Include the best multiplexing layer supported by this system.
* The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
#endif
обработчик файловых событий
Redis разработал собственный обработчик сетевых событий на основе шаблона Reactor: этот обработчик называется обработчиком файловых событий:
- Обработчик файловых событий использует мультиплексор ввода-вывода для одновременного прослушивания нескольких сокетов.и связать различные обработчики событий для сокета в соответствии с задачами, выполняемыми сокетом в данный момент.
- Когда прослушивающий сокет готов выполнить ответ соединения (
accept
), читать(read
), написать (write
),закрытие(close
) и другие операции, будут сгенерированы файловые события, соответствующие операциям, и обработчик файловых событий вызовет ранее связанный обработчик событий сокета для обработки этих событий.
На следующей диаграмме показаны четыре компонента обработчика файловых событий:套接字
,I/O多路复用程序
,文件事件分派器(dispatcher)
,事件处理器
.
Файловое событие является абстракцией операций сокета.Событие файла генерируется всякий раз, когда сокет готов выполнить ответ соединения, запись, чтение, закрытие и т. д. операции. Поскольку к серверу обычно подключено несколько сокетов, одновременно могут происходить несколько файловых событий.Мультиплексор ввода-вывода отвечает за прослушивание нескольких сокетов и доставку тех сокетов, которые сгенерировали события, диспетчеру файловых событий.
Вопрос, заданный Нежей, очень хороший. Подумайте об этом. В жизни группа людей идет в столовую, чтобы поесть. Самая распространенная фраза, которую сказала моя тетя:Расстановка! Расстановка! Ни одного!
Да все идет от жизни! Программа мультиплексирования ввода-вывода Redis всегда помещает все сокеты, которые генерируют события, в очередь, а затем отправляет файловые события в файл упорядоченно, синхронно, по одному сокету за раз через эту очередь. розетка. Мультиплексор ввода-вывода не продолжит доставку следующего сокета диспетчеру файловых событий до тех пор, пока не будет обработано событие, сгенерированное предыдущим сокетом.
Redis написал несколько обработчиков для обработчиков файловых событий, которые используются для реализации различных требований к сетевому взаимодействию:
- Чтобы ответить каждому клиенту, подключающемуся к серверу, сервер связывает прослушивающий сокет для
连接应答处理器
; - Чтобы принять командный запрос от клиента, сервер должен связать клиентский сокет
命令请求处理器
; - Чтобы вернуть результат выполнения команды клиенту, серверу необходимо связать сокет для клиента
命令回复处理器
; - Когда главный сервер и подчиненный сервер выполняют операции репликации, и главный, и подчиненный серверы должны связать
复制处理器
.
обработчик ответа соединения
networking.c/acceptTcpHandler
Эта функция представляет собой обработчик ответа на подключение Redis. Этот процессор используется для ответа клиенту, подключающемуся к сокету прослушивания сервера. Конкретная реализация выглядит следующим образом:sys/socket.h/acccept
Обертка для функции.
Когда сервер Redis инициализируется, программа ответит на соединение с обработчиком и прослушивающим сокетом сервера.AE_READABLE
Время связано, когда клиент используетsys/socket.h/connect
Когда функция подключается к серверу для прослушивания сокета, сокет генерируетAE_READABLE
событие, которое заставляет обработчик ответа соединения выполнить и выполнить соответствующую операцию ответа сокета.
обработчик командных запросов
networking.c/readQueryFromClient
Функция представляет собой процессор командных запросов Redis. Этот процессор отвечает за чтение содержимого командного запроса, отправленного клиентом из сокета. Конкретная реализация выглядит следующим образом.unistd.h/read
Обертка для функции.
После того, как клиент успешно подключится к серверу через обработчик ответа на подключение, серверAE_READABLE
Событие связано с обработчиком запроса команды.Когда клиент отправляет запрос команды на сервер, сокет генерируетAE_READABLE
Событие, которое инициирует команду для запроса процессора на выполнение и выполнения соответствующей операции чтения сокета.
В течение всего процесса подключения клиента к серверу сервер всегда будет клиентским сокетом.AE_READABLE
Командный процессор запроса корреляции событий.
обработчик ответа на команду
networking.c/sendReplyToClient
Функция представляет собой обработчик ответа на команду Redis. Этот процессор отвечает за возврат ответа на команду, полученного после выполнения команды с сервера на клиент через сокет. Конкретная реализация выглядит следующим образом:unistd.h/write
Обертка для функции.
Когда у сервера есть ответ на команду, который необходимо отправить клиенту, сервер отправит клиентский сокетAE_WRITABLE
Событие связано с обработчиком ответа на команду, который генерируется, когда клиент готов получить ответ на команду от сервера.AE_WRITABLE
событие, которое вызывает выполнение обработчика ответа на команду и выполнение соответствующей операции записи сокета.
Когда ответ на команду отправляется, сервер освобождает обработчик ответа на команду из клиентского сокета.AE_WRITABLE
корреляция между событиями.
краткое содержание
В одном предложении описывается применение мультиплексирования ввода-вывода в Redis: Redis помещает все сокеты, генерирующие события, в очередь и передает их диспетчеру файловых событий упорядоченным, синхронным образом, по одному сокету за раз. диспетчер файловых событий выбирает соответствующий процессор для обработки в соответствии с событием, соответствующим сокету, тем самым реализуя эффективные сетевые запросы.
Пользовательский протокол для Redis
Клиенты Redis используют RESP (Протокол сериализации для Redis) протокол для связи с серверной частью Redis. Он прост в реализации, быстро анализируется и удобочитаем.
RESP поддерживает следующие типы данных: простые строки, ошибки, целые числа, объемные строки и массивы.
Способ использования RESP в качестве протокола запроса-ответа в Redis выглядит следующим образом:
- Клиент отправляет команды на сервер Redis в виде массива строк RESP.
- Сервер отвечает одним из типов RESP в соответствии с реализацией команды.
В RESP тип некоторых данных зависит от первого байта:
- заПростая строка, первый байт ответа "+"
- заОшибка, первый байт ответа равен "-"
- зацелое число, первый байт ответа ":"
- заМассовые строки, первый байт ответа - "$"
- замножество, первый байт ответа - "*"
Кроме того, RESP может представлять значения Null, используя специальные варианты объемных строк или массивов, указанные позже. В RESP разные части протокола всегда заканчиваются символом "\r\n" (CRLF).
Ниже только кратко представлен метод кодирования строки и неправильный метод кодирования.Подробности см. на официальном веб-сайте Redis для получения подробного описания RESP.
простая строка
Используйте следующий метод для кодирования: "+", за которым следует строка, и, наконец, "\r\n", строка не может содержать "\r\n". Простые строки используются для передачи относительно коротких двоично-безопасных строк. Например, многие команды redis возвращают «ОК» при успешном выполнении, а кодировка RESP составляет 5 байтов:
"+OK\r\n"
Для отправки двоично-безопасных строк используйте блочные строки RESP. Когда redis возвращает простую строку, клиентская библиотека должна вернуть вызывающей стороне строку после знака «+» (исключая) и до CRLF (исключая).
Ошибка RESP
RESP имеет тип, разработанный специально для ошибок. На самом деле тип ошибки очень похож на простой строковый тип RESP, но первый символ — «-». Разница между простым строковым типом и типом ошибки заключается в том, что клиент рассматривает тип ошибки как исключение, а строка, содержащаяся в типе ошибки, является информацией об исключении. Формат:
"-Error message\r\n"
Тип ошибки возвращается только при возникновении ошибки, например, вы выполнили операцию для определенного типа ошибки, или команда не существует и т. д. Клиентская библиотека должна вызывать исключение, когда возвращается тип ошибки. Ниже приведен пример типа ошибки
-ERR unknown command 'foobar' -WRONGTYPE Operation against a key holding the wrong kind of value
Строка перед пробелом или новой строкой после знака «-» представляет тип возвращаемой ошибки, который является просто соглашением, а не форматом, требуемым RESP. Например, ERR — это общая ошибка, а WRONGTYPE — более конкретная ошибка, указывающая на то, что клиент попытался выполнить операцию с неправильным типом. Это называется префиксом ошибки и облегчает клиентам определение типа ошибки.
Клиент может возвращать разные исключения для разных ошибок или может предоставлять только общий метод для перехвата ошибок и предоставления имен ошибок. Но вы не можете полагаться на эти функции, предоставляемые клиентом, потому что некоторые клиенты просто возвращают общие ошибки, например false.
Высокопроизводительный анализатор протокола Redis
Хотя протокол Redis очень удобочитаем и прост для определения, производительность реализации этого протокола может быть такой же быстрой, как и у двоичного протокола.
Поскольку протокол Redis добавляет длину данных к телу данных, программе не нужно сканировать всю полезную нагрузку на наличие специального символа, такого как JSON, и при этом ей не нужно заключать в кавычки полезную нагрузку, отправляемую на сервер.
Программа может искать символы CR при обработке каждого символа в тексте протокола и вычислять длину пакетного ответа или нескольких пакетных ответов, например:
#include <stdio.h>
int main(void) {
unsigned char *p = "$123\r\n";
int len = 0;
p++;
while(*p != '\r') {
len = (len*10)+(*p - '0');
p++;
}
/* Now p points at '\r', and the len is in bulk_len. */
printf("%d\n", len);
return 0;
}
После получения длины пакетного ответа или нескольких пакетных ответов программе нужно вызвать только один раз.read
Функция, вы можете прочитать все текстовые данные ответа в память без какой-либо обработки данных. CR и LF в конце ответа игнорируются и отбрасываются.
Производительность реализации протокола Redis сравнима с производительностью бинарного протокола, и из-за простоты протокола Redis большинство языков высокого уровня могут легко реализовать этот протокол, что значительно снижает количество ошибок в клиентском программном обеспечении.
Холодное знание: насколько быстр Redis?
После того, как Redis успешно установлен, Redis поставляется с командой redis-benchmark, которую можно использовать для тестирования производительности.Выполняя эту команду, мы можем имитировать сценарий, в котором N клиентов отправляют запросы одновременно, и отслеживать требования к Redis для обработки. эти запросы. время.
Согласно официальным документам, Redis прошел бенчмаркинг более чем на 60 000 подключений, и в этих условиях он все еще может поддерживать эффективность 50 000 запросов в секунду, и если такой же объем запросов попадет в MySQL, он точно не выдержит. просто рухнул. Также по этой причине Redis часто существует как кеш, который может защитить базу данных.
Видно, что Redis не хвастается пропускной способностью в 100 000. В дальнейшем можно сделать вид, что вы ненароком упомянули этот порядок величины при интервьюировании, и обнаружите, что многих людей не интересуют «уровень 100 000» и «миллионный уровень». "уровень" количества. Уровень часто используется без разбора, и возможность сказать это более точно также является плюсом.
Я Ао Бин,Чем больше вы знаете, тем больше вы не знаете, спасибо за ваши таланты:как,собиратьиКомментарий, увидимся в следующий раз!