jeudi 29 septembre 2016

Redis Java Client: Do I need to buffer my commands into a pipeline for performance?

So I am just incrementing scores in a sorted set. That is the only command I am running, about 10-30 commands per second, from a Java application, using the Jedis client. Since I am just updating the scores, I don't care about the response either. My concern is that each 'zincrby' command is being put into its own packet and also of course waiting for the next reply before allowing my thread to resume (let's just assume there is a single thread doing all of this work).

So, I want to just implement pipe-lining to batch say 50 commands at at time. Here's where I see a code/design-pattern smell: Isn't this design pattern common enough that the driver should handle it? It appears that the .net "StackExchange.redis" driver does command batching automatically, but that the Java drivers don't have this feature? Is my idea to make a custom redis command buffer class, which puts incoming commands into a pipeline and calls sync() after 50 items, really needed?

Also, I noticed this in my logs, as I am using Jedis via 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

So it appears that it is closing the connection per naively executed command (via the Spring provided template pattern). I think that closing the connection forces the TCP buffer to send a single command per packet, so that seems pretty inefficient to me since sockets eats up a fair amount of CPU. Although the Spring Data Redis API does allow direct access to the Jedis client and won't close connections if a pipeline is currently open, so writing the "pipeline buffer" is an option with that.

In short, should I create/leverage a buffer that writes to a redis pipeline and the flushes after X commands? I simply don't like the idea of wasting all these CPU cycles (higher AWS bill) running each command naively, and am curious if there is a better design pattern for my scenario.

1 commentaire: