REDIS

What is Redis?

Database based on key-value storage

No tables or predefined schemas

No storage of complex multilevel documents

Blazing fast in execution of queries.

Getting and Setting String

SET <key> <value>

MSET to set multiple values - <key> <value> <key> <value>

GET to get one value - GET <key>

MGET to get multiple values

GETSET - Inserts new values and gets the old values - GETSET <key> <value> ( returns old value)

KEYS * - Lists all the keys

    • if value is of type string -> GET <key>

    • if value is of type hash -> HGETALL <key>

    • if value is of type lists -> lrange <key> <start> <end>

    • if value is of type sets -> smembers <key>

    • if value is of type sorted sets -> ZRANGEBYSCORE <key> <min> <max>

Cheat Sheet - https://gist.github.com/LeCoupa/1596b8f359ad8812c7271b5322c30946

String - Max length of 512mb.

Key can only be strings and values can be List, Set, Hashes, SortedSet

Using integer values reduces space in Redis

TYPE - To get the type of the value. TYPE <key> - returns value type

OBJECT - Refcount, Encoding, Idletime example OBJECT Refcount <key>

DUMP - To get the has the key, which can be used to restore DUMP <key> - returns hashed values

RESTORE -

EXISTS - Checks is key exits - EXISTS <key> - returns 1 if exists or 0 if it does not exist

KEYS - Returns all keys matching pattern - KEYS <key > ie. KEYS h?llo KEYS H*llo KEYS h[ae]llo

SCAN - Used to incrementally iterate a list of items -

REMANE - rename key - RENAME <keY> <newkay>

ATOMIC Numeric Counters -

INCREBYE - Increases the counter value . INCREBYE <key> <value to increase i.e INCRBYTE abc 3

DECREBYTE - Decreases integer value

For float we have no DECREFLOAT, we will need to use INCREFLOAT and set negative values to decrease the value

INCREFLOAT - increases the values of the float ( negative values are allowed, which can be used to decrease float values)

INCR and DECR - increment or decrement the values by 1. INCR <key> -no need to pass value

APPEND - Appends values. APPEND <key> <value to append> - adds the values to existing value

GETRANGE - Gets the range of the string - GETRANGE abc 0 2 - return the first 3 letters of the value

SETRANGE - Unlike Get, for set we need to specifiy offset values . SETRANGE <keY> <offset values> <new value> ex. SETRANGE abc 3 <newvalue>

Implementing Simple Cache:

2 types, condition based (manual coding) or Lifetime based cache values (provided by redis)

EXPIRY - expires in a given time range. EXPIRTY <key> <time> EXPIRTY abc 5 - expires in 5sec

TTL - get the expiry time of the key. TTL <key> - return the time period of the object, if you run the same tel command multiple times, you can see the value decresing

PERSIST - To persist a Key. PERSIST <key> this will persist the key. (even if an expiry is set). If -1 is returned it means key expiry is infinite

Monitoring and Debugging

Type INFO in the cli and it will provide detailed information about the CPU MEMORY and whole bunch of information about redis server and clients. The values showed in the INFO command are aggregated. So when there is a system issue and it is fixed, do a reset before running INFO.

You can reset the statistics when CONFIG RESETSTAT to reset the stats. This will be helpful when there is a issues and if it is fixed.

To monitor redis user MONITOR command in the redid-cli which will listen for logs and log all activities. If redis is on high load the monitoring may affect system performance, particularly when we have multiple monitoring.

Logging:

Default logging is to system out. Logs can be set to using the loglevel and logfile parameters in the redis configuration file.

Syslog also possible by enabling the parameter in config

Slowlog is a parameter where we can enable logging for queries which exceeds specific time, the parameter for this is slowlog-max-len <value in micro seconds> i.e slowlog-max-len 128.

We can also get the slowlog from cli using SLOWLOG GET. You can reset the slowlog by SLOWLOG reset.

DATA STORAGE

Redis perform operations/queries in memory. Data is persisted using 2 ways

RDB - Redis Database

- Performs point-in-time snapshots of redis database at specified interval either synchronously or

asynchronously.

- This can be triggered manually through cli using SAVE or BGSAVE commands

