Немного длинный блог: Redis не так прост, как настроить

Java

Когда я раньше не связывался с Redis, я слышал, как маленькие партнеры в группе больших данных обсуждали Redis. Я думал, что это очень высококлассный продукт. Было бы здорово, если бы наша группа когда-нибудь смогла использовать Redis. проект Я наконец представил технологию Redis.Я использовал ее несколько раз, и я чувствую, что Redis - это то же самое, разве это не просто установить? Когда я узнал, что в Redis тоже есть операции автоинкремента и автодекремента, и эти операции атомарны, и эту технологию можно использовать за секунды, я почувствовал, что уже знаком с Redis. Я полагаю, что у многих творожных мальчиков та же идея, что и у меня: разве Redis не получает просто инкремент? На самом деле Redis далеко не так прост, как мы себе представляли, и сегодня я здесь, чтобы поговорить о Redis.

Мы не будем подробно останавливаться на том, что такое Redis, как его установить и т.д. Перейдем сразу к теме.

Пять типов данных и сценарии применения Redis

В Redis есть пять типов данных, а именно строка, список, хеш, набор, zset (набор сортировки) Думаю, этот момент должен быть понятен тем, кто немного знаком с Redis. Ниже мы обсудим сценарии применения этих пяти типов данных.

string

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

Начнем с самого простого:

localhost:6379> set coderbear hello
OK
localhost:6379> get codebear
"hello"

Я думаю, что все знают эти две команды, поэтому я не буду их объяснять. Давайте снова воспользуемся командой strlen:

localhost:6379> strlen codebear
(integer) 5

О, я понимаю, что команда strlen может получить длину Value Длина hello равна 5, поэтому вывод равен 5. Правильно ли это объяснение? Не волнуйтесь, давайте медленно посмотрим вниз.

Мы используем команду append, чтобы добавить что-то к ключу codebear:

APPEND codebear 中国

Что будет на выходе, если мы снова воспользуемся командой strlen? Конечно, это 7. Хотя я не силен в математике, у меня все еще нет проблем со счетом в пределах 10, но когда мы снова выполним команду strlen, вы обнаружите странное явление:

localhost:6379> strlen codebear
(integer) 11

Нани, почему 11, это из-за того, что у нас неправильный метод открытия, стоит ли попробовать еще раз? Нет необходимости, даже если вы снова попробуете Sansheng III, результат, который вы увидите, все равно будет 11. Почему это? Это связано с проблемами безопасности двоичных файлов.

бинарный сейф

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

Давайте кодмедведь Канкан:

get codebear
"hello\xe4\xb8\xad\xe5\x9b\xbd"

Вы обнаружите, что «привет, Китай» настолько хорош, что хранится в Redis, потому что наш клиент Xshell использует UTF-8.В UTF-8 один китайский обычно занимает три байта, два китайских — 6 байт, поэтому «привет China" занимает в Redis 5+6=11 байт.

Если вы мне все еще не верите, давайте изменим кодировку Xshell на GBK и посмотрим.В мире GBK один китаец обычно занимает два байта, так что:

localhost:6379> set codebeargbk 中国
OK
localhost:6379> get codebeargbk
"\xd6\xd0\xb9\xfa"
localhost:6379> strlen codebeargbk
(integer) 4

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

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

Мы часто используем Redis в качестве кеша и используем набор двух команд get. Мы также можем использовать Redis в качестве системы второго уничтожения. В большинстве случаев также используется тип данных String. Давайте продолжим:

localhost:6379> set codebearint 5
OK
localhost:6379> incr codebearint 
(integer) 6

Возможно, вы никогда не использовали команду incr, но вы можете догадаться, что делает команда incr по результату и названию команды.Да, это самоинкремент.Так как есть самоинкремент, вы также можете сделать самодекремент:

localhost:6379> decr codebearint 
(integer) 5

Только что было 6, но после вызова команды decr снова становится 5.

Ну и еще вопрос, String это строка, как она умеет складывать или вычитать?

Мы используем команду type для проверки типа ключа codebearint:

localhost:6379> type  codebearint
string

Правильно, это поддельный тип String.

Давайте посмотрим еще на одну вещь:

