rueidis
A fast Golang Redis client that does auto pipelining and supports server-assisted client-side caching.
Features
- Auto pipelining for non-blocking redis commands
- Server-assisted client-side caching
- Generic Object Mapping with client-side caching
- Cache-Aside pattern with client-side caching
- Distributed Locks with client-side caching
- Helpers for writing tests with rueidis mock
- OpenTelemetry integration
- Hooks and other integrations
- Go-redis like API adapter by @418Coffee
- Pub/Sub, Sharded Pub/Sub, Streams
- Redis Cluster, Sentinel, RedisJSON, RedisBloom, RediSearch, RedisTimeseries, etc.
- Probabilistic Data Structures without Redis Stack
- Availability zone affinity routing
Getting Started
package main
import (
"context"
"github.com/redis/rueidis"
)
func main() {
client, err := rueidis.NewClient(rueidis.ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
if err != nil {
panic(err)
}
defer client.Close()
ctx := context.Background()
// SET key val NX
err = client.Do(ctx, client.B().Set().Key("key").Value("val").Nx().Build()).Error()
// HGETALL hm
hm, err := client.Do(ctx, client.B().Hgetall().Key("hm").Build()).AsStrMap()
}
Check out more examples: Command Response Cheatsheet
Developer Friendly Command Builder
client.B() is the builder entry point to construct a redis command:

Recorded by @FZambia Improving Centrifugo Redis Engine throughput and allocation efficiency with Rueidis Go library
Once a command is built, use either client.Do() or client.DoMulti() to send it to redis.
You ❗️SHOULD NOT❗️ reuse the command to another client.Do() or client.DoMulti() call because it has been recycled to the underlying sync.Pool by default.
To reuse a command, use Pin() after Build() and it will prevent the command from being recycled.
Pipelining
Auto Pipelining
All concurrent non-blocking redis commands (such as GET, SET) are automatically pipelined by default,
which reduces the overall round trips and system calls and gets higher throughput. You can easily get the benefit
of pipelining technique by just calling client.Do() from multiple goroutines concurrently.
For example:
func BenchmarkPipelining(b *testing.B, client rueidis.Client) {
// the below client.Do() operations will be issued from
// multiple goroutines and thus will be pipelined automatically.
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
client.Do(context.Background(), client.B().Get().Key("k").Build()).ToString()
}
})
}
Benchmark Comparison with go-redis v9
Compared to go-redis, Rueidis has higher throughput across 1, 8, and 64 parallelism settings.
It is even able to achieve ~14x throughput over go-redis in a local benchmark of MacBook Pro 16" M1 Pro 2021. (see parallelism(64)-key(16)-value(64)-10)

Benchmark source code: https://github.com/rueian/rueidis-benchmark
A benchmark result performed on two GCP n2-highcpu-2 machines also shows that rueidis can achieve higher throughput with lower latencies: https://github.com/redis/rueidis/pull/93
Disable Auto Pipelining
While auto pipelining maximizes throughput, it relies on additional goroutines to process requests and responses and may add some latencies due to goroutine scheduling and head of line blocking.
You can avoid this by setting DisableAutoPipelining to true, then it will switch to connection pooling approach and serve each request with dedicated connection on the same goroutine.
When DisableAutoPipelining is set to true, you can still send commands for auto pipelining with ToPipe():
cmd := client.B().Get().Key("key").Build().ToPipe()
client.Do(ctx, cmd)
This allows you to use connection pooling approach by default but opt-in auto pipelining for a subset of requests.
Manual Pipelining
Besides auto pipelining, you can also pipeline commands manually with DoMulti():
cmds := make(rueidis.Commands, 0, 10)
for i := 0; i < 10; i++ {
cmds = append(cmds, client.B().Set().Key("key").Value("value").Build())
}
for _, resp := range client.DoMulti(ctx, cmds...) {
if err := resp.Error(); err != nil {
panic(err)
}
}
When using DoMulti() to send multiple commands, the original commands are recycled after execution by default.
If you need to reference them afterward (e.g. to retrieve the key), use the Pin() method to prevent recycling.
// Create pinned commands to preserve them from being recycled
cmds := make(rueidis.Commands, 0, 10)
for i := 0; i < 10; i++ {
cmds = append(cmds, client.B().Get().Key(strconv.Itoa(i)).Build().Pin())
}
// Execute commands and process responses
for i, resp := range client.DoMulti(context.Background(), cmds...) {
fmt.Println(resp.ToString()) // this is the result
fmt.Println(cmds[i].Commands()[1]) // this is the corresponding key
}
Alternatively, you can use the MGet and MGetCache helper functions to easily map keys to their corresponding responses.
val, err := MGet(client, ctx, []string{"k1", "k2"})
fmt.Println(val["k1"].ToString()) // this is the k1 value
Server-Assisted Client-Side Caching
The opt-in mode of server-assisted client-side caching is enabled by default and can be used by calling DoCache() or DoMultiCache() with client-side TTLs specified.
client.DoCache(ctx, client.B().Hmget().Key("mk").Field("1", "2").Cache(), time.Minute).ToArray()
client.DoMultiCache(ctx,
rueidis.CT(client.B().Get().Key("k1").Cache(), 1*time.Minute),
rueidis.CT(client.B().Get().Key("k2").Cache(), 2*time.Minute))
Cached responses, including Redis Nils, will be invalidated either when being notified by redis servers or when their client-side TTLs are reached. See https://github.com/redis/rueidis/issues/534 for more details.
Benchmark
Server-assisted client-side caching can dramatically boost latencies and throughput just like having a redis replica right inside your application. For example:

Benchmark source code: https://github.com/rueian/rueidis-benchmark
Client-Side Caching Helpers
Use CacheTTL() to check the remaining client-side TTL in seconds:
client.DoCache(ctx, client.B().Get().Key("k1").Cache(), time.Minute).CacheTTL() == 60
Use IsCacheHit() to verify if the response came from the client-side memory:
client.DoCache(ctx, client.B().Get().Key("k1").Cache(), time.Minute).IsCacheHit() == true
If the OpenTelemetry is enabled by the rueidisotel.NewClient(option), then there are also two metrics instrumented:
- rueidis_do_cache_miss
- rueidis_do_cache_hits
MGET/JSON.MGET Client-Side Caching Helpers
rueidis.MGetCache and rueidis.JsonMGetCache are handy helpers fetching multiple keys across different slots through the client-side caching.
They will first group keys by slot to build MGET or JSON.MGET commands respectively and then send requests with only cache missed keys to redis nodes.
Broadcast Mode Client-Side Caching
Although the default is opt-in mode, you can use broadcast mode by specifying your prefixes in ClientOption.ClientTrackingOptions:
client, err := rueidis.NewClient(rueidis.ClientOption{
InitAddress: []string{"127.0.0.1:6379"},
ClientTrackingOptions: []string{"PREFIX", "prefix1:", "PREFIX", "prefix2:", "BCAST"},
})
if err !