Весь код в этой статье будет отображаться в виде псевдокода или изображений, пожалуйста, прочитайте его с уверенностью (C все еще немного трудно читать)
Эта статья основана на написании redis5.0
до начала
Одна вещь, чтобы сказать, прежде чем смотреть на исходный код Redis, мой уровень языка C может только писать дизайн курса студентов колледжа и чистить темы OJ. До того, как я начал читать код, я боялся, что бросу на полпути, но я не мог отказаться от цели, поставленной в начале года (одна уже провалилась, и я не мог достигать ее каждую неделю) , так что я упорствовал. К счастью, то, что я узнал, делится с вами здесь.
Я всегда был робким и застенчивым мальчиком, так что, пожалуйста, поставьте мне лайк.
Суть программирующего мышления
Если я спрошу вас, в чем суть мышления программирования?Какое мышление является экологическим построением богатого программного обеспечения сегодня? (Добро пожаловать, чтобы поделиться своими взглядами в области комментариев)
В моем случае даабстракция и инкапсуляциясостоит из этих двух идей. Например, будь то виртуальная машина JVM системы Java или движок V8 Node.js, по сути, это дальнейшая абстрактная инкапсуляция аппаратных ресурсов операционной системы и обеспечивает унифицированный интерфейс API, так что приложения Разработанный на движке можно запускать на разных платформах.
Вопрос в том, какое это имеет отношение к Redis? Разве Redis не разработан на языке C? Разве язык C не ориентирован на процессы? Как абстрагироваться и как инкапсулировать?
Иметь точку зрения — это нормально. У большинства людей такой же опыт, как и у меня. В лучшем случае они используют язык C для написания задач в нарды и алгоритмы кисти и редко касаются кода производственного уровня. Вспомните, что единственное, что имеет отношение к объектной ориентации, — это структуры, и да, это очень близко к ответу.
Зачем говорить об абстракции и инкапсуляции, как в саспенс-фильме никогда не бывает бесполезных персонажей (Amway, «Непредумышленное убийство»). Цель Redis - работать на различных типах операционных систем.В настоящее время большинство производителей операционных систем борются друг с другом.Что касается земли, то еще не было операционной системы, которая объединила бы мир.Возможно, какая-то друзья уже поняли, что я хочу спросить, то естьКак Redis везде компилирует набор кода«Хотя это и не тема данной статьи, но реализация объектно-ориентированных функций на языке C все же потрясла мой юный ум.Что же касается того, как этого добиться, то об этом будет сказано ниже, вернемся к главному.
Модель Redis/IO
Управляемое событиями и мультиплексирование ввода/вывода
Прежде чем начать, необходимо спросить себя, действительно ли вы понимаете механизм, управляемый событиями?
Я не знаю, есть ли у вас, читатели, опыт разработки приложений Windows на C++ в системе Windows.Программа win32 обычно принимает события, сгенерированные пользователем, в цикле while.Например, читатели, которые читают эту статью, независимо от того, находятся в оккупации Для создания события принято нажимать F12 или двигать колесико мыши.Вообще говоря, операционная система предоставит соответствующие функции API, чтобы мы могли программировать события, генерируемые поведением пользователя.
Псевдокод выглядит следующим образом, если вы хотите испытать конкретный код, вы можете нажатьyour-first-windows-program
while(true){
//获取事件
let msg = getMessage();
//翻译消息
translateMessage(msg);
//分发消息
dispatchMessage(msg);
}
Как показано в приведенном выше коде, можно обнаружить, что характеристики программы механизма, управляемого событиями, следующие:
- Обычно есть контейнер для хранения сгенерированных событий
- цикл событий
- Есть способ получить событие
- После получения события его необходимо обработать (его игнорирование тоже можно рассматривать как способ обработки)
После понимания механизма, управляемого событиями, давайте взглянем на мультиплексирование ввода-вывода, которое пользуется большим успехом!
Чтобы сравнить разницу, давайте сначала рассмотрим классическую программу структуры C/S. Код выглядит следующим образом:
while(true){
Socket client = server.accept();
ClientThread thread = new ClientThread(client);
thread.start();
}
Например, когда Сунь Укун сражается с монстрами, каждый раз, когда он сталкивается с монстром, он будет создавать клона, чтобы играть с монстром, а сам Сунь Укун отвечает за то, чтобы постоянно выщипывать волосы, чтобы создавать клонов и защищать Тан Сенга.
Итак, вопрос в том, если Сунь Укун программист, что должен делать Тан Сен?
В конце концов, Тан Сенг - главный герой, так как мы можем играть, если мы повесим трубку, чтобы мы могли попросить у великого бессмертного волшебное оружие из «Списка подавления монстров», Это волшебное оружие хранит всех маленьких монстров в пустоте. Получите интересующих вас монстров (какие монстры, вы говорите, интересуют обезьян?) и выстройтесь с ними в линию.
Выше приведена модель мультиплексирования ввода-вывода. Крупные разработчики операционных систем уже предоставили нам API-интерфейсы для получения интересующих нас событий, а в сочетании с моделью, управляемой событиями, получается мультиплексирование ввода-вывода.
Более строгое и академическое описание выглядит следующим образом:
Мультиплексирование ввода-вывода. В этой форме параллельного программированияПриложения явно планируют свой собственный логический поток в контексте процесса. Логический поток моделировал конечный автомат, в котором основная программа явно переходила из одного состояния в другое после того, как данные поступали в файловый дескриптор.Поскольку программа представляет собой единый процесс, все потоки совместно используют адресное пространство. ("ЦСАПП")
Другими словами, мы можем использовать диаграмму состояний для описания программы мультиплексирования ввода-вывода.
Еще раз вопрос: должно ли мультиплексирование ввода-вывода быть однопоточным?
Однозначный и утвердительный ответ: нет
Модель ввода/вывода
Нетрудно заметить, что redis использует шаблон проектирования Reactor, другими словами, он использует API, предоставленный нам операционной системой, так что нам не нужно создавать новые потоки для каждого клиента, то есть redis использует шаблон проектирования Reactor с одним потоком, но что, черт возьми, это за поток ввода-вывода?
Так называемый поток ввода-вывода — это поток, отвечающий за чтение данных от клиента и вывод данных ответа клиенту.
Для чего существуют потоки ввода-вывода и когда они запускаются?
Прежде всего, необходимо уяснить, что хотя Redis может получать данные путем опроса, функция, вызываемая при чтении данных клиента и выводе данных клиенту, все равно будет блокироваться (время блокировки, как правило, супер короткое, настолько короткое, что вы его не заметите ), но всегда есть но, предположим, вы работаете в очень бедной компании с одним сервером Redis (и большим количеством данных), и однажды временный работник запихнул в Redis учебный материал объемом 512 МБ.set studyresouces 学习资料
, Если вы продолжите использовать однопоточный режим, нетрудно представить, что весь сервис Redis будет временно заблокирован. Таким образом, если в это время у нас есть несколько потоков ввода-вывода, основной бизнес-поток может передать ввод и вывод потоку ввода-вывода для завершения.Что касается того, когда запускать поток ввода-вывода, давайте поговорим об этом ниже.
Поэтому более разумным определением является следующее
В Redis действительно есть только один поток, отвечающий за работу с данными, но не один поток отвечает за ввод-вывод, Кроме того, Redis также будет открывать потоки при выполнении операций сериализации.
Снова возникает вопрос, почему в redis только один поток отвечает за манипулирование данными?
-
Данные всех операций Redis находятся в памяти
-
Операции с данными Redis обычно могут выполняться за постоянное время.
-
Однопоточные операции с данными позволяют избежать проблем с безопасностью данных, вызванных многопоточными операциями (без блокировки).
основной процесс
Как видите, основной бизнес-поток Redis постоянно вызывается в цикле.beforeSleep
а такжеprocessEvents
метод.
aeProcessEvents
Первый взгляд наaeProcessEvents
, его код показан ниже.
Поскольку у Redis есть временные задачи для выполнения, если он входит в состояние долгосрочной блокировки (redis называет это спящим режимом) при опросе событий, это приведет к невозможности выполнения запланированных задач в течение длительного времени, поэтому необходимо рассчитать максимальное время ожидания.
aeApiPoll() заставит поток войти в состояние блокировки до тех пор, пока не будет сгенерировано событие ввода/вывода.Можно передать максимальное время блокировки.Если это время превышено, он немедленно вернется, даже если нет события ввода/вывода.
После опроса события I/O событие обрабатывается не сразу, а выполняется хук-функция afterSleep, а что делает afterSleep, поговорим ниже.
затем обработкаaeApiPoll
Опрашиваемые события.
Если вы читаете код, нетрудно увидеть, что там есть странная переменнаяinvert
, эта переменная связана с параметром конфигурацииAE_BARRIER
, который определяет порядок выполнения функций чтения и записи.
Обработчики событий чтения и записи, подключенные к клиентам Redis (например, redis-cli), будут указывать на
connection.c
серединаconnSocketEventHandler
.connSocketEventHandler
, эта функция определяет порядок вызова событий чтения и записи в зависимости от ситуации. (в эту функцию будет передан параметр инвертирования и тип опрашиваемого события)
наблюдаемая переменнаяfired
Приходим к следующему выводу, что redis не будет одновременно вызывать обработчики событий чтения и записи в цикле. и если
- AE_BARRIER = 1 (т.е.
invenrt = true
) Redis сначала будет обрабатывать события записи, а затем события чтения - AE_BARRIER = 0 (т.е.
invenrt = false
) redis сначала будет обрабатывать события чтения, а затем события записи
Проблема приходит сноваAE_BARRIER
Какая польза от этого параметра?
Чтобы понять эту проблему, мы должны сначала понять, что такое проблема.落盘
?
Предположим, вы сдаете вступительный экзамен в детский сад и столкнулись с проблемой вычисления.1+1=
, вы поняли, что ответ2
, но вы его не написали из-за нехватки времени, вам сняли 10 баллов и вы пропустили детский сад своей мечты.
Видно, одно дело, что вы это придумали, а другое дело, что вы не написали ответ в бланке ответов.
Аналогия с операционной системой, также будет такая ситуация, вы думаете, что позвонилиwrite
Функция сохраняет данные на жесткий диск.На самом деле данные остаются в памяти на некоторое время, ожидая подходящего времени для сохранения данных на жесткий диск.Предположим, что данные внезапно отключились во время пребывания в Память. Она ушла?
Чтобы избежать этого, операционная система (Linux
)при условииfsync
функция для обеспечения записи данных на жесткий диск, то есть для обеспечения того, чтобы данные落盘
, Когда эта функция вызывается, она блокируется до тех пор, пока данные не будут успешно записаны на жесткий диск.
Исходя из приведенных выше соображений, если Redis настроенappednfsync=always
, и AOF включен (AOF — это механизм сохранения данных Redis), и при выполнении определенных условийinvert=true
эффективный.
При каких условиях?
Прежде всего, уточним, что в общем случае выходные данные выводятся не в процессоре записи, а вbeforeSleep
Данные ответа выводятся клиенту. Давайте посмотрим на стек вызовов при выводе данных для проверки.
Исходный скриншот показан ниже
Кроме того, как правило, после получения соединения от клиента, Redis регистрирует только события чтения для этого соединения и регистрирует заинтересованность в событиях записи только при установленном обработчике записи.
Теперь, малыш, у тебя много вопросительных знаков? Проблема в том, что раз данные были выведены в beforeSleep, то зачем нужно менять порядок чтения и записи данных, сначала писать, а потом читать?
Исключите все возможности, и останется истина, даже если она неразумна.
Есть только одна возможность -> данные не выводятся. 😂
Обратите внимание на следующий код, расположенный по адресуnetworking.c
середина1373
линия
Нетрудно заметить, что при открытииappednfsync=always
И если у клиента еще есть данные для вывода, для этого клиента будет установлен обработчик записи, иinvert
установлен вtrue
. В этом случае происходят следующие события
- 1. Прочитайте команду от клиента и обработайте ее (aeProcessEvents)
- 2. Выполните операцию AOF (перед сном)
- 3. Выведите данные ответа клиенту и обнаружите, что данные еще остались и
appednfsync=always
, откройте AE_BARRIER (т.е. инвертировать=true) и установите процессор записи (beforeSleep) - 4. Вызвать процессор записи для вывода данных (aeProcessEvents)
- 5. Данные выведены и процессор записи удален (aeProcessEvents)
Вообще говоря, после того, как клиент Redis отправит инструкцию, он заблокируется и будет ждать ответа от сервера.В течение этого периода клиент не будет выдавать другие инструкции по работе с данными (ограниченные протоколом RESP2 и следующими протоколами, а клиент с помощью протокола RESP3 можно это сделать))
Удалите код, который записывает обработчик в
writeToClient
, Давайте поговорим об этом позже
Во избежание недоразумений необходимо указать следующее. упоминалось ранееprocessReadEvent
а такжеprocessWriteEvent
все указывают наconnSocketEventHandler
. Однако здесьconnSetWriteHandlerWithBarrier
установить обработчик записиsendReplyToClient
не будуprocessWriteEvent
направлениеsendReplyToClient
, но зарегистрируйтесьconnSocketEventHandler
Обработчик записи вызвал . Может быть, было бы немного более интуитивно смотреть на код.
Код находится в connection.c
Что ты делаешь перед сном?
в предыдущемaeMain
Как видно из кода, он будет вызываться каждый раз, когда вы входите в цикл обработки событий.beforeSleep
, давайте заставим Кан Канга Редиса делать то, что нужно делать перед сном.
В целом, beforeSleep выполняет следующую работу по порядку:
- Обработка принимает
安全传输层协议(TLS)
данные для обработки в клиенте - Если функция кластера включена, вызовите
clusterBeforeSleep
- Выполните быстрое сканирование базы данных, чтобы очистить ключи с истекшим сроком действия.
- Обработка задач, связанных с кластером
- Обработка заблокированных клиентов путем выполнения блокирующих команд (например, клиентов, выполняющих команды подписки)
- Выполнить операцию AOF
- Проверьте, нужно ли вам запускать поток ввода-вывода и выводить данные клиенту (
handleClientsWithPendingWritesUsingThreads
) - Асинхронно закрывать клиентов, которые должны быть закрыты
Многое делается в функции beforeSleep, но что касается модели ввода-вывода, нас интересует только поток данных, поэтому давайте сосредоточимся на обсужденииhandleClientsWithPendingWritesUsingThreads
упрощенныйhandleClientsWithPendingWritesUsingThreads
Код выглядит так
Нетрудно заметить, что основной поток назначает задачи потокам ввода-вывода в основном через очереди задач и битовые массивы флагов для назначения задач потокам, а также черезioThreadOp
Указывает тип текущей задачи для потока, т.е.IO_THREADS_OP_WRITE
выполнить задачу записи илиIO_THREADS_OP_READ
Выполните задание на чтение.
Итак, в чем заключается задача включения многопоточного ввода-вывода? могу взглянутьstopThreaedIOIfNeed
функция.
Видно, что если удовлетворить待处理的任务数量 >= I/O线程数 *2
, тогда Redis включит многопоточный ввод-вывод.
В противном случае он остановит поток ввода-вывода и позволит ему войти в состояние блокировки.
По приведенному выше коду не сложно придумать следующую структуру
Снова возникает вопрос: как основной поток управляет состоянием потока ввода-вывода? К этому нам нужно добавить немного знаний о многопоточности, давайте поговорим об этом дальше, давайте посмотрим, что делает Redis после пробуждения.
afterSleep Что делать после пробуждения?
После пробуждения Redis (изaeApiPoll
вернуться) сделать одно дело, позвонитьhandleClientsWithPendingReadsUsingThreads
Эта функция аналогична описанной выше.handleClientsWithPendingWritesUsingThreads
только похожиеioThreadOp
сталIO_THREADS_OP_READ
То есть поток ввода-вывода обрабатывает только события чтения.
processTimeEvents временные задачи
processTimeEvents
Просто цикл, пройдите по очереди задач по времени и, если она достигнет времени, выньте ее и выполните.Эти задачи, как правило, не слишком сложны, поэтому мы в основном фокусируемся на том, какие есть задачи по времени.
Регистрация задач на время может быть выполнена через
aeCreateTimeEvent
Регистрация запланированной задачи в цикле событий
После позиционирования вы обнаружите, что в конце зарегистрировано только одно задание на время.serverCron
(Эта функция находится в server.c)
Код для регистрации задач по времени в цикле событий выглядит следующим образом, намеренно увиденныйserverCron
срабатывает каждую 1 мс.
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
serverPanic("Can't create event loop timers.");
exit(1);
}
Кроме того, мы можем контролировать следующие два параметраserverCron
поведение.
-
server.hz
Управляет частотой выполнения функции serverCron, по умолчанию 10 (10 исполнений за 1 секунду), максимум 500 -
server.cronloops
Количество выполнений функции serverCron
Например, у Redis есть функция временной статистики использования базы данных, и ее цикл равен 5000, так как же Redis определяет, когда ее вызывать?
Обратите внимание на следующую функцию
function shouldRunWithPeriod(ms){
return ms <= 1000/server.hz || !(server.cronloops % (ms/(1000/server.hz)));
}
можно сделать вывод, когдаserver.cronloops = 50 * n
(n — целое число), т. е. когдаserver.cronloops
Когда оно кратно 50, будет выполняться статистическая функция, помните, мы только что сказалиserver.hz
Можно ли контролировать частоту выполнения serverCron, иserver.hz = 10
то есть каждые 100 мсserverCron
, нетрудно сделать следующие выводы иserver.cronloops = 50
Время50 * 100 == 5000
Отсортированные запланированные задачи показаны в таблице ниже. (server.hz = 10)
Частота выполнения относится к тому, сколько раз server.cron вызывается для выполнения этой задачи,
*
значит выполнять каждый раз
тип задачи | частота выполнения | Примечание |
---|---|---|
Статистика использования памяти | * | |
Обработка выходных сигналов из операционной системы | * | задачи, не рассчитанные на время |
Статистика использования всех словарей базы данных | 50*n(n=1,2,...) | Каждая база данных на самом деле является словарем |
Печать информации о клиенте и ведомом узле | 50*n(n=1,2,...) | Требуется Redis для работы в сторожевом режиме |
Обработка клиента тайм-аута соединения и клиентского буфера (clientCron) | * | задачи, не рассчитанные на время |
Инкрементальное расширение/сокращение словаря базы данных | * | задачи, не рассчитанные на время |
Обработка, связанная с AOF | * | задачи, не рассчитанные на время |
Включите, если последняя запись AOF не удаласьfsync Механизм записи в файл AOF |
10*n(n=1,2,...) | |
Выполнение запланированных задач для репликации master-slave | * | Необходимо включить режим репликации master-slave. |
Временная задача Стража | * | выходит за рамки этой статьи |
Остановите поток ввода-вывода, если слишком мало ожидающих выполнения задач ввода-вывода. | * | Несрочные задания, больше монахов и меньше каши, что скажете? |
Выполнить BGSAVE (сериализовать базу данных, отличную от AOF) | Его необходимо регулярно выполнять в соответствии со значением файла конфигурации. | |
server.cronloops++ |
Реализация потоков ввода/вывода
Из вышеизложенного я полагаю, что все читатели поняли, что основной поток Redis управляет поведением потоков ввода-вывода, назначая очередь задач, флаг состояния потока и разделяя тип задачи для каждого потока, а затем как Redis управляет потоком для войти в состояние блокировки, чтобы избежать простоя и потребления системных ресурсов?
Без лишних слов давайте взглянем на псевдокод.
Видно, что код потока ввода-вывода не сложен, но часть кода действительно запутана.
Например, мы видим, что поток будет выполняться1000000
Пустой цикл загрузки, просто чтобы определить, не установлен ли флаг потока0
.
Почему это разработано именно так?Студенты с опытом параллельного программирования могут легко увидеть, что такое поведение на самом деле自旋
, хотя спин будет потреблять определенное количество ресурсов (но не слишком много), если поток назначен задаче во время спина, ему не нужно входить в состояние блокировки, а затем восстанавливаться из состояния блокировки, и стоимость вращения меньше, чем поток, входящий в состояние блокировки Стоимость состояния для восстановления из состояния блокировки.
Продолжая читать код, мы можем обнаружить, что мьютекс захватывается и тут же освобождается.Почему?По сути, это возможность для основного потока заблокироваться, ведь основной поток заблокирует поток, добавив блокировку. Например
время | поток ввода/вывода | основной поток |
---|---|---|
t1 | Замок А | Рабочий статус |
t2 | Попытка заблокировать A и войти в состояние блокировки | |
t3 | открыть замок А | блокировать |
t4 | сделать следующий цикл | Замок А |
t5 | спиннинг | Рабочий статус |
t6 | Попытка заблокировать A и войти в состояние блокировки | Рабочий статус |
t7 | блокировать | Рабочий статус |
t8 | блокировать | Рабочий статус |
Что происходит, когда выводятся данные ответа
Прочитав вышеизложенное, нетрудно сделать следующие выводы.Redis может не выводить все данные ответа за один раз, а выбрать вывод части данных, а затем продолжить заниматься другими делами?Причина этого в следующем. что основной бизнес-поток Redis только один, поэтому вы не можете заставлять других клиентов ждать слишком долго, если на терминале выполняется временныйkeys *
, так что нам больше не нужно играть?
Чтобы быть более конкретным, давайте посмотримwriteToClient
Комментарии и код написаны автором для анализа, когда бывает, что все данные не выводятся сразу.
-
totwritten
Относится к количеству данных, которые были выведены в настоящее время,NEXT_MAX_WRITES_PER_EVNET
значение64KB
который64*1024
-
server.maxmemory
Относится к максимальному объему памяти, который может использовать Redis, значение по умолчанию равно0
То есть 64-битная система не ограничивает память, а 32-битная система использует до 3Гб памяти -
zmalloc_used_moemory
Текущая выделенная память может быть получена
Отсюда видно, что когдаserver.maxmemory=0
По умолчанию Redis будет выводить все ответы клиенту вовремя, чтобы не занимать память, если установленоserver.maxmemory
, а если условия выполняются, то для более чемNEXT_MAX_WRITES_PER_EVNET
Размер данных ответа не будет выводиться за один раз, а фактическое измерение будет приведено ниже.
Вкратце, постоянный принцип заключается в том, что когда память ограничена или не настроена максимальная память, redis выведет данные ответа клиенту как можно быстрее (данные ответа также должны занимать память), если памяти достаточно , Redis сначала выведет часть данных, а оставшиеся данные будут выведены в следующем цикле обработки событий.
Кроме того, после подтверждения того, что пользовательские данные выводятся,writeToClient
Также очистка вызывает процессор записи, который изначально был установлен на клиенте Redis.
Кроме того, Redis также разрабатывает два типа буферов для временного хранения данных ответа, как показано ниже.
- Буфер данных ответа answerBuffer, тип которого представляет собой массив байтов, используемый для временного хранения данных ответа.
- очередь данных ответа answerList, тип
clientReplyBlock
связанный список
Итак, каковы правила распределения, давайте сначала посмотримaddReply
Реализация функции
Глядя на приведенный выше код, можно сделать следующие выводы.
- Сначала будут предприняты попытки добавить данные ответа в буфер (размер буфера
16 *1024 = 16KB
), если буфер ответов заполнен, добавить его в очередь ответов - Данные ответа будут выполнены
beforSleep
время илиio线程
вывод в
абстракция цикла событий
AeEventLoop
это реализация цикла событий Redis,AeApi
для операционной системыI/O多路复用
Абстракция интерфейса API и предоставление различных реализаций под разные операционные системы.
-
aeMain
это основная функция цикла событий, которая будет вызываться после запуска сервера Redis - в состоянии пройти
aeCreateFileEvent
а такжеaeDeleteFileEvent
Добавляет или удаляет интересующие события ввода-вывода в этом цикле событий (вызовAeApi.aeApiAddEvent
иAeApi.aeApiDelEvent
) - в состоянии пройти
aeCreateTimeEvent
а такжеaeDeleteTimeEvent
Добавить или удалить запланированные задачи -
setBeforeSleepHook
Может быть установлен для входа в опрос события (т.е. вызовAeApi.aeApiPoll
) перед функцией (см. вышеbeforeSleep
) -
setAfterSleepHook
Можно установить функцию, которая будет вызываться после завершения опроса событий (см. выше).afterSleep
) -
setDontWait
При выполнении опроса событий он не переходит в состояние ожидания и немедленно возвращает текущее обрабатываемое событие и немедленно возвращается, если нет события для обработки.
Как показано на рисунке выше, для адаптации к различным экосистемам операционных систем Redis разработал унифицированный интерфейс API опроса событий.AeApi
И предоставляет различные реализации, API в основном предоставляет функции регистрации интересующих событий ввода-вывода, удаления интересующих событий ввода-вывода и событий опроса.
разныеAeApi
Разница между ними показана в таблице ниже.
название | низкоуровневая реализация | представление | Операционная система | описывать |
---|---|---|---|---|
AeEpoll | epoll | высокий | Linux | Количество отслеживаемых дескрипторов (количество клиентов) не ограничено, и эффективность IO не будет снижаться по мере роста количества отслеживаемых fds |
AeApiEvport | evports | Не знаю, не могу делать выводы | Solaris (система выпущена компанией sun, я ее никогда не видел 😅) | Реализация сложнее, но epoll прост в использовании. |
AeApiKqueue | kqueue | Не знаю, не могу делать выводы | FreeBSD, системы Unix | похоже на эпол |
AeSelect | select | худший | Различные операционные системы имеют реализации в качестве итогового решения. | Существует ограничение на количество файловых дескрипторов (количество клиентов), которые могут быть обработаны, максимум 1024. |
Не удовлетворены работой точки?
Просто смотреть на код всегда немного сухо. Давайте будем временным работником и попробуем производительность Redis в разных средах.
Рабочая среда выглядит следующим образом:
- centos7
- 1 CPU
- 2G RAM
-
server.maxmemory = 10485760
то есть 10M
Преследование временных работников
Предположим, что в относительно бедной компании временный работник Сяо Кэ случайно запустил онлайн-базу данных.keys *
операция, так что же происходит?
Прежде чем тест начнется, давайте нажмем две точки останова, которыеaddReply
а такжеwriteToClient
Начать выполнение redis-clikeys *
Заказ
наблюдатьaddReply
вызов
Видно, что из-за того, что данных слишком много, данные ответа не добавляются в буфер, а добавляются в очередь ответов, и команда полного сканирования таблицы выполняется много раз.addReply
вызова, как показано на следующем рисунке.
Вывод клиента тот же, но данные ответа разные
Глядя снова, мы находимwriteToClient
Действительно существует поведение получения данных ответа из очереди ответов
Тогда давайте наблюдатьwriteToClient
Ответ, стек вызовов показан на следующем рисунке
writeClient
Функция В основном мы проверяем, вступит ли в силу лимит выходных данных Redis.
заhandleClientsWithPendingWrites
В основном мы проверяем, будет ли установлен обработчик записи.
Видно, что поскольку данные не выводятся чисто, Redis устанавливает обработчик записи для нашего клиента, а затем мы выпускаем программу без каких-либо происшествий.Мы снова встретимся в writeToClient, и этот вызовwriteToClient
метод станетaeProcessEvents
то есть выводить данные в цикле событий, а не вbeforeSleep
, его стек вызовов показан на следующем рисунке.
вдохновленный
- u1s1, на js действительно здорово писать код, я не знаю, когда выйдет многопоточная версия JavaScript (в этой статье предполагается, что я использую многопоточную версию js)
- не выполнять
keys *
Операция полного сканирования таблицы, если вы не настроили потоки ввода-вывода или максимальное использование памяти - Параметры этой комплектации все с ней оснащены (хотя эксплуатация и обслуживание в основном будут комплектоваться, но все равно надо разбираться)
- Публикация статей или обучение других действительно хорошо помогает систематизировать идеи.
- Во время собеседования нужно только помнить, что Redis действительно не является однопоточным. Точнее, есть только один основной бизнес-поток Redis, но можно настроить несколько потоков ввода/вывода. Операции сериализации RDB откроют поток
- Зачем использовать js в качестве псевдокода? После долгих лет ныряния за золотом я обнаружил, что энтузиазм фронтенд-дядей относительно высок 😜
- Итак, поставьте мне лайк! ! ! В следующий раз, когда я обновлю опыт отладки Redis, я также буду использовать язык, понятный всем.Кстати, я сделаю обзор языка C. В конце концов, я наступил на много ям.
Наконец, диаграмма используется для описания областей памяти и функций, через которые проходит команда redis.