localhost:6379> object encoding codebear
"raw"
localhost:6379> object encoding codebearint
"int"

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

bitmap

Есть такое требование: Подсчитать количество дней, которое указанный пользователь заходит в год? Это не просто, я создаю форму входа, я не могу это сделать? Да, это так, но не слишком ли высока цена?Если есть 1 миллион пользователей, насколько велика эта таблица входа? В это время родился битмап, это просто артефакт для решения подобных задач.

Давайте сначала посмотрим, что такое растровое изображение, грубо говоря, это двоичный массив, для иллюстрации нарисуем картинку:

图片.png

Это растровое изображение, состоящее из множества маленьких сеток, и в сетку можно поместить только 1 или 0. Профессиональная точка зрения состоит в том, что каждая маленькая сетка — это бит. String очень хорошо поддерживает растровое изображение, предоставляя ряд команд растрового изображения, давайте попробуем:

setbit codebear 3 1
(integer) 0
localhost:6379> get codebear
"\x10"

Что это значит, то есть есть 8 маленьких сеток, четвертая сетка равна 1 (индекс начинается с 0), а остальные равны 0, вот так:

图片.png

Давайте подсчитаем, каким должен быть размер, 1 2 4 8 16 , да, это 16 в десятичном виде, а вывод нашего get codebear будет "\x10", "\x" представляет шестнадцатеричный, который равен 10 в шестнадцатеричном, 10 в шестнадцатеричном — 16 в десятичном.

Давайте воспользуемся командой strlen, чтобы увидеть:

localhost:6379> strlen codebear
(integer) 1

Вроде занимает всего один байт, продолжим:

localhost:6379> setbit codebear 5 1
(integer) 0

Растровое изображение становится следующим фиолетовым соусом:

图片.png

Размер в десятичной системе равен 20.

Продолжим смотреть на strlen:

localhost:6379> strlen codebear
(integer) 1

Он по-прежнему занимает всего один байт, мы уже сохранили два данных, разве это не удивительно?

давай продолжим:

localhost:6379> setbit codebear 15 1
(integer) 0
localhost:6379> strlen codebear
(integer) 2

Из этого видно, что битмап можно расширить.Так как я сейчас поставил 1 в 16-й сетке, то битмап расширяется, и теперь strlen равен 2. Затем я хочу знать, сколько сеток в 16 сетках 1. Что мне делать? С помощью команды битсчета:

localhost:6379> bitcount codebear
(integer) 3

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

localhost:6379> setbit codebear 0 1
(integer) 0
localhost:6379> setbit codebear 1 1
(integer) 0
localhost:6379> setbit codebear 364 1
(integer) 0
localhost:6379> bitcount codebear 
(integer) 3

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

localhost:6379> strlen codebear
(integer) 46

Всего 46 байт, даже если вы входите в систему каждый год, она занимает всего 46 байт. Я не знаю, насколько велика должна быть база данных, но я думаю, что она намного больше, чем 46 байт. Если есть 1 миллион пользователей, это менее 50 МБ, даже если байты, занимаемые 1 миллионом пользователей, которые входят в систему каждый день, составляют эти байты.

Давайте изменим приведенные выше требования: Подсчитать количество дней, в течение которых указанный пользователь вошел в систему в течение произвольного временного окна?

Команда bitcount также может сопровождаться двумя параметрами, start и end:

localhost:6379> bitcount codebear 0 2
(integer) 2

Мы также можем написать второй параметр как -1 для представления до последней цифры, а именно:

localhost:6379> bitcount codebear 0 -1
(integer) 3

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

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

У меня болит мозг, это то, что люди делают? Не волнуйтесь, всего этого можно добиться с помощью растрового изображения.

Первое требование легко реализовать.Предполагая, что userId пользователя codebear равен 5, а userId пользователя Xiaoqiang равен 10, мы можем создать растровое изображение, ключом которого является дата, в которой четвертая и девятая ячейки равны 1, что означает, что userId равен 5. Пользователь, чей userId равен 1, вошел в систему в этот день, а затем под битсчетом все в порядке, как показано ниже:

localhost:6379> setbit 20200301 4 1
(integer) 0
localhost:6379> setbit 20200301 9 1
(integer) 0
localhost:6379> bitcount 20200301
(integer) 2

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

localhost:6379> setbit 20200229 9 1
(integer) 1
localhost:6379> bitop and andResult 20200301 20200229
(integer) 2
localhost:6379> bitcount andResult
(integer) 1
localhost:6379> bitop or orResult 20200301 20200229
(integer) 2
localhost:6379> bitcount orResult
(integer) 2

Чтобы объяснить ниже, сначала создайте растровое изображение с ключом 20200229, в котором 10-я маленькая сетка равна 1, что означает, что пользователь с идентификатором пользователя 10 вошел в систему в день 20200229, а затем создайте растровые изображения с ключами 20200301 и 20200229. .И операция, результат тоже битмап, и результат кладется в ключ andResult, далее знакомая команда bitcount, сколько мелких ячеек в Kangkang 1, результат 1, то есть эти два дня , каждый день Вошедший в систему пользователь имеет один.

Так как есть операция И, то есть операция ИЛИ.Следующая команда операции ИЛИ, чтобы узнать, что два пользователя вошли в систему за эти два дня.

Таким образом, последние два требования легко решаются.

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

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

list

Нижний слой списка Redis представляет собой двусвязный список.Начнем с нескольких команд:

localhost:6379> lpush codebear a b c d e
(integer) 5
localhost:6379> lrange codebear 0 -1
1) "e"
2) "d"
3) "c"
4) "b"
5) "a"

нажми, я понимаю, нажми, но что означает +l впереди, l впереди означает лево, lpush означает нажатие слева, поэтому первый толчок крайний справа, а lrange вынесен из left Данные в указанном диапазоне индексов, -1 за ним означает получение последнего.

Поскольку вы можете толкать слева, вы должны уметь толкать и справа, мы, Канкан:

localhost:6379> rpush codebear z
(integer) 6
localhost:6379> lrange codebear 0 -1
1) "e"
2) "d"
3) "c"
4) "b"
5) "a"
6) "z"

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

localhost:6379> lpop codebear
"e"
localhost:6379> lrange codebear 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
5) "z"
localhost:6379> rpop codebear
"z"
localhost:6379> lrange codebear 0 -1
1) "d"
2) "c"
3) "b"
4) "a"

lpop извлекает первый элемент слева, а rpop извлекает первый элемент справа.

Если мы используем комбинацию lpush, rpop или rpush, lpop, то это очередь «первым пришел, первым обслужен», что и является очередью; если мы используем комбинацию lpush, lpop или rpush, rpop, это будет очередь. last-out, который представляет собой стек, поэтому Redis также может использовать в качестве очереди сообщений тип данных списка.

Я считаю, что все, должно быть, играли в форум.Если вы постите позже, пост обычно находится впереди. Для производительности мы можем поместить данные поста в список в Redis, но мы не можем кидать данные в список на неопределенный срок, как правило, посты на первых нескольких страницах увидят больше людей, а их будет меньше. сообщения позже. Некоторые люди прочитали его, поэтому мы можем поместить данные сообщений предыдущих страниц в список, а затем установить правило для регулярного удаления данных из списка. Можем ли мы использовать ltrim списка? Команда:

localhost:6379> ltrim codebear 1 -1
OK
localhost:6379> lrange codebear 0 -1
1) "c"
2) "b"
3) "a"

Этот ltrim немного странный, он сохраняет данные в пределах диапазона индекса, удаляет данные за пределами диапазона индекса, теперь первый заданный параметр равен 1, второй параметр равен -1, он должен хранить данные от индекса 1 до конца данных, поэтому данные с индексом 0 удаляются.

hash

Теперь есть страница сведений о продукте, которая содержит описание продукта, цену, теплое напоминание, количество просмотров, количество покупателей и кучу другой информации.Конечно, мы можем хранить весь объект в String, но может быть и некоторая места, где требуется только введение продукта, и, несмотря на это, мы должны вывезти все это, не правда ли, это немного неэкономично? hash может решить эту проблему:

