Игра с Redis — Как эффективно получить доступ к массивным данным в Redis

Redis

1. Введение

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

Есть ли у вас следующие вопросы в реальном проекте?

  • Как получить доступ к массивным данным в Redis, не затрагивая другие запросы на доступ к Redis?
  • В Redis есть миллионы/десятки миллионов данных, как эффективно получить к ним доступ?
  • Объем данных в Redis слишком велик, как обеспечить быстрый доступ, не вызывая простоя сервиса?

Вышеупомянутые вопросы также часто задают в интервью Redis.

Серия статей "Игра с Redis" в основном описывает базовые, промежуточные и расширенные приложения Redis. Статьи основаны на Redis5.0.4+. Добро пожаловать в CSDN, учетную запись подписки, открытый исходный код Китая, Nuggets и другие платформы для поиска [zxiaofan] для просмотра серии статей.


2. Думай

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

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

Q2: Используйте команду универсальных ключей для запроса любых данных, которые вы хотите проверить?

A2: Просто поиграйте с десятками тысяч данных на своем компьютере, используйте команду ключей онлайн, извините? Ты хочешь собраться и уйти.
++ «Инженер php компании выполнил ключи redis *, что привело к отключению базы данных! В этом году в техническом отделе произошло 2 крупных несчастных случая уровня PO, что привело к потере 4 миллионов юаней в фондах компании. ++ Эта новость еще свежа в памяти, и звонят тревожные звоночки!

Q3: Правильный способ работы с массивными данными в Redis

A3: Используйте команды серии SCAN (SCAN, SSCAN, HSCAN, ZSCAN) для завершения итерации данных.

  Что вы знаете о Redis [команды серии SCAN]?

3. Подробное объяснение команд серии SCAN

  Команды серии SCAN относятся не только к командам SCAN, но также включают SSCAN, HSCAN и ZSCAN.Объекты каждой команды разные, но использование и функции в основном одинаковы.

3.1 Сравнительный анализ команд серии СКАН

  • курсор: курсор итерации;
  • ПОИСКПОЗ: режим сопоставления данных;
  • COUNT: количество возвращенных итераций;
Заказ Функции параметр возвращаемое значение
SCAN Итерировать БД на основе курсора cursor [MATCH pattern] [COUNT count] Возвращает массив, первое значение — это курсор для следующей итерации (64-битный без знака), а второе значение — это список элементов (список ключей)
SSCAN Итерирует наборы на основе курсора key cursor [MATCH pattern] [COUNT count] Возвращает массив, первое значение — это курсор для следующей итерации (64-битный без знака), а второе значение — это список элементов.
HSCAN Итерирует хэши на основе курсора key cursor [MATCH pattern] [COUNT count] Возвращает массив, второе значение представляет собой список значений полей.
ZSCAN Итерирует ZSet на основе курсора key cursor [MATCH pattern] [COUNT count] Возвращает массив, второе значение представляет собой список оценок участников.

3.2 Меры предосторожности для команд серии SCAN

  • Параметры SCAN не имеют ключа, т.к. итеративным объектом являются данные в БД;
  • Возвращаемые значения — все массивы, а первое значение — курсор следующей итерации;
  • Временная сложность: O(1) на запрос, O(N) требуется для выполнения всех итераций, где N — количество элементов;
  • Доступные версии: версия >= 2.8.0;

3.3 Подробное объяснение команд серии SCAN

3.3.1. Инкрементная итерация, которую можно использовать в производственной среде

  • Это не полная итерация, как KEYS и SMEMBERS, и она может блокировать службу на долгое время при выполнении на большом наборе;

3.3.2 Точные результаты не гарантируются

  • SMEMBERS может возвращать элементы всего набора, а команды инкрементной итерации, такие как SCAN, могут изменять элементы в процессе итерации, поэтому нельзя гарантировать точный результат возврата;

3.3.3, на основе итерации курсора

  • SCAN основан на итерации курсора, и каждый запрос возвращает курсор для использования в следующий раз;
  • Курсор курсора может быть больше, чем общее количество элементов БД, и может быть отрицательным;
  • Курсор ошибки: использование прерывистого (не возвращаемого итерацией), отрицательного числа, вне диапазона или другого недопустимого курсора, итерация не сообщит об ошибке, может привести к неопределенному поведению (не может гарантировать точность);

3.3.4 Маркер конца итерации

  • Курсор, возвращаемый SCAN, не обязательно увеличивается, и количество элементов, возвращаемых определенной итерацией, может быть равно 0;
  • Возвращаемый список элементов пуст, что не означает конец итерации;
  • Полная итерация: курсор SCAN начинается с 0, а курсор возврата заканчивается с 0;
  • Состояние итерации контролируется возвращаемым курсором. Итерации могут выполняться одновременно, итерации могут быть прекращены в любое время;

