Фильтрация Хэш-Записей Redis

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

после прочтения этой и этой похоже, я должен:

  1. реализуйте сортированный набор на фильтр. Значения в наборе соответствуют ключам в хэше.
  2. получить хэш-ключи из данного фильтра НАБОР.
  3. как только у меня есть хэш-ключи из набора, извлеките соответствующие записи из хэша. Это должно дать мне все записи, которые принадлежат фильтру.

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

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

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

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

спасибо, Пол!--1-->

3 ответов


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

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

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

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

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

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

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

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

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


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

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


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

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