localhost:6379> hset codebear name codebear
(integer) 1
localhost:6379> hset codebear age 18
(integer) 1
localhost:6379> hset codebear sex true
(integer) 1
localhost:6379> hset codebear address suzhou
(integer) 1
localhost:6379> hget codebear address
"suzhou"

Что, если мы сохраним весь объект и теперь захотим изменить возраст? Выньте весь объект, назначьте его и вставьте обратно, но теперь:

localhost:6379> hincrby codebear age 2 
(integer) 20
localhost:6379> hget codebear age
"20"

set

Set — это неупорядоченная и дедуплицированная структура данных. Мы можем использовать ее для дедупликации. Например, если я хочу сохранить идентификаторы всех продуктов, я могу использовать set для достижения этой цели. В каких сценариях мне нужно хранить идентификаторы все продукты? Для предотвращения проникновения в кеш, конечно же, существует множество схем реализации предотвращения проникновения в кеш, и схема set — лишь одна из них. Давайте перейдем к основному использованию Kangkang:

localhost:6379> sadd codebear 6 1 2 3 3 8 6
(integer) 5
localhost:6379> smembers codebear 
1) "1"
2) "2"
3) "3"
4) "6"
5) "8"

Хорошо видно, что данные, которые мы храним, были дедуплицированы, и данные были нарушены.

Давайте посмотрим, для чего нужна команда srandmember?

localhost:6379> srandmember codebear 2
1) "6"
2) "3"
localhost:6379> srandmember codebear 2
1) "6"
2) "2"
localhost:6379> srandmember codebear 2
1) "6"
2) "3"
localhost:6379> srandmember codebear 2
1) "6"
2) "2"
localhost:6379> srandmember codebear 2
1) "8"
2) "3"

За srandmember могут следовать параметры, за которыми следует 2, что означает, что два неповторяющихся элемента удаляются случайным образом.Что делать, если вы хотите удалить два повторяющихся элемента?

localhost:6379> srandmember codebear -2
1) "6"
2) "6"

Если за ним следует отрицательное число, это означает, что извлеченный элемент можно повторить.

Что, если последующее число больше, чем количество элементов множества?

localhost:6379> srandmember codebear 100
1) "1"
2) "2"
3) "3"
4) "6"
5) "8"
localhost:6379> srandmember codebear -10
 1) "8"
 2) "1"
 3) "1"
 4) "1"
 5) "6"
 6) "1"
 7) "1"
 8) "2"
 9) "6"
10) "8"

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

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

set также может вычислять разницу, объединение, пересечение:

localhost:6379> sadd codebear1 a b c
(integer) 3
localhost:6379> sadd codebear2 a z y
(integer) 3
localhost:6379> sunion codebear1 codebear2
1) "a"
2) "c"
3) "b"
4) "y"
5) "z"
localhost:6379> sdiff codebear1 codebear2
1) "b"
2) "c"
localhost:6379> sinter codebear1 codebear2
1) "a"

Вышеупомянутая команда не будет слишком объясняться, какой от нее толк, мы можем использовать его, чтобы сделать рекомендательную систему для «выманивания финансирования»: кто ваши общие друзья, в какую игру вы оба играете, вы можете знать людей.

zset

Set неупорядочен, а zset упорядочен. Каждый элемент имеет понятие счета. Чем меньше счет, тем выше рейтинг. Начнем с основного использования Kangkang:

localhost:6379> zadd codebear 1 hello 3 world 2 tree
(integer) 3
localhost:6379> zrange codebear 0 -1 withscores
1) "hello"
2) "1"
3) "tree"
4) "2"
5) "world"
6) "3"
localhost:6379> zrange codebear 0 -1
1) "hello"
2) "tree"
3) "world"

Теперь мы создаем zset с ключом codebear и добавляем к нему три элемента: hello, world, tree и score равны 1, 3 и 2 соответственно, а затем используем zrange для извлечения результатов и обнаруживаем, что они имеют был отсортирован по размеру баллов, теперь, если за ним следует withscores, баллы будут выведены вместе.

Если мы хотим увидеть, где находится дерево, мы можем использовать команду zrank:

localhost:6379> zrank codebear tree
(integer) 1

Поскольку он начинается с 0, результат равен 1.

Если мы хотим запросить оценку дерева:

localhost:6379> zscore codebear tree
"2"

Что, если мы хотим вывести первые два от большего к меньшему:

localhost:6379> zrange codebear -2 -1
1) "tree"
2) "world"

Но этот результат немного неправильный, первые два от большого к меньшему, первый элемент - это мир, как насчет:

localhost:6379> zrevrange codebear 0 1
1) "world"
2) "tree"

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

Говоря о szet, также может возникнуть вопрос, для чего используется zset в Redis? пропустить стол.

По поводу скип-таблицы, я не буду здесь ее расширять, зачем использовать скип-таблицу для ее реализации, из-за особенностей скип-таблицы: Чтение и запись баланса.

Почему Redis такой быстрый

Это классический вопрос для интервью. Почти 80% интервью говорят о Redis. Почему Redis такой быстрый? В основном по следующим причинам:

  1. Язык программирования: Redis написан на языке C, который ближе к нижнему уровню и может напрямую вызывать функции ОС.
  2. На основе памяти: поскольку данные Redis размещаются в памяти, они работают быстрее. Если он размещен на жестком диске, производительность зависит от двух показателей: адресации (скорости) и пропускной способности. Адресация на миллисекундном уровне, вообще говоря, пропускная способность около 500M, даже если производительность сервера отличная, пропускной способности в несколько гигабайт не будет, при размещении в памяти она находится на уровне наносекунд.
  3. Однопоточный: поскольку Redis является однопоточным, он позволяет избежать использования переключения потоков, и нет конкуренции, поэтому он быстрее.
  4. Сетевая модель: поскольку сетевая модель Redis — это epoll, это мультиплексная сетевая модель. (обсуждено позже о epoll)
  5. Оптимизация структуры данных Redis: в Redis предусмотрено 5 типов данных, из которых zset оптимизирован с помощью таблицы пропуска, а весь Redis фактически оптимизирован с помощью хэша, так что затраты времени составляют O(1), а поиск выполняется быстрее. .
  6. Redis6.0 представил потоки ввода-вывода, поэтому он быстрее. (О потоках ввода/вывода будет рассказано позже)

Каковы недостатки Redis

Это открытый вопрос со многими ответами, например:

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

Почему Redis может гарантировать атомарность

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

Redis является однопоточным, как это объяснить

Мы подчеркивали, что Redis является однопоточным, а Redis — однопоточным, но действительно ли Redis полностью однопоточный? На самом деле Redis однопоточный, но чтение и запись Redis однопоточные, то есть рабочий поток всего один.

Что такое потоки ввода/вывода

Потоки ввода-вывода — это новая функция, представленная Redis 6.0.В прошлом Redis получал запрос из сокета, обрабатывал его и записывал результат в сериализованный сокет, а именно:

图片.png

И после того, как Redis6.0 запустил потоки ввода-вывода:

图片.png
Видно, что существует несколько потоков ввода-вывода. Поток ввода-вывода отвечает за чтение данных из сокета и запись данных в сокет. Пока рабочий поток обрабатывает данные, другие потоки ввода-вывода могут считывать данные из сокет. Сначала подготовьтесь. Когда рабочий поток занят текущей работой, он может немедленно обработать следующий запрос. Но рабочий поток только один, так что имейте это в виду.

что такое эпол

Epoll — это мультиплексирующая модель ввода-вывода. Прежде чем говорить об epoll, я должен поговорить о традиционной модели ввода-вывода. Традиционная модель ввода-вывода синхронно заблокирована. Что это значит? То есть сокет, установленный сервером, будет ждать подключения клиента.Когда клиент подключен, он будет ждать запроса записи клиента.Сервер может обслуживать только одного клиента.

Позже программисты обнаружили, что для решения этой проблемы можно использовать многопоточность:

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

Выглядит очень красиво.Сервер может обслуживать N клиентов,но не может бесконечно открывать потоки.В Java потоки имеют свои независимые стеки.Поток потребляет не менее 1M,и бесконечно открытые потоки,ЦП тоже не выдерживает.

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

Давайте сначала разберемся с методом epoll.В Linux вы можете использовать man для просмотра функций ОС:

