Skip to content

diiyw/nodis

Repository files navigation

Nodis

GitHub top language GitHub Release

English | 简体中文

Nodis is a Redis implementation using the Golang programming language. This implementation provides a simple way to embed Redis functionality directly into your application or run it as a standalone server. The supported commands are compatible with the original Redis protocol, allowing you to use existing Redis clients like goredis for testing and integration.

Supported Data Types

  • Bitmap
  • String
  • List
  • Hash
  • Set
  • Sorted Set

Key Features

  • Fast and Embeddable: The Golang-based implementation is designed to be fast and easily embeddable within your applications.
  • Low Memory Usage: The system only stores hot data in memory, minimizing the overall memory footprint.
  • Snapshot and WAL for Data Storage: This Redis implementation supports snapshot and write-ahead logging (WAL) mechanisms for reliable data storage.
  • Custom Data Storage Backends: You can integrate custom data storage backends, such as Amazon S3, browser-based storage, and more.
  • Browser Support with WebAssembly: Starting from version 1.2.0, this Redis implementation can run directly in the browser using WebAssembly.
  • Remote Change Monitoring: From version 1.2.0 onwards, the system supports watching for changes from remote sources.
  • Redis Protocol Compatibility: As of version 1.5.0, this Redis implementation fully supports the original Redis protocol, ensuring seamless integration with existing Redis clients.

Supported Commands

Client Handling Configuration Key Commands String Commands Set Commands Hash Commands List Commands Sorted Set Commands Geo Commands
CLIENT FLUSHALL DEL GET SADD HSET LPUSH ZADD GEOADD
PING FLUSHDB EXISTS SET SSCAN HGET RPUSH ZCARD GEOPOS
QUIT SAVE EXPIRE INCR SCARD HDEL LPOP ZRANK GEOHASH
ECHO INFO EXPIREAT DECR SPOP HLEN RPOP ZREVRANK GEODISH
DBSIZE KEYS SETBIT SDIFF HKEYS LLEN ZSCORE GEORADIUS
MULTI TTL GETBIT SINTER HEXISTS LINDEX ZINCRBY GEORADIUSBYMEMBER
DISCARD RENAME INCR SISMEMBER HGETALL LINSERT ZRANGE
EXEC TYPE DESR SMEMBERS HINCRBY LPUSHX ZREVRANGE
SCAN SETEX SREM HICRBYFLOAT RPUSHX ZRANGEBYSCORE
RANDOMKEY INCRBY SMOVE HSETNX LREM ZREVRANGEBYSCORE
RENAMEEX DECRBY SRANDMEMBER HMGET LSET ZREM
PERSIST SETNX SINTERSTORE HMSET LRANGE ZREMRANGEBYRANK
PTTL INCRBYFLOAT SUNIONSTORE HCLEAR LPOPRPUSH ZREMRANGEBYSCORE
UNLINK APPEND HSCAN RPOPLPUSH ZCLEAR
GETRANGE HVALS BLPOP ZEXISTS
STRLEN HSTRLEN BRPOP ZUNIONSTORE
SETRANGE ZINTERSTORE

Get Started

 go get github.com/diiyw/nodis@latest

Or use test version

 go get github.com/diiyw/nodis@main
package main

import "github.com/diiyw/nodis"

func main() {
	// Create a new Nodis instance
	opt := nodis.DefaultOptions
	n := nodis.Open(opt)
	defer n.Close()
	// Set a key-value pair
	n.Set("key", []byte("value"),false)
	n.LPush("list", []byte("value1"))
}

Examples

Watch changes

Server:

package main

import (
	"fmt"
	"github.com/diiyw/nodis"
	"github.com/diiyw/nodis/pb"
	"github.com/diiyw/nodis/sync"
	"time"
)

func main() {
	var opt = nodis.DefaultOptions
	n := nodis.Open(opt)
	opt.Synchronizer = sync.NewWebsocket()
	n.Watch([]string{"*"}, func(op *pb.Operation) {
		fmt.Println("Server:", op.Key, string(op.Value))
	})
	go func() {
		for {
			time.Sleep(time.Second)
			n.Set("test", []byte(time.Now().Format("2006-01-02 15:04:05")))
		}
	}()
	err := n.Publish("127.0.0.1:6380", []string{"*"})
	if err != nil {
		panic(err)
	}
}
  • Browser client built with WebAssembly
GOOS=js GOARCH=wasm go build -o test.wasm
package main

import (
	"fmt"
	"github.com/diiyw/nodis"
	"github.com/diiyw/nodis/fs"
	"github.com/diiyw/nodis/pb"
	"github.com/diiyw/nodis/sync"
)

func main() {
	var opt = nodis.DefaultOptions
	opt.Filesystem = &fs.Memory{}
	opt.Synchronizer = sync.NewWebsocket()
	n := nodis.Open(opt)
	n.WatchKey([]string{"*"}, func(op *pb.Operation) {
		fmt.Println("Subscribe: ", op.Key)
	})
	err := n.Subscribe("ws://127.0.0.1:6380")
	if err != nil {
		panic(err)
	}
	select {}
}
Simple Redis Server
package main

import (
	"fmt"
	"net/http"

	"github.com/diiyw/nodis"
)

func main() {
	opt := nodis.DefaultOptions
	n := nodis.Open(opt)
	if err := n.Serve(":6380"); err != nil {
		fmt.Printf("Serve() = %v", err)
	}
}

You can use redis-cli to connect to the server.

redis-cli -p 6380
> set key value

Benchmark

Embed benchmark

Windows 11: 12C/32G

goos: windows
goarch: amd64
pkg: github.com/diiyw/nodis/bench
BenchmarkSet-12         	 1469863	        715.9 ns/op	     543 B/op	       7 allocs/op
BenchmarkGet-12         	12480278	        96.47 ns/op	       7 B/op	       0 allocs/op
BenchmarkLPush-12       	 1484466	        786.2 ns/op	     615 B/op	       9 allocs/op
BenchmarkLPop-12        	77275986	        15.10 ns/op	       0 B/op	       0 allocs/op
BenchmarkSAdd-12        	 1542252	        831.9 ns/op	     663 B/op	      10 allocs/op
BenchmarkSMembers-12    	12739020	        95.18 ns/op	       8 B/op	       1 allocs/op
BenchmarkZAdd-12        	 1000000	        1177 ns/op	     550 B/op	      10 allocs/op
BenchmarkZRank-12       	11430135	        104.1 ns/op	       7 B/op	       0 allocs/op
BenchmarkHSet-12        	 1341817	        863.5 ns/op	     743 B/op	      11 allocs/op
BenchmarkHGet-12        	 9801158	        105.9 ns/op	       7 B/op	       0 allocs/op

Linux VM: 4C/8GB

goos: linux
goarch: amd64
pkg: github.com/diiyw/nodis/bench
BenchmarkSet-4        	  806912	      1658 ns/op	     543 B/op	       7 allocs/op
BenchmarkGet-4        	 5941904	       190.6 ns/op	       7 B/op	       0 allocs/op
BenchmarkLPush-4      	  852932	      1757 ns/op	     615 B/op	       9 allocs/op
BenchmarkLPop-4       	40668902	        27.22 ns/op	       0 B/op	       0 allocs/op
BenchmarkSAdd-4       	  706376	      1913 ns/op	     662 B/op	      10 allocs/op
BenchmarkSMembers-4   	 4819993	       208.1 ns/op	       8 B/op	       1 allocs/op
BenchmarkZAdd-4       	  729039	      2013 ns/op	     550 B/op	      10 allocs/op
BenchmarkZRank-4      	 4959448	       246.4 ns/op	       7 B/op	       0 allocs/op
BenchmarkHSet-4       	  735676	      1971 ns/op	     742 B/op	      11 allocs/op
BenchmarkHGet-4       	 4442625	       243.4 ns/op	       7 B/op	       0 allocs/op
Redis benchmark tool
redis-benchmark -p 6380 -t set,get,lpush,lpop,sadd,smembers,zadd,zrank,hset,hget -n 100000 -q
SET: 89126.56 requests per second
GET: 90415.91 requests per second
LPUSH: 91491.30 requests per second
LPOP: 92165.90 requests per second
SADD: 91911.76 requests per second
HSET: 93023.25 requests per second

Note

If you want to persist data, please make sure to call the Close() method when your application exits.