3.3.5 Итерационная целостность

  • Данные, которые существуют с начала обхода до конца обхода, должны возвращаться итеративно;
  • Один и тот же элемент может возвращаться несколько раз, и приложение должно выполнять дедупликацию данных;
  • Элементы, добавленные или удаленные во время итерации, могут быть возвращены или не возвращены;
  • Когда типом данных являются наборы (состоящие из целых чисел), хэши, отсортированные наборы и набор небольшой, итерация вернет данные всего набора, независимо от количества;
  • Гарантия окончания итерации: элементы добавляются со скоростью, меньшей скорости итерации.

3.3.6, почему иногда итерация сразу возвращает всю коллекцию

  • Если базовой структурой данных является хеш, а объем данных невелик, Redis использует стратегию оптимизации памяти и использует кодирование с компактным сжатием. В этот момент операция SCAN не возвращает осмысленный курсор, а выполняет итерацию по всей коллекции;
  • Небольшой объем данных? См. официальные инструкции по оптимизации памяти.

3.3.7, описание количества параметров

  • Значение счетчика по умолчанию равно 10;
  • Когда набор данных большой, если совпадение не используется, возвращаемый элемент равен количеству или немного больше, чем количество;
  • Значение параметра count для каждой итерации может быть разным, если используется курсор, возвращенный предыдущей итерацией;

3.3.8, описание соответствия параметров

  • Подобно шаблону ключей;
  • Операция ПОИСКПОЗ выполняется в течение периода от получения данных до возврата элементов, поэтому, если совпадающих элементов меньше, список элементов, возвращаемых несколькими итерациями, может быть пустым;

4. Пример команды серии SCAN

4.1 Пример сканирования

   Подробную информацию см. в разделе «5.2, Некоторые вопросы и ответы».

4.2, пример SSCAN

// SSCAN示例 @zxiaofan
127.0.0.1:6378> SADD sscantest sscantest:1 1 sscantest:2 2 sscantest:3 3 sscantest:4 4 sscantest:1a 1a sscantest:2a 2a sscantest:1ab 1ab sscantest:a1 a1 sscantest:aa1 aa1 
(integer) 0
// MATCH ?:无匹配数据
127.0.0.1:6378> SSCAN sscantest 0 MATCH ? COUNT 1
1) "24"
2) (empty list or set)
127.0.0.1:6378> SSCAN sscantest 24 MATCH ? COUNT 1
1) "20"
2) (empty list or set)
127.0.0.1:6378> SSCAN sscantest 0 MATCH * COUNT 1
1) "24"
2) 1) "sscantest:3"
   2) "sscantest:2a"
127.0.0.1:6378> SSCAN sscantest 24 MATCH * COUNT 1
1) "20"
2) 1) "a1"

4.3, пример HSCAN

// HSCAN示例 @zxiaofan
127.0.0.1:6378> HMSET hscantest hscantest:1 1 hscantest:2 2 hscantest:3 3 hscantest:4 4 hscantest:1a 1a hscantest:2a 2a hscantest:1ab 1ab hscantest:a1 a1 hscantest:aa1 aa1 
OK
127.0.0.1:6378> HSCAN hscantest 0 MATCH hscantest*a COUNT 20
1) "0"
2) 1) "hscantest:1a"
   2) "1a"
   3) "hscantest:2a"
   4) "2a"
127.0.0.1:6378> HSCAN hscantest 0 MATCH hscantest*a COUNT 2
1) "0"
2) 1) "hscantest:1a"
   2) "1a"
   3) "hscantest:2a"
   4) "2a"
127.0.0.1:6378> 

   Как видно из примера HSCAN, возвращаются все совпадающие результаты, даже если параметр count равен 2. Об этом упоминалось ранее, когда объем данных невелик, возвращайте все данные напрямую.

4.4 Пример ZSCAN

// ZSCAN示例 @zxiaofan
// 【移除】并弹出count个分数最大的元素,count默认为1
127.0.0.1:6378> ZPOPMAX zscantest 20
 1) "sscantest:1ab"
 2) "6"
 3) "sscantest:2a"
 4) "5"
 5) "sscantest:1a"
 6) "4"
 7) "sscantest:3"
 8) "3"
 9) "zscantest:1"