man epoll

Во введении есть такой отрывок:

       *  epoll_create(2) creates a new epoll instance and returns a file descriptor referring to that instance.  (The more recent epoll_create1(2) extends
          the functionality of epoll_create(2).)

       *  Interest in particular file descriptors is then registered via epoll_ctl(2).  The set of  file  descriptors  currently  registered  on  an  epoll
          instance is sometimes called an epoll set.

       *  epoll_wait(2) waits for I/O events, blocking the calling thread if no events are currently available.

Хоть мой английский и правда плохой, но с помощью перевода я немного понимаю, что примерно означает:

  1. epoll_create создает экземпляр epoll и возвращает дескриптор файла.
  2. epoll_ctl используется для регистрации интересующих событий.
  3. epoll_wait используется для ожидания событий ввода-вывода. Если в настоящее время нет интересующих событий ввода-вывода, он будет заблокирован. Подразумевается, что если произойдет интересующее событие, этот метод вернется.

Демонстрация также приведена ниже, давайте попробуем:

        epollfd = epoll_create1(0);//创建一个epoll实例,返回一个 epoll文件描述符
           if (epollfd == -1) {
               perror("epoll_create1");
               exit(EXIT_FAILURE);
           }

           ev.events = EPOLLIN;
           ev.data.fd = listen_sock;
            // 注册感兴趣的事件
            // 第一个参数为epoll文件描述符
            // 第二个参数为动作,现在是要添加感兴趣的事件
            // 第三个参数为被监听的文件描述符
            // 第四个参数告诉内核需要监听什么事件
            // 现在监听的事件是EPOLLIN,表示对应的文件描述符上有可读数据
           if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
               perror("epoll_ctl: listen_sock");
               exit(EXIT_FAILURE);
           }
           // 一个死循环
           for (;;) {
               //  等待IO事件的发生
               //  第一个参数是epoll文件描述符
               //  第二个参数是发生的事件集合
               //  第三个参数不重要
               //  第四个参数是等待时间,-1为永远等待
               //  返回值是发生的事件的个数
               nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
               if (nfds == -1) {
                   perror("epoll_wait");
                   exit(EXIT_FAILURE);
               }
               // 循环 
               for (n = 0; n < nfds; ++n) {
                    // 如果发生的事件对应的文件描述符是listen_sock
                   if (events[n].data.fd == listen_sock) {
                       // 建立连接 
                       conn_sock = accept(listen_sock,
                                          (struct sockaddr *) &addr, &addrlen);
                       if (conn_sock == -1) {
                           perror("accept");
                           exit(EXIT_FAILURE);
                       }
                       setnonblocking(conn_sock);// 设置非阻塞
                       ev.events = EPOLLIN | EPOLLET;// 设置感兴趣的事件
                       ev.data.fd = conn_sock;
                       // 添加感兴趣的事件,为 EPOLLIN或者EPOLLET
                       if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                                   &ev) == -1) {
                           perror("epoll_ctl: conn_sock");
                           exit(EXIT_FAILURE);
                       }
                   } else {
                       do_use_fd(events[n].data.fd);
                   }
               }
           }

Поскольку я не изучил язык C, пожалуйста, потерпите меня, если некоторые комментарии будут неправильными, но я думаю, что это означает.

Если вы изучили NIO на Java, вы должны почувствовать то же самое, увидев приведенный выше код.

Что, черт возьми, такое epoll?Проще говоря, это сообщение ядру, какие события меня интересуют, и ядро ​​будет следить за этим для вас.Когда происходит интересующее вас событие, ядро ​​будет активно уведомлять вас.

В чем преимущество этого:

  1. Уменьшите переключение между режимом пользователя и режимом ядра.
  2. Основан на методе оповещения о готовности к событию: ядро ​​активно информирует вас, и вам непросто опрашивать и судить.
  3. Для файловых дескрипторов почти нет верхнего предела: вы можете взаимодействовать с любым количеством клиентов, а поток может прослушивать N клиентов и завершать взаимодействие.

Функция epoll основана на ОС, в windows такого понятия, как epoll, нет.

Что ж, введение в epoll здесь, и появились три новых термина: пользовательский режим, режим ядра и файловый дескриптор.Я не буду объяснять это сначала, и я напишу об этом позже в блоге NIO. В это время epoll будет представлен более подробно.

Политика истечения срока действия Redis

Вообще говоря, есть три наиболее часто используемые стратегии экспирации:

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

Redis использует стратегию регулярного удаления + ленивое удаление.

трубопровод

Если у нас есть много команд для передачи Redis, первое решение — отправлять их одну за другой. Недостатки очевидны: каждая команда должна проходить через сеть, а производительность относительно низкая.Второе решение — использовать трубы. Прежде чем представить конвейер, давайте продемонстрируем одну вещь:

[root@localhost ~]# nc localhost 6379
set codebear hello
+OK
get codebear
$5
hello

Когда мы отправляем команды в Redis, нам не обязательно использовать клиент Redis. Нам нужно только подключиться к порту сервера Redis. Что касается значения вывода $5 после команды get codebear, мы не будем обсудить это здесь.

Как использовать конвейер, в конце концов, с приведенным выше основанием, на самом деле очень просто:

[root@localhost ~]# echo -e "set codebear hello1234 \n incr inttest \n set haha haha" | nc localhost 6379
+OK
:1
+OK

Команды разделяются \n, а затем отправляются в Redis через nc.

Вернемся к Канкану, удалось ли это:

[root@localhost ~]# nc localhost 6379
get inttest
$1
1
get codebear
$9
hello1234
get haha
$4
haha

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

опубликовать подписаться

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

Во-первых, нам нужно создать подписчика, который подписывается на канал с именем hello:

localhost:6379> subscribe hello
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "hello"
3) (integer) 1

Затем, чтобы создать издателя, отправьте сообщение на канал с именем hello:

localhost:6379> publish hello goodmorning
(integer) 1

Наконец, вернитесь к подписчику и обнаружите, что сообщение было получено:

1) "message"
2) "hello"
3) "goodmorning"

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

Это особенно просто?Когда я не знал о существовании ZooKeeper, я подумал, что могу использовать функцию публикации-подписки Redis в качестве центра конфигурации.

устранение памяти

Если память Redis заполнена и больше не может хранить новые данные, будет запущена стратегия Redis по ликвидации памяти.В redis.conf есть конфигурация, которая используется для настройки конкретной стратегии по ликвидации памяти:

maxmemory-policy volatile-lru

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

  • LRU: Наименее используемый алгоритм устранения: если ключ используется редко, он удаляется.
  • LFU: нет алгоритма недавнего устранения: если ключ не использовался в последнее время, он удаляется

Ниже приведена конкретная конфигурация, давайте рассмотрим ее по порядку:

  • volatile-lru: удалить ключ, который использовался реже всего, среди ключей с установленным сроком действия.
  • allkeys-lru: среди всех ключей удалить наименее использовавшийся ключ
  • volatile-lfu: удалить ключи, которые недавно не использовались в ключах с установленным сроком действия.
  • allkeys-lfu: во всех ключах удалить недавно неиспользованные ключи
  • volatile-random: случайным образом удалить ключ в ключе с установленным временем истечения срока действия
  • allkeys-random: удалить ключ случайным образом
  • volatile-ttl: в пространстве ключей с установленным сроком действия ключ с более ранним сроком действия удаляется первым
  • noeviction: боги и лошади ничего не делают, просто выбрасывают исключение

Какую конфигурацию следует использовать в производственной среде? Можно сказать, что ответы в Интернете сильно разнятся, но что может быть объединено, так это то, что выселение вообще не выбирается, поэтому в этом вопросе все еще используется ответ панацеи, совершенно правильный бессмысленный ответ: посмотрите на сцену.

Этот блог закончился, есть еще много вещей, которые не были упомянуты, давайте сначала отложим в сторону master-slave и кластер, версия Redis для одной машины также имеет персистентность, транзакции, протоколы, модули, GEO, hyperLogLog и т. д. ., и расширенные проблемы Redis — разбивка кеша, лавина кеша, проникновение кеша и т. д. не упоминались, и я пообщаюсь с вами позже.