Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 143 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# redisson

A Type-safe Golang Redis RESP2/RESP3 client.
A Type-safe Golang Redis RESP2 client.

## Features

Expand All @@ -10,20 +10,19 @@ A Type-safe Golang Redis RESP2/RESP3 client.
* Check forbid Redis commands in development mode.
* Monitoring cost of Redis commands.
* Monitoring status of connections.
* Monitoring hits/miss of Redis RESP3 client side caching.
* Support Redis RESP2/RESP3.
* Opt-in client side caching.
* Auto pipeline for non-blocking Redis RESP3 commands.
* Connection pooling for blocking Redis RESP3 commands.
* Support Redis RESP2.

## Requirement

* Currently, only supports Redis < 7.x
* Golang >= 1.8
* Golang >= 1.16

## Base Library
- RESP2, using [go-redis/redis](https://github.com/go-redis/redis) library.
- RESP3, using [rueian/rueidis](https://github.com/rueian/rueidis) library.

## Links
* [English](https://github.com/sandwich-go/redisson/blob/master/README.md)
* [中文文档](https://github.com/sandwich-go/redisson/blob/master/README_CN.md)
* [English](https://github.com/sandwich-go/redisson/blob/version/1.0/README.md)
* [中文文档](https://github.com/sandwich-go/redisson/blob/version/1.0/README_CN.md)

## Getting Started

Expand All @@ -37,7 +36,7 @@ import (

func main() {
c := redisson.MustNewClient(redisson.NewConf(
redisson.WithResp(redisson.RESP3),
redisson.WithResp(redisson.RESP2),
redisson.WithDevelopment(false),
))
defer c.Close()
Expand All @@ -52,71 +51,108 @@ func main() {
```

## Check
Check only in development mode.

### Check version
if Redis < 6.0
```go
c := redisson.MustNewClient(redisson.NewConf(
redisson.WithResp(redisson.RESP3),
redisson.WithResp(redisson.RESP2),
redisson.WithDevelopment(true),
))
defer c.Close()

res := c.Set(ctx, "key", "10", -1)
```
Output:
```go
Line 34: - redis 'SET KEEPTTL' are not supported in version "5.0.0", available since 6.0.0
```text
[SET KEEPTTL]: redis command are not supported in version "5.0.0", available since 6.0.0
```

> :warning: Will Panic when check version failed in development mode.

### Check deprecated
if Redis >= 4.0
```go
c := redisson.MustNewClient(redisson.NewConf(
redisson.WithResp(redisson.RESP3),
redisson.WithResp(redisson.RESP2),
redisson.WithDevelopment(true),
))
defer c.Close()

res := c.HMSet(ctx, "key", "10")
```
Output:
```go
As of Redis version 4.0.0, this command is regarded as deprecated.
```text
[HMSET]: As of Redis version 4.0.0, this command is regarded as deprecated.
It can be replaced by HSET with multiple field-value pairs when migrating or writing new code.
```

### Check slot for multiple keys
```go
```text
c := redisson.MustNewClient(redisson.NewConf(
redisson.WithResp(redisson.RESP3),
redisson.WithResp(redisson.RESP2),
redisson.WithDevelopment(true),
))
defer c.Close()

res := c.MSet(ctx, "key1", "10", "key2", "20")
```
Output:
```go
Line 34: - multi key command with different key slots are not allowed
```text
[MSET]: multi key command with different key slots are not allowed
```

> :warning: Will Panic when has different slots of keys in development mode.

### Check forbid
```go
c := redisson.MustNewClient(redisson.NewConf(
redisson.WithResp(redisson.RESP3),
redisson.WithResp(redisson.RESP2),
redisson.WithDevelopment(true),
))
defer c.Close()

res := c.ClusterFailover(ctx)
```
Output:
```go
Line 34: - command 'CLUSTER FAILOVER' not allowed
```text
[CLUSTER FAILOVER]: redis command are not allowed
```

> :warning: Will Panic when exec forbid command in development mode.

#### Forbid commands
* CLUSTER ADDSLOTS
* CLUSTER ADDSLOTSRANGE
* CLUSTER DELSLOTS
* CLUSTER DELSLOTSRANGE
* CLUSTER FAILOVER
* CLUSTER FORGET
* CLUSTER MEET
* CLUSTER REPLICATE
* CLUSTER RESET HARD/SOFT
* CLUSTER SAVECONFIG
* CLUSTER SLAVES
* KEYS
* MIGRATE
* BGREWRITEAOF
* BGSAVE
* CONFIG GET
* CONFIG RESETSTAT
* CONFIG REWRITE
* CONFIG SET
* FLUSHALL ASYNC/SYNC
* FLUSHDB ASYNC/SYNC
* SAVE
* SHUTDOWN NOSAVE/SAVE
* SLAVEOF
* SELECT

## Monitor

Import Grafana dashboard id `16768`

```go
import (
"github.com/prometheus/client_golang/prometheus"
Expand All @@ -127,7 +163,7 @@ var DefaultPrometheusRegistry = prometheus.NewRegistry()

c := redisson.MustNewClient(redisson.NewConf(
redisson.WithResp(redisson.RESP3),
redisson.WithDevelopment(true),
redisson.WithEnableMonitor(true),
))
defer c.Close()

Expand All @@ -136,30 +172,92 @@ c.RegisterCollector(func(c prometheus.Collector) {
})
```


## Auto Pipeline

All non-blocking commands sending to a single Redis node are automatically pipelined through one tcp connection,
which reduces the overall round trips and system calls, and gets higher throughput.

Notice: Only supports when use Redis RESP3 client.


## Client Side Caching

The Opt-In mode of server-assisted client side caching is always enabled.

```golang
c.Cache(time.Minute).Get(ctx, "key").Val()
![grafana_dashboard](https://github.com/sandwich-go/redisson/blob/version/1.0/grafana_dashboard.png)

## Benchmark
### Environment
- [go-redis/redis](https://github.com/go-redis/redis) v8.11.5
- [joomcode/redispipe](https://github.com/joomcode/redispipe) v0.9.4
- [mediocregopher/radix](https://github.com/mediocregopher/radix) v4.1.1
- [rueian/rueidis](https://github.com/rueian/rueidis) v0.0.74
- [sandwich-go/redisson](https://github.com/sandwich-go/redisson) v1.1.14

### Benchmarking Result
##### Single, Parallel mode, Get Command
```markdown
+---------------------------------------------------+-----------+-------+-------+-----------+
| Single Parallel(128) Get | iteration | ns/op | B/op | allocs/op |
+===================================================+===========+=======+=======+===========+
| sandwich-go/redisson/RESP2:Val(64):Pool(100) | 362365 | 6136 | 279 | 6 |
| sandwich-go/redisson/RESP2:Val(64):Pool(1000) | 504202 | 4731 | 286 | 6 |
| sandwich-go/redisson/RESP2:Val(256):Pool(100) | 362181 | 6334 | 487 | 6 |
| sandwich-go/redisson/RESP2:Val(256):Pool(1000) | 481341 | 4946 | 495 | 6 |
| sandwich-go/redisson/RESP2:Val(1024):Pool(100) | 332634 | 6822 | 1351 | 6 |
| sandwich-go/redisson/RESP2:Val(1024):Pool(1000) | 451609 | 5299 | 1360 | 6 |
| sandwich-go/redisson/RESP3:Val(64):Pool(100) | 1208716 | 1923 | 320 | 4 |
| sandwich-go/redisson/RESP3:Val(256):Pool(100) | 1000000 | 2013 | 512 | 4 |
| sandwich-go/redisson/RESP3:Val(1024):Pool(100) | 728786 | 2816 | 1281 | 4 |
| rueian/rueidis/rueidiscompat:Val(64):Pool(100) | 1253146 | 1847 | 256 | 4 |
| rueian/rueidis/rueidiscompat:Val(256):Pool(100) | 1000000 | 2034 | 448 | 4 |
| rueian/rueidis/rueidiscompat:Val(1024):Pool(100) | 792254 | 2686 | 1217 | 4 |
| go-redis/redis/v8:Val(64):Pool(100) | 369186 | 6098 | 279 | 6 |
| go-redis/redis/v8:Val(64):Pool(1000) | 506796 | 4750 | 286 | 6 |
| go-redis/redis/v8:Val(256):Pool(100) | 357454 | 6266 | 487 | 6 |
| go-redis/redis/v8:Val(256):Pool(1000) | 486217 | 4919 | 495 | 6 |
| go-redis/redis/v8:Val(1024):Pool(100) | 331382 | 6779 | 1351 | 6 |
| go-redis/redis/v8:Val(1024):Pool(1000) | 452067 | 5307 | 1360 | 6 |
| mediocregopher/radix/v4:Val(64):Pool(100) | 596540 | 4284 | 26 | 1 |
| mediocregopher/radix/v4:Val(64):Pool(1000) | 589083 | 4902 | 54 | 1 |
| mediocregopher/radix/v4:Val(256):Pool(100) | 576108 | 4384 | 27 | 1 |
| mediocregopher/radix/v4:Val(256):Pool(1000) | 597157 | 4993 | 54 | 1 |
| mediocregopher/radix/v4:Val(1024):Pool(100) | 573411 | 4539 | 27 | 1 |
| mediocregopher/radix/v4:Val(1024):Pool(1000) | 559611 | 5062 | 56 | 1 |
| joomcode/redispipe:Val(64):Pool(100) | 1109589 | 2137 | 168 | 5 |
| joomcode/redispipe:Val(256):Pool(100) | 1000000 | 2170 | 377 | 5 |
| joomcode/redispipe:Val(1024):Pool(100) | 958350 | 2442 | 1241 | 5 |
+---------------------------------------------------+-----------+-------+-------+-----------+
```

An explicit client side TTL is required because Redis server may not send invalidation message in time when
a key is expired on the server. Please follow [#6833](https://github.com/redis/redis/issues/6833) and [#6867](https://github.com/redis/redis/issues/6867)
![BenchmarkSingleClientGetParallel](https://github.com/sandwich-go/go-redis-client-benchmark/blob/master/BenchmarkSingleClientGetParallel.png)

##### Cluster, Parallel mode, Get Command
```markdown
+---------------------------------------------------+-----------+-------+-------+-----------+
| Cluster Parallel(128) Get | iteration | ns/op | B/op | allocs/op |
+===================================================+===========+=======+=======+===========+
| sandwich-go/redisson/RESP2:Val(64):Pool(100) | 361689 | 6246 | 279 | 6 |
| sandwich-go/redisson/RESP2:Val(64):Pool(1000) | 494625 | 4819 | 286 | 6 |
| sandwich-go/redisson/RESP2:Val(256):Pool(100) | 353413 | 6439 | 487 | 6 |
| sandwich-go/redisson/RESP2:Val(256):Pool(1000) | 478305 | 5035 | 494 | 6 |
| sandwich-go/redisson/RESP2:Val(1024):Pool(100) | 324940 | 6992 | 1351 | 6 |
| sandwich-go/redisson/RESP2:Val(1024):Pool(1000) | 441291 | 5472 | 1360 | 6 |
| sandwich-go/redisson/RESP3:Val(64):Pool(100) | 1036126 | 2275 | 320 | 4 |
| sandwich-go/redisson/RESP3:Val(256):Pool(100) | 1008175 | 2420 | 513 | 4 |
| sandwich-go/redisson/RESP3:Val(1024):Pool(100) | 766168 | 2906 | 1282 | 4 |
| rueian/rueidis/rueidiscompat:Val(64):Pool(100) | 946216 | 2266 | 256 | 4 |
| rueian/rueidis/rueidiscompat:Val(256):Pool(100) | 924811 | 2292 | 448 | 4 |
| rueian/rueidis/rueidiscompat:Val(1024):Pool(100) | 856582 | 2802 | 1218 | 4 |
| go-redis/redis/v8:Val(64):Pool(100) | 351850 | 6251 | 279 | 6 |
| go-redis/redis/v8:Val(64):Pool(1000) | 489259 | 4821 | 286 | 6 |
| go-redis/redis/v8:Val(256):Pool(100) | 356703 | 6385 | 487 | 6 |
| go-redis/redis/v8:Val(256):Pool(1000) | 478236 | 5012 | 494 | 6 |
| go-redis/redis/v8:Val(1024):Pool(100) | 333362 | 6972 | 1351 | 6 |
| go-redis/redis/v8:Val(1024):Pool(1000) | 443264 | 5386 | 1360 | 6 |
| mediocregopher/radix/v4:Val(64):Pool(100) | 477573 | 4598 | 113 | 2 |
| mediocregopher/radix/v4:Val(64):Pool(1000) | 386779 | 5431 | 114 | 2 |
| mediocregopher/radix/v4:Val(256):Pool(100) | 459818 | 4737 | 113 | 2 |
| mediocregopher/radix/v4:Val(256):Pool(1000) | 383200 | 5656 | 114 | 2 |
| mediocregopher/radix/v4:Val(1024):Pool(100) | 451070 | 4911 | 114 | 2 |
| mediocregopher/radix/v4:Val(1024):Pool(1000) | 356745 | 5745 | 114 | 2 |
| joomcode/redispipe:Val(64):Pool(100) | 1091751 | 2147 | 170 | 5 |
| joomcode/redispipe:Val(256):Pool(100) | 1088572 | 2298 | 379 | 5 |
| joomcode/redispipe:Val(1024):Pool(100) | 800530 | 2548 | 1246 | 5 |
+---------------------------------------------------+-----------+-------+-------+-----------+
```

Although an explicit client side TTL is required, the `Cache()` still sends a `PTTL` command to server and make sure that
the client side TTL is not longer than the TTL on server side.
![BenchmarkClusterClientGetParallel](https://github.com/sandwich-go/go-redis-client-benchmark/blob/master/BenchmarkClusterClientGetParallel.png)

Notice: Only supports when use Redis RESP3 client.
See [Benchmark Detail Result](https://github.com/sandwich-go/go-redis-client-benchmark)


* [Opt-in client side caching](https://redis.io/docs/manual/client-side-caching/)
Expand Down
Loading