10) "2"
11) "sscantest:2"
12) "2"
13) "test1"
14) "1"
15) "sscantest:1"
16) "1"
127.0.0.1:6378> ZPOPMAX zscantest 20
(empty list or set)
127.0.0.1:6378> ZADD zscantest 1 zscantest:1 2 zscantest:2 3 zscantest:3 4 zscantest:1a 5 zscantest:2a 6 zscantest:1ab 7 zscantest:a1 8 zscantest:aa1
(integer) 8
// NX:不存在才添加;CH:返回被改变(含新增)的元素个数
127.0.0.1:6378> ZADD zscantest NX CH 1 test1 2 zscantest:1
(integer) 1
127.0.0.1:6378> ZSCAN zscantest 0 MATCH *a COUNT 5
1) "0"
2) 1) "zscantest:1a"
   2) "4"
   3) "zscantest:2a"
   4) "5"
127.0.0.1:6378> 

5. Резюме

5.1 Посмотрите, на сколько вопросов вы сможете ответить во время интервью

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

5.2 Ответы на некоторые вопросы

5.2.1 Если возвращенные данные SCAN пусты, итерация завершена?

// SCAN返回数据为空就是迭代结束了吗? @zxiaofan
127.0.0.1:6378> keys k?
1) "k1"
2) "k2"
127.0.0.1:6378> SCAN 0 MATCH k?
1) "88"
2) (empty list or set)
127.0.0.1:6378> SCAN 88 MATCH k?
1) "34"
2) 1) "k1"
127.0.0.1:6378> SCAN 34 MATCH k?
1) "122"
2) (empty list or set)
127.0.0.1:6378> SCAN 122 MATCH k?
1) "14"
2) (empty list or set)
127.0.0.1:6378> SCAN 14 MATCH k?
1) "33"
2) (empty list or set)
127.0.0.1:6378> SCAN 33 MATCH k?
1) "53"
2) (empty list or set)
127.0.0.1:6378> SCAN 53 MATCH k?
1) "93"
2) (empty list or set)
127.0.0.1:6378> SCAN 93 MATCH k?
1) "107"
2) 1) "k2"
127.0.0.1:6378> SCAN 107 MATCH k?
1) "79"
2) (empty list or set)
127.0.0.1:6378> SCAN 79 MATCH k?
1) "0"
2) (empty list or set)
127.0.0.1:6378> 

Глядя на приведенный выше пример, данные, соответствующие "k?", на самом деле имеют 2 части "k1" и "k2". В течение всего процесса итерации возвращаемые данные много раз пусты, но итерация не закончилась (потому что " k1", "k2" не возвращают все итерации).
Следовательно, только когда курсор возвращается к 0, это может означать, что итерация завершена.

5.2.2 Если параметр курсора первой итерации не равен 0, может ли быть достигнута полная итерация?

// 如果首次迭代cursor参数不是0,能实现完整迭代吗? @zxiaofan
127.0.0.1:6378> keys k?
1) "k1"
2) "k2"
127.0.0.1:6378> SCAN 66 MATCH k?
1) "122"
2) (empty list or set)
127.0.0.1:6378> SCAN 122 MATCH k?
1) "14"
2) (empty list or set)
127.0.0.1:6378> SCAN 14 MATCH k?
1) "33"
2) (empty list or set)
127.0.0.1:6378> SCAN 33 MATCH k?
1) "53"
2) (empty list or set)
127.0.0.1:6378> SCAN 53 MATCH k?
1) "93"
2) (empty list or set)
127.0.0.1:6378> SCAN 93 MATCH k?
1) "107"
2) 1) "k2"
127.0.0.1:6378> SCAN 107 MATCH k?
1) "79"
2) (empty list or set)
127.0.0.1:6378> SCAN 79 MATCH k?
1) "0"
2) (empty list or set)
127.0.0.1:6378> 

Глядя на приведенный выше пример, данные, соответствующие «k?», на самом деле имеют 2 части «k1» и «k2».Когда курсор равен 66 в первый раз, когда SCAN использует, мы можем обнаружить, что после многих итераций, когда курсор возвращается до 0, "k1" Это никогда не повторялось.
Следовательно, если параметр курсора первой итерации не равен 0, полная итерация не может быть достигнута.

  Полная итерация должна начинаться с курсора 0 и заканчиваться курсором 0.

6. Постскриптум

  В этой статье проводится подробный сравнительный анализ команд Redis серии SCAN и практических примеров, а также систематизируются часто задаваемые вопросы в интервью. Рекомендуется, чтобы учащиеся, прочитавшие эту статью, добились лучших результатов на практике. Добро пожаловать в серию статей @zxiaofan «Веселье с Redis», чтобы расти вместе.
На сколько вопросов интервью, упомянутых в разделе 5, можно ответить сейчас?


Удачи тебе!

Вся жизнь состоит из выбора!
В будущем вы обязательно будете благодарны себе за то, что усердно трудитесь сейчас!
CSDN】【GitHub】【OSCHINA】【Наггетс】【Публичный аккаунт WeChat