Освоение Redis с помощью вопросов на собеседовании [постоянно обновляется]

Redis

Эта статья была включена в учебное пособие с открытым исходным кодом с номером 1.1K Star — «Dachang Interview Guide North». следующие два QR-кода обращают внимание на общедоступный аккаунт «Dachang Interview», спасибо!

Лучший адрес для чтения «Dachang Interview Guide North»:

не найдено9.GitHub.IO/interviewGU…

Адрес проекта "Dachang Interview Guide North":

GitHub.com/не найдено9/я…

Чтобы получить автономную PDF-версию "Dachang Interview Guide North", отсканируйте приведенный ниже QR-код, чтобы подписаться на общедоступную учетную запись "Dachang Interview".

Скриншот проекта "Dachang Interview Guide North":

Обзор содержания сухих грузов:

[Интервью с Дачангом, выпуск 01] Как обеспечить согласованность кэша и базы данных в сценариях с высокой степенью параллелизма?

[Интервью Дачанга, выпуск 02] Как очистить ключи Redis с истекшим сроком действия?

[Интервью с Дачангом 03] Как MySQL решает проблему фантомного чтения?

[Интервью с Дачангом 04] Расскажите о том, как выполняется оператор обновления MySQL?

[Интервью с Дачангом, выпуск 05] Расскажите, как вы понимаете блокировку в MySQL?

[Интервью с Дачангом 06] Расскажите о своем понимании сохраняемости Redis?

[Интервью с Дачангом, выпуск 07] Расскажите, как вы понимаете синхронизированные блокировки?

[Интервью с Дачангом 08] Расскажите о своем понимании HashMap?

Резюме

Недавно я закончил читать «Проектирование и реализация Redis», а затем написал заметки для каждой главы. Но я всегда чувствовал, что недостаточно хорошо разбираюсь, поэтому собрал в Интернете несколько вопросов, связанных с Redis, а затем зашел в свои заметки, прочитал книги и поискал информацию, чтобы попытаться ответить. не полностью охватил все интервью, связанные с Redis, я буду продолжать обновлять его в будущем.С одной стороны, это мой собственный учебный отчет, а с другой стороны, я делюсь им с вами и общаюсь с вами.

Список вопросов на собеседовании:

1. Что такое Redis?

2. Как реализуется постоянство Redis?

3. В чем разница между AOF и RDB?

4. Как предотвратить увеличение размера файла AOF?

5. Как выбрать стратегию сохраняемости Redis?

6. Что такое блокировка добавления файла AOF?

7. Как очистить ключи с истекшим сроком действия в Redis?

8. Почему Redis является однопоточным?

9. Почему у Redis высокая производительность?

10. Какие существуют модели ввода-вывода в Linux?

11. В чем разница между синхронным и асинхронным?

12. В чем разница между блокировкой и неблокировкой?

13. Как решить проблему проникновения в кэш Redis?

14. Что такое фильтр Блума?

15. Как решить проблему лавины кеша Redis?

16. Каковы общие структуры данных Redis?

17. Расскажите о своем понимании простых динамических строк в Redis?

18. Расскажите о своем понимании хеш-объекта в Redis?

19. Расскажите о своем понимании List в Redis?

20. Расскажите о своем понимании Set в Redis?

21. Расскажите о своем понимании ZSet (упорядоченный набор) в Redis?

22. Как реализована синхронизация ведущий-ведомый в Redis?

23. Что такое часовой в Redis?

24. Как клиент подключается к системе Sentinel?

25. Как система Redis Sentinel обеспечивает автоматический переход на другой ресурс?

Ниже приведен ответ, который я попытался написать после прочтения книг, материалов и заметок.Если что-то не так, пожалуйста, поправьте меня, и все смогут учиться и прогрессировать вместе.

Что такое Редис?

Redis — это база данных с открытым исходным кодом, основанная на памяти и постоянном хранилище пар ключ-значение, написанная на языке C.

Как реализуется постоянство Redis?

Redis в основном реализует персистентность через AOF и RDB.

Постоянство AOF в основном означает, что Redis добавляет команду в конец буфера aof_buf после изменения соответствующей команды, а затем в конце каждого цикла обработки событий в соответствии с конфигурацией appendfsync (записывается всегда, каждую секунду записывается каждую секунду, нет, чтобы решить, когда записывать в соответствии с операционной системой), чтобы определить, нужно ли записывать aof_buf в файл AOF.

Постоянство RDB означает, что при выполнении определенного условия триггера (выполнение определенного количества команд модификации в течение временного интервала или ручное выполнение команд SAVE и BGSAVE) создается база данных для всей информации о паре ключ-значение на данный момент времени. Сожмите файл dump.rdb, затем удалите старый и замените его.

Принцип реализации состоит в том, чтобы разветвить дочерний процесс, а затем пройти пару ключ-значение для создания файла RDB.Во время процесса генерации родительский процесс будет продолжать обрабатывать запрос, отправленный клиентом.Когда родительский процесс хочет изменить данные, страница памяти будет скопирована, а скопированные данные изменены. (То есть COPY ON WRITE, технология копирования при записи, заключается в том, что когда несколько вызывающих объектов одновременно запрашивают один и тот же ресурс, например, память или хранилище данных на диске, они будут совместно использовать один и тот же указатель на ресурс, указывающий к тому же ресурсу, только когда Когда вызывающая сторона пытается изменить содержимое ресурса, система фактически копирует выделенную копию вызывающей стороне. Другие вызывающие стороны по-прежнему используют исходный ресурс. В реализации ArrayList это также полезно. При добавлении нового элемента процесс такой, заблокируйте, скопируйте исходный массив, затем добавьте новые элементы, затем замените старый массив, разблокируйте)

В чем разница между AOF и RDB?

Поскольку AOF сохраняет все выполненные команды модификации, степень детализации выше, а когда выполняется восстановление данных, восстановленные данные являются более полными, но поскольку все команды необходимо выполнить один раз, эффективность относительно низкая, а также потому, что все команды модификации сохраненный, то же самое. Для набора данных сохраненный файл будет больше, чем RDB, и по мере увеличения времени выполнения файл AOF может становиться все больше и больше, поэтому файл AOF будет регенерирован путем выполнения команды BGREWRITEAOF для уменьшения размер файла. После сбоя и перезапуска сервера Redis способ восстановления данных по умолчанию — восстановление данных через файлы AOF, а затем файлы RDB. RDB сохраняет всю информацию о паре ключ-значение в определенный момент времени, поэтому при восстановлении часть данных может быть потеряна, но эффективность восстановления будет выше

Как предотвратить увеличение размера файлов AOF?

Чтобы файл AOF не становился все больше и больше, вы можете выполнить команду BGREWRITEAOF, чтобы разветвить дочерний процесс, прочитать информацию о паре ключ-значение текущей базы данных, сгенерировать требуемую команду записи и записать новый файл AOF. Во время генерации родительский процесс продолжает нормально обрабатывать запрос, а после выполнения команды модификации в буфер aof_buf записывается не только команда, но и буфер rewrite aof_buf. Когда создается новый файл AOF, родительский процесс дочернего процесса отправляет сигнал, и родительский процесс записывает команду модификации для перезаписи буфера aof_buf в новый файл AOF.После завершения записи новый файл AOF переименовывается. , атомарно ( atomic ), чтобы заменить старый файл AOF.

Как выбрать стратегию сохранения Redis?

Характеристики сохраняемости RDB: небольшие файлы, быстрое восстановление, отсутствие влияния на производительность, низкая производительность в реальном времени и плохая совместимость (старая версия Redis несовместима с новой версией файлов RDB). Характеристики сохраняемости AOF: большие файлы, медленное восстановление, значительное влияние на производительность и высокая производительность в реальном времени. В настоящее время это основное направление сохранения (в основном потому, что текущая разработка проекта не может допустить потери большого количества данных). Следует понимать, что включение опции сохранения неизбежно приведет к определенному потреблению производительности. Постоянство RDB в основном связано с тем, что bgsave блокирует основной поток Redis при выполнении операции форка. И запись данных на жесткий диск будет иметь определенное давление ввода-вывода. Постоянство AOF в основном связано с давлением ввода-вывода при синхронизации данных в буфере aof_buf с диском, и частота записи данных на жесткий диск будет намного выше. Во-вторых, перезапись файла AOF аналогична сохранению RDB, и также будет блокировка во время разветвления и необходимость записи данных на жесткий диск.

Ниже приведены сценарии для нескольких вариантов сохраняемости:

1. Нет необходимости рассматривать ситуацию с потерей данных, значит, нет необходимости рассматривать сохраняемость.

2. В случае экземпляра с одним компьютером допустима потеря данных на десять минут или дольше.Вы можете выбрать сохраняемость RDB, что мало влияет на производительность.Если вы можете принять только потерю данных второго уровня, вы можете выберите только постоянство AOF.

3. В среде ведущий-ведомый после того, как главный сервер выполнит команду модификации, он отправит команду подчиненному серверу.После выполнения подчиненного сервиса он будет поддерживать синхронизацию данных с главным сервером, осуществлять горячее резервное копирование данных, и продолжать предоставлять данные после выхода из строя мастера. В то же время можно выполнить разделение чтения и записи для совместного использования запросов чтения Redis.

Итак, в случае горячего резервного копирования данных с сервера требуется ли постоянство? Постоянство требуется, потому что без постоянства, когда главный сервер и подчиненный сервер выходят из строя одновременно, происходит потеря данных. (Например: все машины в компьютерном зале выключены). Если в системе есть автоматический механизм подтягивания (т. е. перезапуск службы после обнаружения того, что служба остановлена), мастер будет автоматически перезапущен. перезапустится, и данные синхронизации подчиненного устройства также станут пустыми. Следует стараться избегать одновременного появления «механизма автоматического подтягивания» и «отсутствия настойчивости».

Таким образом, обычно можно использовать следующие решения:

Главный сервер не поддерживает постоянство, что повышает его производительность.

Включите сохраняемость AOF с сервера, отключите сохраняемость RDB, регулярно создавайте резервные копии файлов AOF и выполняйте команду bgaofrewrite рано утром, чтобы перезаписать файлы AOF, чтобы уменьшить размер файлов AOF. (Конечно, если у вас высокая устойчивость к потере данных, вы также можете включить сохранение RDB и отключить сохранение AOF)

4. Аварийное восстановление в разных местах, общие сбои (сбой питания, выключение) не повлияют на диск, но некоторые катастрофические сбои (землетрясения, наводнения) повлияют на диск, поэтому необходимо периодически восстанавливать файл AOF на одной машине или с сервера файлы RDB копируются в компьютерные комнаты в других регионах.

Что такое блокировка добавления файла AOF?

После добавления команды модификации в aof_buf, если конфигурация установлена ​​каждую секунду, операция fsync будет выполняться каждую секунду, а запись будет вызываться для однократной записи на диск.Однако, если загрузка жесткого диска слишком высока, операция fsync может занять более 1 с, а основной поток Redis продолжит записывать команды в aof_buf на высокой скорости, нагрузка на жесткий диск может становиться все больше и больше, а потребление ресурсов ввода-вывода будет быстрее, поэтому логика обработки Redis заключается в сравнении успешного времени последней fsync.Если оно превышает 2 с, основной поток будет заблокирован до завершения синхронизации fsync, поэтому он может потерять данные до 2 с вместо 1 с.

Как очистить ключи с истекшим сроком действия в Redis?

(1) Инертное удаление. При доступе к ключу, если будет обнаружено, что срок действия ключа истек, ключ будет удален.

(2) Регулярная очистка. Элемент конфигурации Redis hz определяет период выполнения задачи serverCron. Время по умолчанию для каждой очистки составляет 25 мс. Каждая очистка будет проходить по всем БД по очереди и случайным образом извлекать 20 ключей из БД. Если срок их действия истечет, они будут удалены , Если срок действия 5 ключей истек, то продолжайте очистку этой БД, иначе начните очистку следующей БД.

(3) При выполнении команды записи, если обнаружится, что памяти недостаточно, память будет очищена в соответствии с настроенной стратегией устранения.Стратегия устранения в основном включает следующее:

1. noeviction, не удалять, при достижении лимита памяти сразу возвращается сообщение об ошибке при выполнении команды записи.

2.allkeys-lru, среди всех ключей первым удалить наименее используемый ключ. (Подходит для запросов, которые следуют степенному распределению, то есть к некоторым ключам обращаются часто, а к некоторым ключам обращаются реже)

3.allkeys-random, во всех ключах удалить некоторые ключи случайным образом. (подходит для более равномерного распределения запросов)

4. volatile-lru, среди ключей с истекшим сроком действия сначала удалите наименее используемый ключ.

5. volatile-random, в ключе с истекающим сроком действия удалить часть ключа случайным образом.

6. volatile-ttl, в ключе с истекающим сроком действия предпочтительно удаляется ключ оставшегося периода времени.

После версии 4.0 добавлены следующие два:

volatile-lfu: выбирает наименее часто используемые данные из набора данных (server.db[i].expires) с установленным сроком действия.

allkeys-lfu: удалить наименее часто используемые ключи в пространстве ключей, когда памяти недостаточно для вновь записанных данных.

Алгоритм LRU

Принцип разработки алгоритма LRU заключается в том, что если к части данных не было доступа в последнее время, доступ к ним не будет осуществляться в течение определенного периода времени. Следовательно, когда количество элементов достигает предельного значения, предпочтительно удаляется элемент с наибольшим временем с момента последнего использования. Это можно реализовать с помощью HashMap+ узел двусвязного списка.После каждого доступа к элементу перемещайте элемент в конец связанного списка.Когда элемент заполнен, удаляйте элемент в начале списка. связанный список. Можно ли использовать односвязный список?Тоже можно.Хотя узел односвязного списка не может получить информацию предварительного узла, он может установить ключ и значение следующего узла на текущем узле, а затем указать следующий указатель текущего узла на узел next node.

LFU-алгоритм

