Redis имеет 5 основных структур данных: строка, список, хеш, набор и zset. Это наиболее часто используемые структуры данных в повседневной разработке.Если вы полностью поймете эти пять структур данных, у вас будет половина знаний о приложении Redis.
string
Сначала начнем со строк. Строка представляет переменный байтовый массив.Мы инициализируем содержимое строки, получаем длину строки, получаем подстроку строки, перезаписываем содержимое подстроки строки и добавляем подстроку.
Строки Redis — это динамические строки, которые можно изменять. Внутренняя структура похожа на ArrayList в Java. В нем используется метод предварительного выделения избыточного пространства для сокращения частого выделения памяти. Как показано на рисунке, внутренняя структура — это текущая структура. фактическая выделенная емкость строки обычно выше, чем фактическая длина строки len. Если длина строки меньше 1 М, существующее пространство будет удвоено для расширения. Если длина строки превышает 1 М, за один раз будет расширено только 1 М. Следует отметить, что максимальная длина строки составляет 512M.
строка инициализацииНеобходимо указать «имя переменной» и «содержимое переменной».
> set ireader beijing.zhangyue.keji.gufen.youxian.gongsi
OK
получить содержимое строкиУкажите «имя переменной»
> get ireader
"beijing.zhangyue.keji.gufen.youxian.gongsi"
Получить длину строкиУкажите «имя переменной»
> strlen ireader
(integer) 42
получить подстрокуУкажите «имя переменной», а также начальную и конечную позицию [начало, конец]
> getrange ireader 28 34
"youxian"
покрывающая подстрокаУкажите «имя переменной» вместе с начальной позицией и целевой подстрокой.
> setrange ireader 28 wooxian
(integer) 42 # 返回长度
> get ireader
"beijing.zhangyue.keji.gufen.wooxian.gongsi"
добавить подстроку
> append ireader .hao
(integer) 46 # 返回长度
> get ireader
"beijing.zhangyue.keji.gufen.wooxian.gongsi.hao"
К сожалению, строки не предоставляют методов вставки строк и методов удаления подстрок.
прилавокЕсли содержимое строки является целым числом, строка также может использоваться в качестве счетчика.
> set ireader 42
OK
> get ireader
"42"
> incrby ireader 100
(integer) 142
> get ireader
"142"
> decrby ireader 100
(integer) 42
> get ireader
"42"
> incr ireader # 等价于incrby ireader 1
(integer) 43
> decr ireader # 等价于decrby ireader 1
(integer) 42
Счетчик имеет диапазон, он не может превышать Long.Max и не может быть ниже Long.MIN.
> set ireader 9223372036854775807
OK
> incr ireader
(error) ERR increment or decrement would overflow
> set ireader -9223372036854775808
OK
> decr ireader
(error) ERR increment or decrement would overflow
Срок действия и удалениеСтрока может быть активно удалена с помощью команды del, а время истечения может быть установлено с помощью команды expire, и она будет автоматически удалена в конце, что является пассивным удалением. Время жизни строки можно получить с помощью директивы ttl.
> expire ireader 60
(integer) 1 # 1表示设置成功,0表示变量ireader不存在
> ttl ireader
(integer) 50 # 还有50秒的寿命,返回-2表示变量不存在,-1表示没有设置过期时间
> del ireader
(integer) 1 # 删除成功返回1
> get ireader
(nil) # 变量ireader没有了
list
Redis называет структуру данных списка списком, а не массивом, потому что структура хранения списка использует связанный список вместо массива, а связанный список также является двусвязным списком. Поскольку это связанный список, производительность случайного позиционирования низкая, а производительность сквозной вставки и удаления лучше. Если длина списка списка очень длинная, мы должны обратить внимание на временную сложность связанных операций связанного списка при его использовании.
отрицательный индексИспользуйте натуральные числа для позиций элементов связанного списка0,1,2,....n-1
Указывает, что можно использовать и отрицательные числа.-1,-2,...-n
Представлять,-1
означает «последний к последнему»,-2
означает «предпоследний», тогда-n
Он представляет первый элемент, и соответствующий нижний индекс0
.
очередь/стекСвязанный список может добавлять и удалять элементы из начала и конца списка.В сочетании с четырьмя инструкциями rpush/rpop/lpush/lpop связанный список можно использовать как очередь или стек, и это можно сделать слева направо.
# 右进左出
> rpush ireader go
(integer) 1
> rpush ireader java python
(integer) 3
> lpop ireader
"go"
> lpop ireader
"java"
> lpop ireader
"python"
# 左进右出
> lpush ireader go java python
(integer) 3
> rpop ireader
"go"
...
# 右进右出
> rpush ireader go java python
(integer) 3
> rpop ireader
"python"
...
# 左进左出
> lpush ireader go java python
(integer) 3
> lpop ireader
"python"
...
В повседневных приложениях списки часто используются в качестве асинхронных очередей.
длинаИспользуйте команду llen, чтобы получить длину связанного списка.
> rpush ireader go java python
(integer) 3
> llen ireader
(integer) 3
случайное чтениеВы можете использовать команду lindex для доступа к элементу в указанной позиции, использовать команду lrange для получения списка дочерних элементов связанного списка и указать параметры начального и конечного индексов.
> rpush ireader go java python
(integer) 3
> lindex ireader 1
"java"
> lrange ireader 0 2
1) "go"
2) "java"
3) "python"
> lrange ireader 0 -1 # -1表示倒数第一
1) "go"
2) "java"
3) "python"
При использовании lrange для получения всех элементов вам необходимо указать end_index. Если нет отрицательного индекса, вам нужно сначала получить длину с помощью команды llen, а затем вы можете получить значение end_index. С отрицательным индексом вы можете использовать -1 вместо end_index для достижения того же эффекта.
изменить элементИспользуйте директиву lset для изменения элемента в указанной позиции.
> rpush ireader go java python
(integer) 3
> lset ireader 1 javascript
OK
> lrange ireader 0 -1
1) "go"
2) "javascript"
3) "python"
вставить элементДля вставки элементов в середину списка используйте инструкцию linsert.Опытные программисты знают, что при вставке элементов мы часто не знаем, вставлять до или после указанной позиции, поэтому антирез добавляет параметр направления перед в инструкции linsert./ после, чтобы показать индикацию до и после введения. Но что неожиданно, так это то, что инструкция linsert вставляется не указанием позиции, а указанием конкретного значения. Это связано с тем, что в распределенной среде элементы списка всегда часто меняются, а это означает, что индекс элемента, вычисленный в предыдущий момент, может не совпадать с индексом, который вы ожидаете в следующий момент.
> rpush ireader go java python
(integer) 3
> linsert ireader before java ruby
(integer) 4
> lrange ireader 0 -1
1) "go"
2) "ruby"
3) "java"
4) "python"
Пока в практических приложениях заданного сценария применения вставки я не нашел.
удалить элементОперация удаления списка не определяет элемент по указанию индекса, нужно указать максимальное количество удалений и значение элемента.
> rpush ireader go java python
(integer) 3
> lrem ireader 1 java
(integer) 1
> lrange ireader 0 -1
1) "go"
2) "python"
Список фиксированной длиныВ сценариях практического применения мы иногда сталкиваемся с требованием «списка фиксированной длины». Например, чтобы отобразить список выигравших имен пользователей в режиме реального времени в виде бегущей строки, из-за того, что выигравших пользователей слишком много, число, которое может быть отображено, обычно не превышает 100, поэтому список фиксированной длины будет используется здесь. Команда для поддержки списка фиксированной длины — ltrim, и необходимо указать два параметра, start и end, указывающие, что диапазон индексов списка должен быть сохранен, а все элементы за пределами диапазона будут удалены.
> rpush ireader go java python javascript ruby erlang rust cpp
(integer) 8
> ltrim ireader -3 -1
OK
> lrange ireader 0 -1
1) "erlang"
2) "rust"
3) "cpp"
Если реальный нижний индекс, соответствующий концу указанного параметра, меньше начала, эффект эквивалентен команде del, потому что такой параметр указывает, что диапазон нижнего индекса элемента списка должен оставаться пустым.
быстрый список
Если вы пойдете глубже, вы обнаружите, что базовое хранилище Redis — это не простой связанный список, а структура, называемая быстрым списком. Во-первых, когда элементов списка мало, будет использоваться непрерывное хранилище памяти, эта структура — ziplist, то есть сжатый список. Он хранит все элементы рядом друг с другом и выделяет непрерывный участок памяти. Когда объем данных относительно велик, он будет изменен на быстрый список. Поскольку дополнительное место для указателя, необходимое обычному связанному списку, слишком велико, оно будет занимать место впустую. Например, в этом списке хранятся только данные типа int, а структура также требует двух дополнительных указателей prev и next. Таким образом, Redis объединяет связанный список и ziplist для формирования быстрого списка. То есть несколько ziplists используются последовательно с использованием двусторонних указателей. Это не только обеспечивает высокую производительность вставки и удаления, но и не требует слишком большой избыточности пространства.hash
Hash эквивалентен HashMap на языке Java или dict на языке Python. С точки зрения структуры реализации он использует двумерную структуру. Первое измерение — это массив, а второе измерение — связанный список. Ключ и значение хэш-содержимое хранится в связанном списке.Массив Он хранит указатель на начало связанного списка. При поиске элемента по ключу сначала вычислите хэш-код ключа, затем используйте хэш-код для деления по модулю длины массива, чтобы найти заголовок связанного списка, а затем просмотрите связанный список, чтобы получить соответствующее значение. связанный список должен генерироваться. Элементы «хеш-коллизии» связаны друг с другом. Разработчики языка Java почувствуют себя очень знакомыми, потому что такая структура и HashMap ничем не отличаются. Длина массива первого измерения хеша также равна 2^n.
добавить элементыВы можете использовать hset для добавления одной пары ключ-значение за раз или вы можете использовать hmset для добавления нескольких пар ключ-значение за раз.
> hset ireader go fast
(integer) 1
> hmset ireader java fast python slow
OK
получить элементВы можете найти значение, соответствующее определенному ключу, с помощью hget, получить значение, соответствующее нескольким ключам, с помощью hmget, использовать hgetall для получения всех пар ключ-значение и использовать hkeys и hvals для получения всех списков ключей и списков значений соответственно. Эти операции аналогичны интерфейсу Map языка Java.
> hmset ireader go fast java fast python slow
OK
> hget ireader go
"fast"
> hmget ireader go python
1) "fast"
2) "slow"
> hgetall ireader
1) "go"
2) "fast"
3) "java"
4) "fast"
5) "python"
6) "slow"
> hkeys ireader
1) "go"
2) "java"
3) "python"
> hvals ireader
1) "fast"
2) "fast"
3) "slow"
удалить элементВы можете использовать hdel для удаления указанного ключа, hdel поддерживает одновременное удаление нескольких ключей
> hmset ireader go fast java fast python slow
OK
> hdel ireader go
(integer) 1
> hdel ireader java python
(integer) 2
Проверить, существует ли элементОбычно мы используем hget, чтобы узнать, является ли значение, соответствующее ключу, пустым, пока соответствующий элемент не существует.Однако, если длина строки значения особенно велика, немного расточительно судить о том, существует ли элемент или нет в этом В настоящее время вы можете использовать директиву hexists.
> hmset ireader go fast java fast python slow
OK
> hexists ireader go
(integer) 1
прилавокХэш-структуру также можно использовать в качестве счетчика, который можно использовать как независимый счетчик для каждого внутреннего ключа. Если значение не является целым числом, вызов инструкции hincrby завершится ошибкой.
> hincrby ireader go 1
(integer) 1
> hincrby ireader python 4
(integer) 4
> hincrby ireader java 4
(integer) 4
> hgetall ireader
1) "go"
2) "1"
3) "python"
4) "4"
5) "java"
6) "4"
> hset ireader rust good
(integer) 1
> hincrby ireader rust 1
(error) ERR hash value is not an integer
РасширениеКогда элементы внутри хэша переполнены (часты коллизии хэшей), требуется расширение. Расширение необходимо применить для нового массива двойного размера, а затем переназначить все пары ключ-значение связанному списку, соответствующему новому индексу массива (перефразировать). Если хеш-структура большая, например, миллионы пар ключ-значение, то полный процесс перехеширования займет много времени. Это немного напрягает для однопоточного Redis. Поэтому Redis использует прогрессивную схему перефразирования. Он будет одновременно поддерживать две новые и старые хэш-структуры и постепенно переносить элементы старой структуры в новую структуру в последующих задачах синхронизации, а также читать и записывать инструкции хеш-структуры. Таким образом можно избежать заклинивания резьбы, вызванного расширением.
сокращатьХэш-структура Redis не только расширяется, но и сжимается, с этой точки зрения она более мощная, чем HashMap в Java, которую можно только расширять. Принцип сжатия такой же, как и у расширения, за исключением того, что размер нового массива в два раза меньше размера старого массива.
set
Все Java-программисты знают, что внутренняя реализация HashSet использует HashMap, но все значения указывают на один и тот же объект. То же самое справедливо и для структуры set Redis, которая также использует внутреннюю хеш-структуру, и все значения указывают на одно и то же внутреннее значение.
добавить элементыМожно добавить несколько элементов одновременно
> sadd ireader go java python
(integer) 3
прочитать элементИспользуйте smembers для вывода списка всех элементов, используйте scar для получения длины набора, используйте srandmember для получения случайных элементов count, если параметр count не указан, по умолчанию используется значение 1.
> sadd ireader go java python
(integer) 3
> smembers ireader
1) "java"
2) "python"
3) "go"
> scard ireader
(integer) 3
> srandmember ireader
"java"
удалить элементИспользуйте srem для удаления одного или нескольких элементов и используйте spop для удаления случайного элемента.
> sadd ireader go java python rust erlang
(integer) 5
> srem ireader go java
(integer) 2
> spop ireader
"erlang"
Проверить, существует ли элементС помощью директивы sismember можно получить только один элемент.
> sadd ireader go java python rust erlang
(integer) 5
> sismember ireader rust
(integer) 1
> sismember ireader javascript
(integer) 0
sortedset
SortedSet (zset) — это особая структура данных, предоставляемая Redis, с одной стороны, она эквивалентна структуре данных Java.Map<String, Double>
, вы можете присвоить вес каждому значению элементаscore
, а с другой стороны аналогичноTreeSet
, внутренние элементы будут отсортированы в соответствии с оценкой веса, вы можете получить рейтинг каждого элемента, а также вы можете получить список элементов в диапазоне оценки.
Базовая реализация zset использует две структуры данных, первая — это хэш, а вторая — список переходов.Функция хеша заключается в том, чтобы связать значение элемента и весовую оценку, чтобы гарантировать уникальность значения элемента.Соответствующее значение оценки можно найти через значение элемента. Назначение списка пропуска — отсортировать значение элемента и получить список элементов в соответствии с диапазоном оценки.
добавить элементыС помощью инструкции zadd можно добавить одну или несколько пар значение/оценка, и оценка будет помещена впереди.
> zadd ireader 4.0 python
(integer) 1
> zadd ireader 4.0 java 1.0 go
(integer) 2
длинаКоличество элементов zset можно получить командой zcard
> zcard ireader
(integer) 3
удалить элементЭлементы в zset можно удалить с помощью инструкции zrem, а несколько элементов можно удалить одновременно.
> zrem ireader go python
(integer) 2
прилавокКак и хеш-структура, zset также может использоваться в качестве счетчика.
> zadd ireader 4.0 python 4.0 java 1.0 go
(integer) 3
> zincrby ireader 1.0 python
"5"
Получить рейтинги и баллыПолучите вес указанного элемента с помощью команды zscore, получите прямой рейтинг указанного элемента с помощью команды zrank и получите обратный рейтинг [последнего] указанного элемента с помощью команды zrevrank. Положительное направление — от меньшего к большему, отрицательное — от большего к меньшему.
> zscore ireader python
"5"
> zrank ireader go # 分数低的排名考前,rank值小
(integer) 0
> zrank ireader java
(integer) 1
> zrank ireader python
(integer) 2
> zrevrank ireader python
(integer) 0
Получить список элементов на основе диапазона ранговИспользуйте команду zrange, чтобы указать параметр диапазона ранжирования, чтобы получить соответствующий список элементов, и укажите параметр withscores, чтобы получить общий вес элементов. Получить список элементов [reciprocal] директивой zrevrange по отрицательному рангу. Положительное направление — от меньшего к большему, отрицательное — от большего к меньшему.
> zrange ireader 0 -1 # 获取所有元素
1) "go"
2) "java"
3) "python"
> zrange ireader 0 -1 withscores
1) "go"
2) "1"
3) "java"
4) "4"
5) "python"
6) "5"
> zrevrange ireader 0 -1 withscores
1) "python"
2) "5"
3) "java"
4) "4"
5) "go"
6) "1"
Получить список на основе диапазона балловИспользуйте команду zrangebyscore, чтобы указать диапазон оценок для получения соответствующего списка элементов. Получите список инвертированных элементов с помощью команды zrevrangebyscore. Положительное направление — от меньшего к большему, отрицательное — от большего к меньшему. параметр-inf
означает отрицательную бесконечность,+inf
представляет собой положительную бесконечность.
> zrangebyscore ireader 0 5
1) "go"
2) "java"
3) "python"
> zrangebyscore ireader -inf +inf withscores
1) "go"
2) "1"
3) "java"
4) "4"
5) "python"
6) "5"
> zrevrangebyscore ireader +inf -inf withscores # 注意正负反过来了
1) "python"
2) "5"
3) "java"
4) "4"
5) "go"
6) "1"
Удалить список элементов на основе диапазонаНесколько элементов могут быть удалены одновременно либо по диапазону рангов, либо по диапазону оценок.
> zremrangebyrank ireader 0 1
(integer) 2 # 删掉了2个元素
> zadd ireader 4.0 java 1.0 go
(integer) 2
> zremrangebyscore ireader -inf 4
(integer) 2
> zrange ireader 0 -1
1) "python"
список прыжковФункция сортировки внутри zset реализуется через структуру данных «список переходов», которая имеет особую и сложную структуру. Читатели должны быть мысленно готовы к глубине этого фрагмента контента.
Поскольку zset должен поддерживать случайную вставку и удаление, использовать массив для его представления непросто. Давайте сначала рассмотрим структуру обычного связанного списка.
Нам нужно, чтобы этот список был отсортирован по значению оценки. Это означает, что когда необходимо вставить новый элемент, он должен найти точку вставки в определенной позиции, чтобы связанный список можно было поддерживать в порядке. Обычно мы находим точку вставки с помощью бинарного поиска, но объект бинарного поиска должен быть массивом.Только массив может поддерживать быстрое позиционирование местоположения, а связанный список не может этого сделать.Что нам делать?
Подумайте о стартапе, в котором вначале всего несколько человек, и все в команде равны, все соучредители. По мере роста компании количество людей постепенно увеличивается, а стоимость командного общения увеличивается. В это время будет введена система лидера команды для разделения команды. В каждой команде будет лидер. Когда собрание проводится, оно делится на команды, и несколько руководителей групп также имеют свои собственные договоренности о встрече. По мере дальнейшего расширения компании ей необходимо добавить еще один уровень — департамент, и каждый департамент будет избирать представителя из списка руководителей групп в качестве министра. У министров также будут собственные договоренности о встречах на высоком уровне.
Список пропуска похож на иерархию такого типа, и все элементы нижнего слоя будут связаны друг с другом. Затем выберите представителя через каждые несколько элементов, а затем используйте другой уровень указателей, чтобы связать этих представителей вместе. Затем выберите из этих представителей представителей второго уровня, а затем свяжите их вместе. В итоге образуется пирамидальная структура.
Подумайте о расположении вашего родного города на карте мира: Азия-->Китай->Провинция Аньхой->Город Аньцин->Уезд Цзунъян->Город Тангоу->Деревня Тяньцзянь->xxxx, это тоже похожая структура.
Причина, по которой «список переходов» «перескакивает», заключается в том, что внутренние элементы могут «играть несколько ролей», например элемент в середине рисунка выше, который одновременно находится в слоях L0, L1 и L2, и может быстро «прыгать между разными уровнями».
При позиционировании точки вставки сначала позиционируйте ее на верхнем уровне, затем переходите на следующий уровень позиционирования, пока нижний слой не найдет подходящую позицию и не вставит новый элемент. Вы спросите, откуда у вновь вставленного элемента возможность «многозадачности»?
Список переходов использует случайную стратегию, чтобы определить, на какой уровень может перейти новый элемент.Во-первых, уровень L0 должен быть 100%, уровень L1 имеет только вероятность 50%, уровень L2 имеет только вероятность 25%, а слой L3 имеет вероятность всего 12,5% Случайно до самого верхнего слоя L31. Подавляющее большинство элементов не проходят дальше нескольких слоев, и лишь немногие могут углубиться в верхний слой. Чем больше элементов в списке, тем глубже вы сможете пройти, и тем больше вероятность достижения верхнего уровня.
Это вполне справедливо.Сможете ли вы войти в центр, зависит не от борьбы с отцом, а от удачи.
Сканируйте в WeChat и подпишитесь на общедоступный аккаунт Code Cave, чтобы читать больше интересных статей.