Проблема с использованием команды Redis HSCAN

Redis

помещение

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

USER_ID:1
   ORDER_ID:ORDER_XX: {"amount": "100","orderId":"ORDER_XX"}
   ORDER_ID:ORDER_YY: {"amount": "200","orderId":"ORDER_YY"}

ЧувствоватьHashСценарии, где тип полностью соответствует требованиям. Тогда считайте само собой разумеющимся, чтобы рассмотреть возможность использованияHSCANКоманда разбита на страницы, что вызывает проблемы, возникающие позже.

Команды SCAN и HSCAN

SCANКоманда выглядит следующим образом:

SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
// 返回值如下:
// 1. cursor,数值类型,下一轮的起始游标值,0代表遍历结束
// 2. 遍历的结果集合,列表

SCANкомандоватьRedisДобавлено в версии 2.8.0, временная сложность рассчитывается следующим образом: Временная сложность каждого раунда обхода равнаO(1), обходятся все элементы до тех пор, пока курсорcursorВременная сложность возврата 0 равнаO(N)Nколичество элементов в коллекции.SCANдля всегоDatabaseПоследовательный обход всех ключей внутри, он не будет блокироватьсяRedis, то есть использоватьSCANПроизводительность команды, проходящей через KEY, будет лучше, чемKEY *Заказ. заHashтип имеет производную командуHSCANпосвященный обходуHashТип и связанные с ним свойства (Field) поля:

HSCAN key cursor [MATCH pattern] [COUNT count]
// 返回值如下:
// 1. cursor,数值类型,下一轮的起始游标值,0代表遍历结束
// 2. 遍历的结果集合,是一个映射

Автор в свое время не проверял подробноRedisОфициальная документация, считать само собой разумеющимся, чтоHashТип разбиения по страницам прост:

// 第一页
HSCAN USER_ID:1 0 COUNT 1    <= 这里认为返回的游标值为1
// 第二页
HSCAN USER_ID:1 1 COUNT 1    <= 这里认为返回的游标值为0,结束迭代

На самом деле результат выполнения таков:

HSCAN USER_ID:1 0 COUNT 1

// 结果
0 
 ORDER_ID:ORDER_XX
 {"amount": "100","orderId":"ORDER_XX"}
 ORDER_ID:ORDER_YY
 {"amount": "200","orderId":"ORDER_YY"}

То есть в первом раунде обхода все соответствующие KEYField-Valueвернулся в полном объеме. Автор пытался увеличить хэш set KEY=USER_ID:1элементы внутри, но когда объем данных относительно велик, это все равно не дает ожидаемого эффекта разбиения по страницам; с другой стороны, попробуйте изменитьCOUNTзначение, найденное в любом случае для измененияCOUNTНи одно из значений не влияет на результат обхода (то есть все результаты возвращаются в первой итерации). В случае недоумения вы можете только внимательно прочитать официальную документацию, чтобы найти решение. существуетSCANкомандаCOUNTПричина указана в описании объекта:

Простой перевод для понимания:

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

  1. COUNTЗначение по умолчанию — 10.
  2. при обходе целиSet,Hash,Sorted SetилиKeyПространство достаточно велико, чтобы его можно было представить с помощью хеш-таблицы, и оно не используетMATCHНа основании свойств,RedisСервер вернетсяCOUNTили чемCOUNTБольшой результирующий набор элементов обхода.
  3. При обходе содержит толькоIntegerстоило тогоSetсборник (также известный какintsets),илиziplistsкодированный типHashилиSorted Setмножества (указывая на то, что пространство, занимаемое элементами в этих множествах, достаточно мало), тоSCANКоманда вернет все элементы в коллекции, игнорируя ее напрямуюCOUNTАтрибуты.

Обратите внимание на пункт 3, это вHashиспользуется в коллекцияхHSCANЗаказCOUNTОсновная причина отказа собственности.RedisЕсть два иHashтипziplistСоответствующие значения конфигурации для кодирования:

hash-max-ziplist-entries 512
hash-max-ziplist-value 64

Когда выполняется одно из следующих двух условий,HashКодировка коллекции будет заданаziplistпревратится вdict:

  • когдаHashэлементы данных в коллекции (т.е.Field-Valueсправа), когда число превышает 512.
  • когдаHashлюбой вставленный в коллекциюField-ValueправильноValueДлина превышает 64.

когдаHashКодировка коллекции будет заданаziplistпревратится вdict,RedisзаHashОптимизация использования пространства памяти типа эквивалентна неудаче, и она понижена до кодировки словарного типа, которая потребляет больше памяти.HSCANЗаказCOUNTсвойства вступят в силу.

Верификация случая

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

// 70个X
HSET USER_ID:2 ORDER_ID:ORDER_XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX   
// 70个Y
HSET USER_ID:2 ORDER_ID:ORDER_YYY YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

Затем приступайте к тестированиюHSCANЗаказ:

// 查看编码
object encoding USER_ID:2
// 编码结果
hashtable

// 第一轮迭代
HSCAN USER_ID:2 0 COUNT 1
// 第一轮迭代返回结果
2 
 ORDER_ID:ORDER_YYY
 YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

// 第二轮迭代 
HSCAN USER_ID:2 2 COUNT 1
0 
 ORDER_ID:ORDER_XXX
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

В тестовом примере длина двух значений намеренно установлена ​​равной 70, что больше 64, то есть пустьHashКоллекция превращается вdict(hashtable)тип такой, чтоCOUNTсвойства вступают в силу. Однако такой подход заключается в отказе отRedisзаHashОптимизация памяти для коллекций. Очевидно,HSCANКоманды естественно рассчитаны не на листание данных, а на пошаговую итерацию (т.е. если итерируемая коллекция большая, то не заблокируетRedisСлужить). Так что я, наконец, отказался от использованияHSCANкоманда для поиска других инструментов, более подходящих для запросов на подкачку данныхRedisЗаказ.

резюме

Через этот простой случай наступления на яму автор приобрел некоторый опыт:

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

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

Приложение

(Конец этой статьи e-a-20190812 c-1-d)

Технический публичный аккаунт ("Throwable Digest"), который время от времени выкладывает оригинальные технические статьи автора (никогда не занимайтесь плагиатом и не перепечатывайте):

Развлекательный публичный аккаунт («Скульптуры из песка каждый день») выбирает интересные фотографии, тексты и видео о скульптурах из песка, чтобы время от времени публиковать их, чтобы облегчить жизнь и работу: