Java-клиент Redis: нужно ли буферизовать мои команды в конвейер для повышения производительности?

Поэтому я просто увеличиваю баллы в отсортированном наборе. Это единственная команда, которую я запускаю, около 10-30 команд в секунду, из приложения Java, используя клиент Jedis. Поскольку я просто обновляю оценки, меня тоже не волнует ответ. Меня беспокоит то, что каждая команда ZINCRBY помещается в свой собственный TCP-пакет, а также ожидает следующего ответа, прежде чем разрешить моему потоку отправить следующий поток ZINCRBY.

Итак, я хочу просто реализовать конвейерную обработку, скажем, 50 команд за раз. Вот где я вижу запах кода/паттерна дизайна: разве этот шаблон проектирования не достаточно распространен, чтобы драйвер мог с ним справиться? Похоже, что драйвер .net «StackExchange.redis» автоматически выполняет пакетную обработку команд, но драйверы Java не имеют этой функции? Действительно ли нужна моя идея создать собственный класс буфера команд Redis, который помещает входящие команды в конвейер и вызывает sync() после 50 элементов?

Кроме того, я заметил это в своих журналах, так как использую Jedis через Spring Data Redis:

20160929 06:48:27.393 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.393 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.393 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.393 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.393 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.394 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.394 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.629 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.630 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.630 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.631 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.631 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.631 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.631 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.631 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.632 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.632 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.632 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.632 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.632 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.633 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.633 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.633 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection
20160929 06:48:27.633 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Opening RedisConnection
20160929 06:48:27.633 [Twitter4J Async Dispatcher[0]] DEBUG o.s.d.r.c.RedisConnectionUtils # Closing Redis Connection

Таким образом, кажется, что он закрывает соединение для наивно выполненной команды (через шаблон шаблона, предоставленный Spring). Я думаю, что закрытие соединения заставляет TCP-буфер отправлять одну команду на пакет, так что мне это кажется довольно неэффективным, поскольку сокеты поглощают изрядное количество ЦП. Хотя Spring Data Redis API разрешает прямой доступ к клиенту Jedis и не закрывает соединения, если конвейер в данный момент открыт, поэтому запись «конвейерного буфера» является вариантом с этим.

Короче говоря, должен ли я создать/использовать буфер, который записывает в конвейер Redis и сбрасывает данные после X-команд? Мне просто не нравится идея тратить все эти циклы ЦП (более высокий счет AWS) на наивное выполнение каждой команды, и мне любопытно, есть ли лучший шаблон проектирования для моего сценария. Или если переключение на другой клиент Java Redis решит эту проблему. Или если есть какая-то библиотека Java, которая уже буферизует команды в конвейере Redis.


person Zombies    schedule 29.09.2016    source источник


Ответы (2)


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

Как правило, все клиенты Java Redis (Jedis, Lettuce, Redisson и многое другое) писать команды по умолчанию напрямую в TCP-канал. Таким образом, каждая команда отправляется в виде одного или нескольких TCP-пакетов. Lettuce и Redisson в качестве режима работы, поскольку оба клиента используют асинхронную модель программирования, управляемую событиями, для записи команд Redis в канал TCP. Jedis заставляет вас ждать результата команды, поскольку он предоставляет блокирующий API. Redisson и Lettuce предоставляют различные виды API (асинхронные с использованием Futures или реактивные с использованием RxJava/Reactive Streams), которые не заставляют вас ждать результата команды.

Пакетная обработка/буферизация — это еще один метод сбора команд в памяти клиента и пакетной отправки команд в Redis. Это работает как с Lettuce, так и с Redisson. Jedis записывает команды в режиме конвейерной обработки непосредственно в TCP-канал.

Пакетирование команд имеет некоторые последствия. Возможна автоматическая пакетная обработка команд (скажем, размером 10 или 50), но это требует некоторого внимания со стороны пользователей. Для пакетной обработки всегда требуется окончательная синхронизация, чтобы команды не задерживались в очереди и не были отправлены из-за того, что размер пакета еще не достигнут.

Spring Data Redis использует Jedis и Lettuce для раскрытия своей функциональности, поэтому Spring Data Redis требуется для работы с общим содержанием обоих драйверов. Вы можете настроить Spring Data Redis для использования пула соединений с Jedis, чтобы получить выгоду от соединений в пуле, которые не закрываются каждый раз, когда вы взаимодействуете с Redis.

person mp911de    schedule 29.09.2016
comment
Спасибо. Я намерен использовать салат после некоторых исследований. В моем случае я читаю из потокового twitter API и имею достаточно согласованных команд redis для отправки (все срабатывают и забывают), что я легко заполню свой буфер размером 50, а затем вызову connection.flushCommands();. Мне было интересно, существовали ли в каком-либо из драйверов Java команды сброса после x или другие функции оптимизации типа «выстрелил и забыл», как в драйвере .net StackExchange.redis (см. stackoverflow.com/questions/27796054/) - person Zombies; 29.09.2016
comment
Я забыл добавить вывод к сообщению: важно знать, для чего вы оптимизируете. У каждого подхода есть свои свойства, и они должны соответствовать вашему подходу и варианту использования. Спасибо за указатель сброса после x-команд. Это может представлять интерес. - person mp911de; 29.09.2016

Конвейерная обработка — это распространенный шаблон Redis для снижения стоимости связи. Jedis — рекомендуемый драйвер Java для Redis, поддерживающий конвейерную обработку. Lettuce — это альтернатива, которая поддерживает конвейерная обработка.

person Pascal Le Merrer    schedule 29.09.2016