AOF - Append Only File

- Is a sequence of changes and not a snapshot unlike RDB.

- When you restart server the file is loaded to restore data into memory.

- Higher level of data durability.

- AOF files are append only

- Slows down server restarts.

- Files are big compared to RDB.

AOF can be turned on from the redis.conf file by setting the option appendonly option to yes, and setting the appendfilename option.

This can be manually triggered from cli using BGREWRITEAOF command

Master/Slave replication

We can have one master and one or more slaves. Slaves are read only and master is read/write.

Run master

src/redis-server --port 6379 --dbfilename db1.rdb

Run Slave

src/redis-server --port 6380 --dbfilename db2.rdb

In redis cli connect to master server and issue the following command

SLAVEOF <host> <port> i.e SLAVEOF localhost 6379

To remove the slave

SLAVEOF NO ONE

This will remove the slave

Sharding

Horizontal partitioning is called Sharding. There are 2 main partitioning

1. Range Partitioning and

2. Hash Partitioning

Range Partitioning:

When there is big dataset, it can be shared across servers, and this can only done from client side.

Range portioning is done by sending the date to appropriate database based on some conditional values. say userid 1-500 goes to redis1 and 500-1000 goes to redis2, you will need to programatically get the id and send to the appropriate database. Check range.py file attached.

The simplest way is range partitioning, mapping ranges of objects into specific Redis instances. For example ID 0 to ID 10000 are held by instance R0 while users with ID 10001 to ID 20000 are held by instance R1 and so forth. This system works and is used in practice. Its biggest disadvantage is that you need a table that maps ranges to instances. The table needs to be managed and you need a separate table for every type of object.

Hash Partitioning:

Here we can send date to different databases using the hash. Check hash.py attached.

An alternative to range partitioning is hash partitioning which works with any kind of key. Use a hash function (e.g. crc32) on the key name and you get a number, e.g. 93024922. Now use a modulo operation on this number to turn it into a number between 0 and 3. This number can then be mapped to one of the four Redis instances. In our case 93024922 modulo 4 equals 2, so I know my key foobar should be stored in the R2 instance.

REDIS CLUSTERING

Start first server with node1.conf

src/redis-server <path>/node1.conf

<node1.conf>

port 7000

cluster-enabled yes

cluster-config-file cluster-node-1.conf

cluster-node-timeout 5000

appendonly yes

appendfilename node-1.aof

src/redis-trib.rb create <server-ip>:<port <server-ip>:<port> ...

While connecting client we should specify -c option to specify we are connecting to client

src/redis-cli -c -h 127.0.0.1 -p 7000

./redis-trib.rb rebalance --weight 0fdc74e9=0 127.0.0.1:6801

/redis-trib del-node $host:$port ``

In order to empty out a master, you can use the rebalance command, but telling it to give the node a weight of 0

Remove a slave node from the cluster. To remove a master, use the same command, but the master must be empty (hold no keys).

---------------------------------------------------------------------------------------------

BUILD A SWARM CLUSTER WITH REDIS

Step1:

Create a swarm cluster

Step2:

Download redis4 image or build one

Create folders for bind mounting the data

Step3:

Create a Docker service ( one for each redis instance with different ports)

docker service create \

--name redis1 \

--hostname redis1 \

--mount type=bind,source=/home/siftuser/sift/redis/data/redis1-master,destination=/data \

--mount type=bind,source=/home/siftuser/sift/redis/redis1.conf,destination=/usr/local/etc/redis/redis.conf \

-p 6401:6401 \

-p 16401:16401 \

--constraint 'node.hostname == sit1' \

--network redis \

knowesis/redis:4.0.8 redis-server /usr/local/etc/redis/redis.conf

Step4:

Edit the redis.conf file (one for each redis masters)

redis.conf ( leave other fields to default apart from those listed below)

bind (comment if want to expose redis on all interfaces)

port 6401 (set the port where the service will be running) - set different port for different redis servers, can be same also)

pidfile <filename> - change the name with port (optional)

cluster-enabled yes

cluster-config-file <filename> (optional)

cluster-node-timeout 15000

