Подтвердить что ты не робот

Фильтрация записей Redis Hash

Я использую redis для хранения хэшей с ~ 100 тыс. записей за хэш. Я хочу реализовать фильтрацию (огранку) записей в заданном хеше. Обратите внимание, что запись хэша может принадлежать n фильтрам.

После прочтения этого и этого он выглядит следующим образом:

  • Внедрить отсортированный набор для каждого фильтра. Значения в SET соответствуют клавишам внутри HASH.
  • Извлеките клавиши HASH из заданного фильтра SET.
  • Как только у меня есть кнопки HASH из SET, выберите соответствующие записи из HASH. Это должно дать мне все записи, принадлежащие фильтру.

Во-первых, правильный подход выше на высоком уровне?

Предполагая, что подход в порядке, бит, который мне не хватает, является наиболее эффективной версией для извлечения записей HASH? Правильно ли я рассуждаю, как только у меня есть клавиши HASH, я должен использовать PIPELINE для очереди нескольких команд HGETALL, проходящих через каждый HASH-ключ? Есть ли лучший подход?

Моя забота об использовании PIPELINE заключается в том, что я считаю, что он будет блокировать всех других клиентов при обслуживании этой команды. Я буду подталкивать отфильтрованные результаты с 500 результатами на страницу. При использовании нескольких браузеров-клиентов, выполняющих фильтрацию, не говоря уже о задних процессах, которые заполняют SET и HASH, это звучит, как будто существует много споров, если PIPELINE блокирует. Может ли кто-нибудь дать представление об этом?

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

Спасибо, Пол

4b9b3361

Ответ 1

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

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

Примером этого в действии является мой Redis mini StackOverflow clone каждый клик делает вызов ToQuestionResults(), который, поскольку операции конвейерны отправляет все операции по 1 вызову записи Socket и считывает результаты в 1 считывании блокировки Socket, который более эффективен, чем блокировка чтения для каждого вызова:

https://github.com/ServiceStack/ServiceStack.Examples/blob/master/src/RedisStackOverflow/RedisStackOverflow.ServiceInterface/IRepository.cs#L180

Моя забота об использовании ТРУБОПРОВОДА что я считаю, что это заблокирует все остальные клиентов при обслуживании команды.

Это не является серьезной проблемой, и я не стал бы думать о том, как здесь работает Redis, предположим, что он делает это наиболее эффективно, когда Pipelining не блокирует обработку других команд клиентов. Концептуально вы можете думать, что redis-server обрабатывает каждую команду (конвейерно или нет) в порядке FIFO (т.е. Время ожидания/считывания всего конвейера не теряется).

Вы описываете что-то ближе к MULTI/EXEC (т.е. Redis Transactions), где все операции выполняются сразу, как только сервер Redis считывает EXEC (т.е. транзакцию EOF). Это тоже не проблема, и redis-сервер все равно не тратит время на ожидание получения всей транзакции, он просто приостанавливает частичный набор команд во временной очереди, пока не получит окончательный EXEC, который затем обрабатывается сразу.

Вот как redis достигает атомарности, обрабатывая каждую команду, по очереди, как только она их получает. Поскольку нет других потоков, нет переключения контекста нити, никаких блокировок и многопоточных проблем. Он в основном достигает concurrency, обрабатывая каждую команду очень быстро.

Итак, в этом случае я бы использовал Pipelining, поскольку он всегда выигрывал, тем более, что больше команд вы конвейерно (поскольку вы уменьшаете количество чтения блокировки).

Ответ 2

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

Отправка 500 операций HGET может работать (при условии, что набор содержит как хэш, так и ключ), хотя возможно, что использование хэшей вообще является случаем преждевременной оптимизации - вам может быть лучше использовать обычные клавиши и MGET.

Ответ 3

Я думаю, вы неправильно понимаете, что такое конвейерная обработка. Он не блокируется, пока все команды отправляются. Все, что он делает, это BUFFERING команды, а затем выполнение их всех сразу в конце, поэтому они выполняются, как если бы они были одной единственной командой. Ни при каких обстоятельствах не происходит блокировка. То же самое верно для redis multi/exec. Самое близкое, что вы можете заблокировать/заблокировать в redis, - это оптимистичная блокировка с помощью watch, что приведет к сбою exec, если ключ redis был записан с тех пор, как вы вызвали watch.

Еще эффективнее, что вызов hget 500 раз в блоке конвейера должен просто вызвать hmget('hash-key',*keys), где keys - массив из 500 хеш-ключей, которые вы просматриваете. Это приведет к одному вызову redis, который будет таким же, как если бы он был конвейерным, но должен выполняться быстрее, поскольку вы не зацикливаете на рубине.