В соответствии с принципом разработки алгоритма LFU, если к данным обращались больше раз за последний период, вероятность доступа к ним позже будет выше. Существует счетчик ссылок.После доступа к каждому данным счетчик ссылок увеличивается на 1. Когда данные необходимо удалить, удаляются данные с наименьшим счетчиком ссылок. В реализации Redis, После доступа к каждому ключу счетчик ссылок увеличивается на число p от 0 до 1, и чем чаще доступ, тем больше значение p, и в течение определенного интервала времени Если к ключу нет доступа, счетчик ссылок уменьшается.

Почему Redis однопоточный?

Официальный ответ на часто задаваемые вопросы Redis: Redis — это операция, основанная на памяти, и ЦП не будет узким местом.Узким местом Redis, скорее всего, является размер машинной памяти или пропускная способность сети. Так как однопоточность легко реализовать и ЦП не станет узким местом, логично принять однопоточное решение. (Один поток здесь означает, что модуль, обрабатывающий сетевые запросы, является однопоточным, а другие модули не обязательно однопоточными)

Redis использует преимущества одного потока:

1. Код проекта Redis станет понятнее, а логика обработки — проще.

2. Нет необходимости рассматривать ситуацию с изменением данных несколькими потоками.При изменении данных нет необходимости блокировать и разблокировать, и не будет проблемы взаимоблокировки, что приведет к потреблению производительности.

3. Нет потребления производительности, вызванного переключением, вызванным многопроцессорностью или многопоточностью.

Недостатки использования одного потока в Redis:

1. Преимущества многоядерных машин нельзя использовать в полной мере, но можно использовать ресурсы, запустив несколько экземпляров Redis на машине.

Почему производительность Redis высока?

Согласно введению на официальном сайте, одна машина Redis может достигать QPS 10 Вт (количество запросов в секунду).Основные причины, по которым Redis так быстр, следующие:

1. Полностью основано на памяти, все данные хранятся в памяти, и при чтении нет дискового ввода-вывода, поэтому скорость очень высокая.

2. Redis использует однопоточную модель, нет накладных расходов на переключение контекста, нет условий гонки, нет необходимости рассматривать различные проблемы с блокировкой, нет операции снятия блокировки и нет потребления производительности из-за возможных тупики.

3. Структуры данных, используемые в проекте Redis, специально разработаны.Например, SDS (Simple Dynamic String) — это при частом изменении строк на языке C выделение памяти будет выполняться часто, что потребляет много производительности, в то время как SDS будет использовать Предварительное выделение пространства и отложенное освобождение пространства позволяют избежать этих проблем. Технология предварительного распределения пространства: при изменении SDS, если фактическая длина измененного SDS равна len,

Когда Len

Когда len>1M, выделенное пространство будет len+1+1M, то есть будет зарезервировано неиспользуемое пространство длиной 1M, из которых 1 хранит нулевые символы.

4. Используя мультиплексную модель ввода-вывода, можно одновременно отслеживать возможности событий ввода-вывода нескольких потоков.Когда он простаивает, текущий поток будет заблокирован.Когда один или несколько потоков имеют события ввода-вывода, он изменится с состояние блокировки Просыпайтесь, опрашивайте те потоки, которые фактически генерировали события, и обрабатывайте только готовые потоки последовательно. Позволяет одному потоку эффективно обрабатывать несколько запросов на подключение (минимизируя время, затрачиваемое на сетевой ввод-вывод).

Каковы модели ввода-вывода Linux?

Модель ввода-вывода в основном состоит из модели блокирующего ввода-вывода, неблокирующей модели ввода-вывода, модели мультиплексирования ввода-вывода, модели ввода-вывода, управляемой информацией, и модели асинхронного ввода-вывода.

Блокирующая модель ввода/вывода

Процесс пользовательского режима вызывает системный вызов recvfrom для принятия данных.Если данные в текущем ядре не готовы, он будет ждать и не будет выполнять другие операции, пока данные в ядре не будут готовы, копирует данные в пространство пользователя memory, а затем recvfrom возвращает сигнал «Успех», в это время происходит разблокировка пользовательского режима и обработка полученных данных.

Модель ввода/вывода без блокировки

В модели неблокирующего ввода-вывода, когда процесс ожидает данных от ядра, а данные не приходят, процесс продолжает обращаться к ядру до тех пор, пока ядро ​​не будет готово для данных. Процесс пользовательского режима вызывает recvfrom для получения данных, и в настоящее время пакеты данных не генерируются. В это время recvfrom возвращает EWOULDBLOCK. Процесс пользовательского режима всегда будет вызывать recvfrom, чтобы запросить ядро. Когда ядро ​​будет готово для данных, Процесс пользовательского режима больше не будет запрашивать ядро.Данные копируются из ядра в пространство пользователя, recvfrom успешно возвращается, и процесс пользовательского пространства начинает обрабатывать данные.

Модель мультиплексирования ввода/вывода

Мультиплексирование ввода-вывода относится к множеству соединений ввода-вывода, мультиплексирующих процесс. Первый уровень мультиплексирования ввода-вывода состоит в том, что процесс соответствует множеству соединений, и каждый раз, когда он проходит от начала до конца, определяется, есть ли события ввода-вывода, которые необходимо обработать, и если они есть, они будут обработаны. Недостатком является то, что эффективность относительно низка: не приходят события, которые заставят ЦП простаивать. Обновленная версия модели мультиплексирования ввода/вывода Когда нет события ввода-вывода, процесс находится в состоянии блокировки.При наличии события ввода-вывода агент разбудит процесс и выполнит опрос для обработки события ввода-вывода. (Агенты здесь выбирают и опрашивают, select может отслеживать только 1024 соединения, а poll может отслеживать неограниченное количество соединений) epoll — это обновленная версия select and poll, которая решает многие проблемы, является потокобезопасной и может уведомлять процесс о том, какое соединение Socket имеет события ввода-вывода, что повышает эффективность поиска. Самая большая разница между epoll и select/poll заключается в том, (1) mmap используется внутри epoll для разделения части пространства между пользователем и ядром, что позволяет избежать копирования данных туда и обратно (2) epoll управляется событиями, epoll_wait возвращает только те события, которые произошли, избегая всей операции опроса таких событий, как select и poll.

Модель ввода-вывода, управляемая информацией

Он неблокирующий. Когда ему нужно дождаться данных, процесс пользовательского режима отправит сигнал ядру, чтобы сообщить нужные ему данные, а затем выполнит другие задачи. После того, как ядро ​​подготовит данные, оно отправит сигнал пользовательскому режиму. После того, как процесс получит его, он немедленно вызовет recvfrom и будет ждать, пока данные будут скопированы из пространства ядра в пользовательское пространство. данные.

Модель асинхронного ввода/вывода

Отличие от модели ввода-вывода, управляемой информацией, заключается в том, что после того, как данные скопированы из состояния ядра в пространство пользователя, ядро ​​уведомляет процесс пользовательского состояния обработать данные. Во время копирования данных в пользовательское пространство процесс пользовательского режима не блокируется.

В чем разница между синхронным и асинхронным?

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

В чем разница между блокировкой и не блокировкой?

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

Как решить проблему проникновения в кэш Redis?

Проникновение в кэш Redis означает, что злоумышленник умышленно запрашивает большое количество данных, ключ которых отсутствует в кеше Redis. Запрос попадает в базу данных, что приводит к чрезмерной нагрузке на базу данных.

1. Хорошо поработайте над проверкой параметров и напрямую возвращайте недействительные запросы, что может избежать только некоторых ситуаций, а злоумышленники всегда могут найти некоторые ситуации, которые не охвачены.

2. Для ключа, который не может быть найден в кеше, ключ, который нужно искать в базе данных, кэшируется в Redis, но это может привести к кэшированию в Redis большого количества недействительных ключей.Можно установить короткий срок действия время, например 1 минута.

3. Вы также можете использовать фильтр Блума, чтобы сохранить все возможные существующие данные в достаточно большом растровом изображении, удалив хэш-значение.При обработке запросов вы можете искать в ботмапе для хранения данных, которые не существуют, перехвачены.

Что такое фильтр Блума?

Фильтр Блума можно понимать как ошибочную структуру множества. При использовании фильтра Блума для определения того, существует ли в нем элемент, если возвращаемый результат да, он может существовать или не существовать на самом деле. Когда возвращаемый результат не существует, фактический результат положителен, нет. Фильтр Блума на самом деле представляет собой большой битовый массив, при добавлении ключа на ключе через несколько хеш-функций вычисляется индекс, после чего соответствующее значение в битовом массиве устанавливается в 1 по индексу.

Как решить проблему лавины кеша Redis?

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

1. Добавьте случайное значение при установке времени аннулирования кэша, чтобы избежать коллективного аннулирования.

2. Механизм двойного кеша, время истечения кеша A составляет 20 минут, кеш B не имеет срока действия, чтение данных из кеша A, когда нет кеша A, чтение данных из кеша B и запуск асинхронного потока для обновления кеш А.

Как решить проблему поломки кеша Redis?

Нарушение кэша в основном вызвано большим количеством одновременных запросов на доступ после истечения срока действия определенного ключа, что приводит к чрезмерной нагрузке на базу данных. план освобождения: 1. Установите мьютекс перед доступом к базе данных. После сбоя получения данных из кэша сначала установите блокировку мьютекса, установите ее успешно, а затем выполните операцию с базой данных, в противном случае засните на некоторое время и повторите попытку.

Каковы общие структуры данных Redis?

Основными структурами данных в Redis являются строки String, хэш-таблицы, списки списков, коллекции Set и упорядоченные коллекции ZSet.

Расскажите о своем понимании простых динамических строк в Redis?

Простые динамические строки в Redis на самом деле являются инкапсуляцией и оптимизацией строк в языке C, потому что строки в языке C имеют два недостатка:

1. Небезопасен для двоичного кода (поскольку строка заканчивается нулевым символом, а в середине строки не может быть нулевого символа).

2. Когда строка часто изменяется, это требует перераспределения памяти, что снижает производительность. (Простые динамические строки в Redis будут иметь предварительное выделение памяти и отложенное освобождение пространства).

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

1. Строки могут сохранять некоторые строковые данные и некоторые числовые данные, поэтому вы можете использовать INCR, DECR, INCRBY для сложения и вычитания чисел, поэтому вы можете использовать строки в качестве счетчиков.

2. В то же время, поскольку в языке C каждый символ представляет собой байт и 8 двоичных бит, поэтому вы можете использовать простую динамическую строку в качестве битового массива, а также использовать команды setbit и getbit для присвоения значений битовый массив и получить значение, могут сохранять данные о ежедневных проверках пользователя в течение года в небольшом пространстве, а фильтр Блума в Redis также реализован через битовый массив.

Базовое хранилище для строк

В Redis каждое значение представляет собой объект Redis, который соответствует структуре RedisObject.В структуре RedisObject сохраняются некоторые атрибуты, такие как тип объекта, базовая кодировка и т. д., а также указатель ptr, указывающий к конкретному объекту объекта адрес хранения.

struct RedisObject {
    int4 type; 
    int4 encoding; 
    int24 lru; 
    int32 refcount; 
    void *ptr; 
} robj;

В Redis строки хранятся двумя способами: в кодировке int, в кодировке embstr и в исходном кодировании.

кодировка int

Когда значение является целым числом и может быть представлено типом long (8 байт), то оно относится к кодировке int, и ptr непосредственно хранит значение. (И Redis оптимизирует и создаст строковый объект от 0 до 9999 в качестве общей переменной при запуске.)

embstr и необработанное кодирование

В обоих методах хранения для хранения строк используются RedisObject и структура SDS (простая динамическая строка). Разница в том, что объекты embstr используются для хранения более коротких строк. Структура RedisObject в кодировке embstr и структура SDS, на которую указывает ptr, находятся в памяти. , Это непрерывно, количество выделений памяти и количество освобождений памяти однократно, и необработанное кодирование вызовет функцию выделения памяти дважды, чтобы создать структуру RedisObject и структуру SDS соответственно.

Расскажите о своем понимании хеш-объекта в Redis?

В Redis value может быть хэш-таблицей, а базовая кодировка может быть ziplist или hashtable (по умолчанию, когда количество элементов меньше 512, базовый слой использует ziplist для хранения данных).

ziplist

Когда длина строки, хранимой элементом, короткая, а количество элементов небольшое (менее 64 байт, число меньше 512), для экономии памяти хеш-таблица будет использовать ziplist в качестве базовой реализации, а ziplist это непрерывный кусок памяти,каждая нода хранит соответствующий ключ и значение,а потом каждая нода хранится вместе очень компактно.Преимущество в том что нет лишнего места.Недостаток в том что вставка новых элементов требует вызова realloc для расширения память. (Память может быть перераспределена, содержимое будет скопировано, а также может быть расширен исходный адрес).

hashtable

Когда элементов много, в качестве базовой реализации будет использоваться кодирование хэш-таблицы. В это время указатель ptr RedisObject будет указывать на структуру dict. Массив ht в структуре dict сохраняет два элемента, ht[0] и ht[ 1].Обычно ht[0] сохраняет пары ключ-значение, ht[1] используется только в прогрессивном перефразировании. Hashtable разрешает конфликты с помощью метода адресации по цепочке.В массиве table хранится головной узел связанного списка (чтобы добавить новый элемент, сначала вычислите хеш-значение по ключу, а затем по модулю длины массива, чтобы получить индекс массива, и добавить элемент в связанный список, соответствующий индексу массива).

struct dict {
    int rehashindex;
    dictht ht[2]; 
}
struct dictht {
    dictEntry** table; // 二维
    long size; // 第一维数组的长度 
    long used; // hash 表中的元素个数 ...
}
typedef struct dictEntry {
  //键  
  void *key;
  //值,可以是一个指针,也可以是一个uint64_t整数,也可以是int64_t的整数
  union {
    void *val;
    uint64_tu64;
    int64_ts64;
  } v;
  //指向下一个节点的指针
  struct dictEntry *next;
} dictEntry;

