Treds is a Radix Trie based data structure server that stores keys in sorted order, ensuring fast and efficient retrieval. A scan operation returns keys in their sorted sequence.
- Keys at root level having a common prefix can be queried optimally
SCANKEYS/SCANKVS/KEYS/KVS
commands returns the results in sorted order- Unlike Redis KEYS, Treds
KEYS
has cursor and matches any valid regex expression also it returns count number of data if data is there - Unlike Redis SCAN, Treds
SCAN
always returns count number of data if data is there. TredsSCAN
works on prefix only. - Unlike Redis ZRANGEBYLEX, Treds
ZRANGELEX
always returns data irrespective of score, basically data across different scores are returned - Unlike Redis PSUBSCRIBE, Treds
PSUBSCRIBE
is designed to work with channels having a common prefix - It has Sorted Maps instead of Sorted Sets. So we can create a Sorted Key/Value pair with associated with a score
- New command -
DELPREFIX
- Deletes all keys having a common prefix and returns number of keys deleted - New command -
LNGPREFIX
- Returns the key value pair in which key is the longest prefix of given string - New command -
PPUBLISH
- Publish a message to all channels that have names with the given channel as their prefix - Currently, it only has Key/Value store, Sorted Maps store, List store, Set store and Hash store and only supports strings/number as values
It is single threaded and has event loop. Implemented using modified Radix trees where leaf nodes are connected by Doubly Linked List in Radix Trie to facilitate the quick lookup of keys/values in sorted order. Doubly Linked List of leaf nodes are updated at the time of create/delete and update of keys optimally. This structure is similar to Prefix Hash Tree, but for Radix Tree and without converting keys to binary. Tree Map used to store score maps also are connected internally using Doubly Linked List using similar logic. For more details - check out the medium article
Both Treds and Redis are filled with 10 Million Keys in KeyValue Store and 10 Million Keys in a Sorted Map/Set respectively
Each key is of format user:%d
, so every key has prefix user:
The commands are run in Golang program and redirecting the output to a file go run main.go > out
.
For Redis setup see - Redis Prefix Bench Repo.
For Etcd setup see - Etcd Prefix Bench Repo
Treds Command -
scankeys 0 prefix 100000000000
Redis Command -
scan 0 match prefix count 100000000000
This graph shows the performance comparison between Treds - ScanKeys and Redis - Scan:
Treds Command -
scankvs 0 prefix 1000
Redis Command -
FT.SEARCH idx:user prefix SORTBY name LIMIT 0 1000
Prefix for redis command can be replaced by "User*", "User1*", "User10*" ... etc
This graph shows the performance comparison between Treds - ScanKVS and Redis FT.Search:
Treds Command -
zrangescorekeys key 0 max 0 100000000000 false
Redis Command -
zrangebyscore key 0 max
This graph shows the performance comparison between Treds - ZRangeScoreKeys and Redis - ZRangeByScore:
Treds Command -
scankeys 0 prefix 100000000000
Etcd Command -
etcdctl get prefix --prefix --keys-only
This graph shows the performance comparison between Treds - ScanKeys and Etcd get --prefix command:
PING
- Replies with aPONG
SET key value
- Sets a key value pairGET key
- Get a value for a keyDEL key
- Delete a keyMSET key1 value1 [key2 value2 key3 value3 ....]
- Set values for multiple keysMGET key1 [key2 key3 ....]
- Get values for multiple keysDELPREFIX prefix
- Delete all keys having a common prefix. Returns number of keys deletedLNGPREFIX string
- Returns the key value pair in which key is the longest prefix of given stringDBSIZE
- Get number of keys in the dbSCANKEYS cursor prefix count
- Returns the count number of keys matching prefix starting from an index in lex order only present in Key/Value Store. Last element is the next cursorSCANKVS cursor prefix count
- Returns the count number of keys/value pair in which keys match prefix starting from an index in lex order only present in Key/Value Store. Last element is the next cursorKEYS cursor regex count
- Returns count number of keys matching a regex in lex order starting with cursor. Count is optional. Last element is the next cursorKVS cursor regex count
- Returns count number of keys/values in which keys match a regex in lex order starting with cursor. Count is optional. Last element is the next cursorEXPIRE key seconds
- Expire key after given secondsTTL key
- Returns the time in seconds remaining before key expires. -1 if key has no expiry, -2 if key is not present.
KEYSZ cursor regex count
- Returns count number of keys in Sorted Maps Store matching a regex in lex order starting with cursor. Count is optional. Last element is the next cursorZADD key score member_key member_value [score member_key member_value ....]
- Add member_key with member value with score to a sorted map in keyZREM key member [member ...]
- Removes a member from sorted map in keyZCARD key
- Returns the count of key/value pairs in sorted map in keyZSCORE key member
- Returns the score of a member in sorted map in keyZRANGELEXKEYS key offset count withscore min max
- Returns the count number of keys are >= min and <= max starting from an index in a sorted map in lex order. WithScore can be true or falseZRANGELEXKVS key offset count withscore min max
- Returns the count number of key/value pair in which keys are >= min and <= max starting from an index in a sorted map in lex order. WithScore can be true or falseZRANGESCOREKEYS key min max offset count withscore
- Returns the count number of keys with the score between min/max in sorted order of score. WithScore can be true or falseZRANGESCOREKVS key min max offset count withscore
- Returns the count number of key/value pair with the score between min/max in sorted order of score. WithScore can be true or falseZREVRANGELEXKEYS key offset count withscore min max
- Returns the count number of keys are >= min and <= max starting from an index in a sorted map in reverse lex order. WithScore can be true or falseZREVRANGELEXKVS key offset count withscore min max
- Returns the count number of key/value pair in which keys are >= min and <= max starting from an index in a sorted map in reverse lex order. WithScore can be true or falseZREVRANGESCOREKEYS key min max offset count withscore
- Returns the count number of keys with the score between min/max in reverser sorted order of score. WithScore can be true or falseZREVRANGESCOREKVS key min max offset count withscore
- Returns the count number of key/value pair with the score between min/max in reverse sorted order of score. WithScore can be true or false
KEYSL cursor regex count
- Returns count number of keys in List Store matching a regex in lex order starting with cursor. Count is optional. Last element is the next cursorLPUSH key element [element ...]
- Adds elements to the left of list with keyRPUSH key element [element ...]
- Adds elements to the right of list with keyLPOP key count
- Removes count elements from left of list with key and returns the popped elementsRPOP key count
- Removes count elements from right of list with key and returns the popped elementsLREM key index
- Removes element at index of list with keyLSET key index element
- Sets an element at an index of a list with keyLRANGE key start stop
- Returns the elements from start index to stop index in the list with keyLLEN key
- Returns the length of list with keyLINDEX key index
- Returns the element at index of list with key
KEYSS cursor regex count
- Returns count number of keys in Set Store matching a regex in lex order starting with cursor. Count is optional. Last element is the next cursorSADD key member [member ...]
- Adds the members to a set with keySREM key member [member ...]
- Removes the members from a set with keySMEMBERS key
- Returns all members of a set with keySISMEMBER key member
- Return 1 if member is present in set with key, 0 otherwiseSCARD key
- Returns the size of the set with keySUNION key [key ...]
- Returns the union of sets with the give keysSINTER key [key ...]
- Returns the intersection of sets with the given keysSDIFF key [key ...]
- Returns the difference between the first set and all the successive sets.
KEYSH cursor regex count
- Returns count number of keys in Hash Store matching a regex in lex order starting with cursor. Count is optional. Last element is the next cursorHSET key field value [field value ...]
- Sets field value pairs in the hash with keyHGET key field
- Returns the value present at field inside the hash at keyHGETALL key
- Returns all field value pairs inside the hash at the keyHLEN key
- Returns the size of hash at the keyHDEL key field [field ...]
- Deletes the fields present inside the hash at the keyHEXISTS key field
- Returns a true or false based on field is present in hash at key or notHKEYS key
- Returns all field present in the hash at keyHVALS key
- Returns all values present in the hash at key
SNAPSHOT
- Persist the Key Value Store data on disk immediately.RESTORE folder_path
- Restore the persisted snapshot on disk immediately.
FLUSHALL
- Deletes all keys
MULTI
- Starts a transactionEXEC
- Execute all commands in the transaction and close the transactionDISCARD
- Discard all commands in the transaction and close the transaction
PUBLISH channel message
- Publish a message to a channelSUBSCRIBE channel [channel ...]
- Subscribe to channelsUNSUBSCRIBE channel [channel ...]
- Unsubscribe to channelsPSUBSCRIBE channel [channel ...]
- Subscription receives all messages published to channels whose names are prefixes of the given channels.PPUBLISH channel message
- This command publishes the message to all channels that have names with the given channel as their prefix.PUBSUBCHANNELS prefix
- Returns all active channels having one or more subscribers, a common prefix with the given prefix. Prefix is optional.
While PUBLISH
and SUBSCRIBE
are similar to Redis, PSUBSCRIBE
and PPUBLISH
are designed to work with channels having a common prefix.
If a client subscribes to a channel named NEWS-IND-KA-BLR
using PSUBSCRIBE
, then the client will receive messages published
to channels NEWS-IND-KA-BLR
, NEWS-IND-KA
, NEWS-IND
, NEWS
using PPUBLISH
.
In simple words - PPUBLISH
publishes a message to all channels that have names with the given channel as their prefix and
PSUBSCRIBE
receives all messages published to channels whose names are prefixes of the given channels.
DCREATE collectionname schemajson indexjson
- Create a collection with schema and indexDDROP collectionname
- Drop a collectionDINSERT collectionname json
- Insert a document in a collectionDQUERY collectionname json
- Query a collectionDEXPLAIN collectionname json
- Explain a query - Returns the query plan - index with which query is executed
DCREATE users "{\"name\": {\"type\": \"string\"}, \"age\": {\"type\": \"float\", \"min\": 18}, \"salary\": {\"type\": \"float\"}}" "[{\"fields\": [\"age\"], \"type\": \"normal\"}, {\"fields\": [\"salary\"], \"type\": \"normal\"}]"
DCREATE users "{\"name\": {\"type\": \"string\"}, \"age\": {\"type\": \"float\", \"min\": 18}, \"salary\": {\"type\": \"float\"}}" "[{\"fields\": [\"age\", \"salary\"], \"type\": \"normal\"}]"
DINSERT users "{\"name\": \"Spiderman\", \"age\": 13, \"salary\": 500}"
DINSERT users "{\"name\": \"Heman\", \"age\": 14, \"salary\": 600}"
DINSERT users "{\"name\": \"Superman\", \"age\": 15, \"salary\": 300}"
DINSERT users "{\"name\": \"Batman\", \"age\": 18, \"salary\": 900}"
DINSERT users "{\"name\": \"Antman\", \"age\": 25, \"salary\": 800}"
DEXPLAIN users "{\"filters\":[{\"field\":\"age\",\"operator\":\"$gt\",\"value\":14},{\"field\":\"salary\",\"operator\":\"$lt\",\"value\":900}]}"
DQUERY users "{\"filters\":[{\"field\":\"age\",\"operator\":\"$gt\",\"value\":14},{\"field\":\"salary\",\"operator\":\"$lt\",\"value\":900}]}"
VCREATE vectorname maxNeighbor levelFactor efSearch
- Create a vector store with maxNeighbor, levelFactor and efSearchVDROP vectorname
- Drop a vector storeVINSERT vectorname float [float...]
- Insert a vector in a vector storeVSEARCH vectorname float [float...] k
- Search k nearest neighbors of a vector in a vector store using HNSW algorithmVDELETE vectorname string
- Delete a vector from a vector store, input is the vector id returned inVINSERT
orVSEARCH
VCREATE vec 6 0.5 100
VINSERT vec 1.0 2.0
VINSERT vec 2.0 3.0
VINSERT vec 3.0 4.0
VSEARCH vec 1.5 2.5 2 // Returns 2 nearest neighbors
To run server run the following command on repository root
export TREDS_PORT=7997
go run main.go -port 7997
Using docker
docker run -p 7997:7997 absolutelightning/treds
Default Port of Treds is 7997
If port is set in env variable as well as flag, flag takes the precedence.
To build the binary for the treds server, run following command in repo root -
Binary named treds
will be generated in repo root.
make build
GOOS=linux GOARCH=arm64 make build
./treds
Treds encodes and decodes the messages in RESP so redis-cli can be used to interact with Treds server.
redis-cli -p 7997
It is advised to run Treds cluster on production. To bootstrap a 3 node cluster, lets say we have 3 servers
Sever 1, Server 2 and Server 3
On Server 1 run
./treds -bind 0.0.0.0 -advertise ip-server-1 -servers 'uuid-server-2:ip-server-2:8300,uuid-server-3:ip-server-3:8300' -id uuid-server-1
On Server 2 run
./treds -bind 0.0.0.0 -advertise ip-server-2 -servers 'uuid-server-1:ip-server-1:8300,uuid-server-3:ip-server-3:8300' -id uuid-server-2
On Server 3 run
./treds -bind 0.0.0.0 -advertise ip-server-3 -servers 'uuid-server-1:ip-server-1:8300,uuid-server-2:ip-server-2:8300' -id uuid-server-3
- Currently only KV Store gets persisted in Snapshot, add support for other store.
- Authentication.
- Tests
- More Commands ...