cluster-announce-ip 10.21.11.141 (set the physical ip of host)

cluster-announce-port 6401 ( port of redis server)

cluster-announce-bus-port 16401 (port of the bus - this port should be open in docker service)

Step5: CREATE CLUSTER

Redis cluster can be created using the redis-trib.rb ruby script. Spin up ruby container and execute commands inside the container to form redis cluster

docker service create \

--hostname ruby \

--name ruby \

--constraint 'node.role==manager' \

--network redis \

ruby sh -c 'gem install redis && wget http://download.redis.io/redis-stable/src/redis-trib.rb && sleep infinity'

Login into ruby container and execute

ruby redis-trib.rb create 10.21.11.141:6401 10.21.11.143:6402 10.21.11.141:6403

(here 2 master are running in 141 host and one master in 141 host)

Step5: Add Slaves

Create redis slave service (3 slaves - so 3 services). Bring up all the services before adding the slaves.

docker service create \

--name redis1-slave \

--hostname redis1-slave \

--mount type=bind,source=/home/siftuser/sift/redis/data/redis1-slave,destination=/data \

--mount type=bind,source=/home/siftuser/sift/redis/redis1-slave.conf,destination=/usr/local/etc/redis/redis.conf \

-p 6501:6501 \

-p 16501:16501 \

--constraint 'node.hostname == sit3' \

--network redis \

knowesis/redis:4.0.8 redis-server /usr/local/etc/redis/redis.conf

Step6: Add slaves to master

Login into the ruby container and execute

Get the mastered ids by using the following command in redis-cli (from any node)

redis1:6401> cluster nodes

042fe8225492441e0775e97e3cb002c1b4116a51 10.21.11.141:6401@16401 myself,master - 0 1521715673000 1 connected 0-5460

b473fab3ece6b9549c72911e264f0c49ceea4ad0 10.21.11.143:6402@16402 master - 0 1521715674034 2 connected 5461-10922

4d4e3f10fba66e177083832550c88b214387b181 10.21.11.141:6403@16403 master - 0 1521715675035 3 connected 10923-16383

This will provide list of nodes with master ID.

Login into ruby container and execute the following command to add node(s)

ruby redis-trib.rb add-node --slave --master-id 042fe8225492441e0775e97e3cb002c1b4116a51 10.21.11.143:6501 10.21.11.141:6401

master-id <id> <new node:port> <anynode in cluster IP:port>

Repeat the command with different nodes ip and port.

Now with cluster nodes in redid-cli will show all the nodes with associated master/slave

Step7: Monitoring (optional)

docker service create \

--name reddie \

--hostname reddie \

-p 443:443 \

--network redis \

--constraint 'node.role==manager' \

-e CONNECT=redis1:6401 \

get-reddie.com/reddie

Run the Reddie service to run the monitor the redis cluster visually.

--------

SETTING UP CLUSTER - MANUALLY

redis-trib was used to add nodes to cluster, which is easy way of configuring the cluster. But to understand better on how cluster work, lets look at forming cluster without using redis-trib

Step1 - Bring up all the master nodes with the configuration

port <port>

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

appendonly yes

This will bring up the server is cluster mode but has not yet formed a cluster. Run CLUSTER NODES to see the nodes. To form the cluster, we need to use the CLUSTER MEET command.

Note: Cluster meet only accepts IP address and not hostname

Step2: Form the cluster

redis-cli -h <host> -p <port> CLUSTER MEET <IP> <port>

eg. redis-cli -h master1 -p 6401 CLUSTER MEET 10.21.112.141 6402

Repeat the cluster meet command by passing other nodes in cluster.

If you check the CLUSTER NODES, you can find the cluster is formed.

Step3: Set HASH SLOT for the nodesc

Node1: Allocate the slots 0-5400 to node1

for slot in {0..5400}; do redis-cli -h master1 -p 6379 CLUSTER ADDSLOTS $slot; done;

Node2: Allocate the slots 5401-10800 to node2

for slot in {5401..10800}; do redis-cli -h master1 -p 6379 CLUSTER ADDSLOTS $slot; done;

Node3: Allocate the slots 10801-16383 to node3