прогрессивная перефразировка

Когда коэффициент загрузки >= 1, будет выполнена операция расширения хеш-таблицы (если это происходит во время выполнения команды BGSAVE или BGREWRITEAOF, для расширения требуется >= 5).

Когда коэффициент загрузки меньше 0,1, будет выполнена операция сжатия хеш-таблицы.

Поскольку повторное хеширование напрямую в один момент времени повлияет на производительность, вы можете выполнять повторное хэширование поэтапно. Конкретные этапы выполнения следующие: 1. Сначала выделите место для хеш-таблицы ht[1] в структуре dict (размер зависит от n-й степени двойки, ближайшей к фактическому используемому пространству), а затем установите для атрибута rehashindex значение 0.

2. Затем каждый раз, когда вы ищете, изменяете и добавляете элемент в ht[0], в дополнение к выполнению указанной операции он будет перехешировать все пары ключ-значение, соответствующие индексу ht[1], и перехешировать +1.

3. Когда все пары ключ-значение в ht[0] будут перехэшированы в ht[1], значения указателей ht[1] и ht[0] будут заменены местами, а rehashindex будет установлен в -1, что означает перепрошивка завершена. (В течение всего периода перефразирования поиск, обновление и удаление будут выполняться сначала в ht[0], а если нет, то в ht[1], а вновь добавленные пары ключ-значение будут сохраняться в ht[1] ).

Расскажите о своем понимании List в Redis?

В Redis хранимое значение может быть списком, очень похожим на LinkedList в Java. Базовая структура данных — это связанный список. Вставка и удаление выполняются быстро, произвольный доступ — медленно, а временная сложность — O(N). Когда данные списка в Java кэшируются, они обычно сериализуются в JSON и сохраняются в Redis в виде строк вместо использования списка в Redis для хранения. Список в Redis можно использовать как очередь или как стек. В реальном использовании он часто используется для асинхронных очередей, сериализации задач, которые могут быть задержаны, в строки и вставки их в список Redis, а другой поток опрашивает данные из списка для обработки.

quiklist

В старой версии Redis, когда элементов мало, в качестве базовой кодировки используется ziplist, а когда элементов много, в качестве базовой кодировки используется двусвязный список. Поскольку каждому узлу связанного списка нужны указатели prev и next, которые занимают 16 байт, а память каждого узла выделяется отдельно, что увеличивает фрагментацию памяти, поэтому новая версия использует quiklist в качестве базового кода. список, но каждый узел является ziplist. (Максимальная длина каждого ziplist по умолчанию составляет 8 КБ.)

Расскажите о своем понимании Set в Redis?

Набор — это неупорядоченный, неповторяющийся набор строк. Базовая кодировка имеет два типа: вставку и хеш-таблицу.

inset

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

hashtable

Когда количество элементов велико, Set использует хэш-таблицу для хранения элементов, значение элемента используется в качестве ключа, и все значения равны NULL.

Расскажите о своем понимании ZSet (упорядоченный набор) в Redis?

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

Когда элементов немного, базовое кодирование ZSet реализуется с помощью ziplist, и все элементы сортируются от низшего к высшему по Score.

Когда элементов много, используйте skiplist+dict для достижения, skiplist хранит значение элемента и Score, а также упорядочивает все элементы по количеству очков. Удобно вставлять, удалять, обновлять и выполнять поиск диапазона по Score с временной сложностью O(logN).

В dict хранится отношение сопоставления между значением элемента и оценкой, что удобно для нахождения соответствующей оценки элемента с временной сложностью O (1).

Как реализована синхронизация Redis master-slave?

После того, как ведущий-ведомый узел установит соединение, подчиненный узел будет оценивать

1. Если подчиненный узел ранее не синхронизировал данные, это первая репликация, и будет выполнена полная ресинхронизация. Затем подчиненный узел отправит команду PSYNC?-1 главному узлу, запрашивая у главного узла выполнить полную ресинхронизацию.

2. Если это первая репликация с узла (например, повторное подключение после отключения), В это время подчиненный узел отправит ранее синхронизированный идентификатор репликации (случайная строка, идентифицирующая конкретный набор данных на главном узле) и смещение (текущее смещение репликации подчиненного сервера) на главный узел с помощью команды PSYNC, и главный узел будет судить,

  • Если идентификатор репликации не соответствует текущему идентификатору репликации или соответствующее смещение не существует в текущем буфере, будет выполнена полная ресинхронизация, как в первой репликации выше.
  • Если идентификатор репликации совпадает с текущим идентификатором репликации и в текущем буфере имеется соответствующее смещение, будет выполнена частичная повторная синхронизация. (Частичная повторная синхронизация поддерживается версиями после Redis 2.8. Она в основном основана на соображениях производительности. Выполнять полную повторную синхронизацию для небольшой части модификации данных во время отключения относительно неэффективно.)
Полная повторная синхронизация

Главный узел выполнит команду BGSAVE, разветвит дочерний процесс и сгенерирует постоянный файл RDB в фоновом режиме.После завершения отправит его на С сервера подчиненный узел принимает и загружает файл RDB, так что состояние базы данных подчиненного узла обновляется до состояния, когда главный узел выполняет команду BGSAVE. И во время генерации файла RDB главный узел также будет использовать буфер для записи всех команд записи, выполненных за этот период, отправить эти команды на подчиненный узел, а подчиненный узел выполнит команду для обновления состояния своей базы данных до быть точно таким же, как главный узел.

частичная повторная синхронизация

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

распространение команды

После выполнения полной ресинхронизации или частичной ресинхронизации, после того как статус базы данных главного и подчиненных узлов станет согласованным, он перейдет к фазе распространения команды. После того, как мастер-узел выполнит команду модификации, он добавит команду модификации в буфер буфера в памяти (это кольцевой массив фиксированной длины, который перезапишет предыдущие данные при его заполнении), а затем асинхронно отправит буфер команда буфера подчиненному узлу.

Что такое Sentinel в Redis?

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

Как клиент подключается к системе Sentinel?

Во-первых, дозорный узел в Redis — это поставщик конфигурации, а не прокси. Разница в том, что

поставщик конфигурации

Первый отвечает только за хранение последней информации об узле master-slave для получения клиентами.

играет роль

Все клиентские запросы будут проходить через дозорный узел.

Таким образом, фактическая разработка, дозорный узел через адрес в имени клиента + первичный узел (дозорная система может отслеживать множество главных узлов для различения имен) может передавать информацию главному узлу, Следующий код реализован в нижнем слое, последовательно передается команда «sentinel get-master-addr-by-name» клиенту на дозорный узел, Успешный главный узел не отправляет информацию обратно команде контрольного узла. В то время как клиентская подписка + контрольный узел канала переключения-мастера, после сбоя главного узла, контрольный узел автоматического переключения узла главного сервера обновит главный узел, И обновляя информацию главного дозорного узла, хранящуюся на сервере, отправляет сообщения на главный канал переключателя +, извлекает информацию из контрольного узла главного узла до клиента для получения информации, инициализирует пул соединений.

 String masterName = "mymaster";
 Set<String> sentinels = new HashSet<>();
 sentinels.add("192.168.92.128:26379");
 sentinels.add("192.168.92.128:26380");

 JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels); //初始化过程做了很多工作
 Jedis jedis = pool.getResource();
 jedis.set("key1", "value1");
 pool.close();

Как система Redis Sentinel достигает автоматического отработки отработки?

1. Определение того, что главный узел субъективно находится в автономном режиме

Поскольку каждые 2 секунды дозорный узел отправляет команду PING главному узлу.Если ответ не получен в течение определенного интервала времени, дозорный узел субъективно считает главный узел отключенным.

2. Определите главный узел объективно в автономном режиме

После того, как дозорный узел определит, что главный узел субъективно находится в автономном режиме, он отправит команду sentinel is-master-down-by-addr другим дозорным узлам, чтобы получить статус главного узла от других дозорных узлов. , определено, что главный узел объективно находится в автономном режиме.

3. Провести дозорные выборы лидера

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

В выборах используется алгоритм Raft.Основная идея заключается в том, что все дозорные узлы A сначала отправят команды другим дозорным узлам, чтобы подать заявку на то, чтобы стать лидером дозорного узла B. Если B не согласился с другими дозорными узлами, то согласитесь с A Чтобы стать лидером, дозорный узел, набравший более половины голосов, выиграет выборы.Если лидер-дозорный узел не будет избран в этом голосовании, начнется новый раунд выборов, пока не будет избран первичный узел оценивается объективно. Узлы Sentinel, которые переходят в автономный режим, обычно могут стать лидерами.)

4. Лидер Sentinel для отработки отказа

Лидер-дозорный узел сначала выберет узел из подчиненных узлов в качестве нового главного узла. Выбранные правила:

1. Сначала исключите некоторые неработоспособные узлы. (не в сети, отключенные, те, кто не отвечал на команду INFO дозорного узла в течение последних 5 с, и те, кто был отключен от старого главного сервера в течение длительного времени)

2. Затем выберите подчиненный узел в качестве главного узла в соответствии с приоритетом, смещением копии и идентификатором runid с наименьшим идентификатором runid.

Отправьте команду slaveof no one этому подчиненному узлу, чтобы сделать его главным узлом, сделайте другие подчиненные узлы своими подчиненными узлами с помощью команды slaveof и обновите автономный главный узел до подчиненного узла нового главного узла.