for slot in {10801..16383}; do redis-cli -h master1 -p 6379 CLUSTER ADDSLOTS $slot; done;

Step4: Start Slave servers

Start 3 slave nodes with the similar configuration of master as shown in step1.

Step5: Add Slaves to Master

Slave1 - redis-cli -h slave1 -p <port> CLUSTER REPLICATE <masternodeid>

Slave2 - redis-cli -h slave2 -p <port> CLUSTER REPLICATE <masternodeid>

Slave3 - redis-cli -h slave3 -p <port> CLUSTER REPLICATE <masternodeid>

At this point, Redis cluster is configured properly with 3 master and 3 slaves. Again, running the CLUSTER NODES command will show the master and slaves correctly configured.

Step4: Adding/Removing nodes

Adding and removing nodes can be done without any downtime. Say we want to migrate all the slots from server 3 to server4. Bring up a new server with the above configurations as mentioned in step1. Using cluster meet add it to cluster as shown in step2.

Migrating hash slots procedure has few steps to be followed

On the cluster node which owns the specific hash slot (master3), the command CLUSTER SETSLOT slot MIGRATING should be executed, where is the Node ID of the new node master4

redis-cli -h master3 -p <port> CLUSTER SETSLOT 15929 MIGRATING <nodeid-master4>

When a slot is marked as MIGRATING, the node will accept all the requests for queries that are about this hash slot, but only if the given key exists, otherwise the query is forwarded to the node that is target of the migration.

On the cluster node which should become a new owner of the specific hash slot (master4), the command CLUSTER SETSLOT slot IMPORTING, where is the Node ID of current owner master3

redis-cli -h master4 -p <port> CLUSTER SETSLOT 15929 IMPORTING <nodeid-master3>

At this point all the keys from has slot should be migrated using the MIGRATE command.

example

redis-cli -h master3 -p <port>MIGRATE master4 <port> KEYS key1 key2 key3

Though we have more simpler method of migration to new server, the above method will help to understand how node migration works.

Simplest way could be adding a new node (master4) as slave of master and stop the master which will make the slave as master(redis 4.08)

-- BASIC STEPS FOR ADDING NEW NODE

    1. Start new redis instances - Let's say you want to add 2 more master and there slaves (Total 4 redis instances)

    2. Then using redis-trib utility do following :

      1. redis-trib.rb add-node <new master node:port> <any existing master> e.g. ./redis-trib.rb add-node 192.168.1.16:7000 192.168.1.15:7000

    3. After this new node will be assigned an id . Note that id and run following command to add slave to node that we added in prev step

      1. /redis-trib.rb add-node --slave --master-id <master-node-id> <new-node> <master-node> ./redis-trib.rb add-node --slave --master-id 6f9db976c3792e06f9cd252aec7cf262037bea4a 192.168.1.17:7000 192.168.1.16:7000where 6f9db976c3792e06f9cd252aec7cf262037bea4a is id of 192.168.1.16:7000.

    1. Using similar steps you can add 1 more master-slave pair .

    2. Since these node do not contains any slots to serve, you have move some of the slots from existing masters to new masters .( Re-Sharding)

    3. To that you can run following command/Resharding steps :

      1. 6.1 ./redis-trib.rb reshard <any-master-ip>:<master-port>

      2. 6.2 It will ask : How many slots do you want to move (from 1 to 16384)? => Enter number of slots you want to move

      3. 6.3 Then it will ask : What is the receiving node ID?

      4. 6.4 Enter node id to which slots need to be moved . (new masters)

      5. 6.5 It will prompt : Please enter all the source node IDs. Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs.Source node #1: (enter source node id or all)

      6. 6.6 Then it will prompt info saying Moving slot n to node node-id like

      7. Moving slot 10960 from 37d10f18f349a6a5682c791bff90e0188ae35e49 Moving slot 10961 from 37d10f18f349a6a5682c791bff90e0188ae35e49 Moving slot 10962 from 37d10f18f349a6a5682c791bff90e0188ae35e49

      8. 6.7 It will ask : Do you want to proceed with the proposed reshard plan (yes/no)? Type Yes and enter and you are done .

Note : If data is large it might take some time to reshard.