A Hands On Beginners Guide to Redis
For the uninitiated, let's talk about why Redis is a great solution for adding speed and power to your application stack.
Yesterday I wrote a post about using Redis to create a simple chatroom, but today I wanted to go back to the basics and define what Redis is and how we can get started with it.
So what is Redis then?
Redis is a super fast non-relational database that uses keys to map to different data types. It is a NoSQL solution which roughly means that there are no tables or relationships between the data you store. Without the common rules of a relational database, Redis is quite flexible in how you can interact with it. Like memcached, it is in-memory storage which is why it's so incredibly fast. Unlike memcahced though, it allows more complex data structures giving it the ability to handle many different problem sets.
Will it replace your MySQL instance?
That's up to you. In some cases it can, but from my experience Redis is a tool that really comes into play when you need to optimize your application's performance.
Another nice thing about Redis is that it has persistence built in even though the database it is in-memory.
Still, what makes Redis a viable solution really comes down to the data types and its general ease. We can use list types to handle growing data sets which allows us to add and remove items efficiently. In memcached, we would be limited to string types and adding and removing becomes much more complex in our codebase. We can use sorted sets to handle scoring data and regular sets for simple set arithmetic.
In the end though, it's the speed that has attracted me to Redis most. Since it's in-memory it's fast anyways, but we avoid query parsers when we write to Redis which is another level of speed gained. Getting, updating, and deleting data is done in nearly real-time due to the fact that we don't need to scan all of our data. It's honestly hard to beat in the realm of NoSQL.
If you're still not convinced, let's install it and play around.
The official Redis website has a guide for installation. They recommend you install the source to ensure that you get the latest version. If you'd like to do so, definitely check out that link and do the simple make build. However, you can also install it from your package manager. For me, this means Ubuntu with apt-get.
$ sudo apt-get install redis-server
Installing redis-server will give you access to the following commands:
- redis-server: The running Redis instance.
- redis-sentinel: Redis Sentinel executable (monitoring and failover stuff)
- redis-cli: The command line interface for interacting with Redis.
- redis-benchmark: Analyze Redis's performance.
Let's test this and ensure that we have everything installed. Go to your terminal and open the
$ redis-cli redis 127.0.0.1:6379>
If all went well, you'll see the interactive prompt open giving us the local server address on the default Redis port of 6379. For the rest of this tutorial, commands I run will be in this redis-cli environment.
Let's do a quick test command to ensure that the cli is working for us.
redis 127.0.0.1:6379> ping PONG
If you're still with me, you're all set up. Let's check out some of the data types.
Like many programming languages, a string is simply a sequence of characters. We can store integers, words, and even full sentences here. It's the most basic type in Redis and very easy to use. Let's jump into the redis-cli and see some basic commands.
redis 127.0.0.1:6379> set hello world OK redis 127.0.0.1:6379> get hello "world" redis 127.0.0.1:6379> del hello (integer) 1 redis 127.0.0.1:6379> get hello (nil)
I did a few commands in succession so let me break down what we just did. With Redis strings, we create a new one by using the
set command. This command takes the form of
set key value. So for our example, we create a new entry with a key of "hello" and a value of "world". The redis-cli tells us that the command was "OK" when things work well.
Since we have an entry set, we can access that entry with the
get command. We fetch entries based on their key so we use
get hello to fetch our value of "world".
Deleting entries is done with the
del command. Again, we reference the key and run
del hello which returns a "1" representing the number of items deleted.
Before moving forward, take note about the ease of these commands. They are intuitive and quick. For all of the Redis data types, you'll see that the same basic commands exist so understanding the most simple command structures will give you a good foundation to understand the others.
Unlike most key-value stores, Redis supports the linked-list type which is a huge advantage for us. We can store groups of strings and integers in our lists giving us more viable storage solution in different problem sets.
Working with lists is quite simple. Let's see how we would add items to a list.
redis 127.0.0.1:6379> rpush my_list "hello world" (integer) 1 redis 127.0.0.1:6379> rpush my_list bacon (integer) 2 redis 127.0.0.1:6379> rpush my_list donuts (integer) 3
We use the
rpush command which translates to "push this value on the right end of my list". Every time we add an item to the list, it returns the list length for our reference. Let's grab some values.
redis 127.0.0.1:6379> lrange my_list 0 -1 1) "hello world" 2) "bacon" 3) "donuts"
We use the
lrange command which translates to "from this list, get this range". In our command, we specify the beginning and end using indexes much like that of Python and other languages. We can also select a single value by index:
redis 127.0.0.1:6379> lindex my_list 2 "donuts"
We use the
lindex command which translates to "from this list, get this index". Again, take note at the commands and see that they are quite intuitive. Let's remove an element.
redis 127.0.0.1:6379> lpop my_list "hello world" redis 127.0.0.1:6379> lrange my_list 0 -1 1) "bacon" 2) "donuts"
Like a programming language, we can pop items off the list. We do so with the
lpop command. Notice that in Redis, we remove items from the left side of lists. First in, first our mentality here.
Let's see Redis sets.
Sets are very similar to lists, but they take the form of a hash table in a way. They store unique values and are unordered. For this reason, we work with them based on value instead of by index. Let's create a set and add items to it:
redis 127.0.0.1:6379> sadd my_set value1 (integer) 1 redis 127.0.0.1:6379> sadd my_set value2 (integer) 1 redis 127.0.0.1:6379> sadd my_set value3 (integer) 1 redis 127.0.0.1:6379> sadd my_set value1 (integer) 0
We use the
sadd command which translates to "add to set". This works just like a list, but instead of returning a size, it returns a "1" if the item was added and a "0" if the item is already in the set. Let's grab the set values.
redis 127.0.0.1:6379> smembers my_set 1) "value2" 2) "value3" 3) "value1"
Unlike lists, we don't care about a range since sets have no order. We use the
smembers command to get all the members of a particular set. We can also check membership of values like so:
redis 127.0.0.1:6379> sismember my_set value1 (integer) 1 redis 127.0.0.1:6379> sismember my_set value (integer) 0
sismember commands says "is the value a member of this set". The return values should make sense here. We can remove values from a set as well:
redis 127.0.0.1:6379> srem my_set value1 (integer) 1 redis 127.0.0.1:6379> smembers my_set 1) "value2" 2) "value3"
We use the
srem to remove a value from the set. Working with sets is usually much easier than a list, but they require you to know the values rather than relying on an index. Again, there are some situations where this is a better solutions than a list. Either way, Redis has nice and complex data types.
When lists and sets aren't working for you, hashes come to the rescue and give you a key value mapping within a key. They're like Python dictionaries and very useful. Let's create a hash and set some values.
redis 127.0.0.1:6379> hset my_hash key1 value1 (integer) 1 redis 127.0.0.1:6379> hset my_hash key2 value2 (integer) 1 redis 127.0.0.1:6379> hset my_hash key1 value3 (integer) 0
We use the
hset command which translates to "using this hash, set this value". It takes the form of
hset main_key sub_key value. Like a set, the pairs are unique and will return a "0" if it is already in the hash. We can get our hash values:
redis 127.0.0.1:6379> hgetall my_hash 1) "key1" 2) "value1" 3) "key2" 4) "value2"
We use the
hgetall command which translates to "get from my hash all values". Notice the weird output though. Keys and values don't match up together in the output. This is ok, it's just how Redis will dump the data for us. You can see that all is fine by deleting data.
redis 127.0.0.1:6379> hdel my_hash key1 (integer) 1 redis 127.0.0.1:6379> hgetall my_hash 1) "key2" 2) "value2"
As we can see, the
hdel command deletes from the hash and after getting all the members again, we see that it worked despite the odd output format. We can get members of course as well:
redis 127.0.0.1:6379> hget my_hash key2 "value2"
We use the
hget command to get the value for our hash and the sub key. Hopefully you're starting to pick up on the similarities between the commands. It gets easy to figure out based on the data type what the command is. Let's see the last data type in redis: sorted sets.
Like hashes, sorted sets store key value pairs, but the types are more limited. The keys are all unique and can be anything, but the values, which Redis dubs as "scores" must be integers and floating points. This is because our sorted sets are actually sorted based on these scores. This is how we have order. One thing to notice with these commands is that they are prefaced with a "Z" since "S" is already taken by sets. Let's add some items to a sorted set (zset).
redis 127.0.0.1:6379> zadd my_zset 100 first (integer) 1 redis 127.0.0.1:6379> zadd my_zset 300 second (integer) 1 redis 127.0.0.1:6379> zadd my_zset 10 third (integer) 1
We use the
zadd command to add items to a sorted set. It takes the format of
zadd set_name score member. Let's see what we have in our sorted set now.
redis 127.0.0.1:6379> zrange my_zset 0 -1 withscores 1) "third" 2) "10" 3) "first" 4) "100" 5) "second" 6) "300"
We use the
zrange command, which is much like we do with lists, to get the members of our zset. I added the "withscores" flag to also return the actual scores with each member. As we can see, these are in ascending order based on the score making our members seem out of place.
Given that these have scores attached to them, we can get a range based on these scores as well.
redis 127.0.0.1:6379> zrangebyscore my_zset 0 200 withscores 1) "third" 2) "10" 3) "first" 4) "100"
We use the
zrangebyscore command which gets us a range of items, but instead of indexes we can specify a score range. This is useful when you are getting subsets of values and you don't care about the indexes. We can of course remove members from the zset:
redis 127.0.0.1:6379> zrem my_zset first (integer) 1 redis 127.0.0.1:6379> zrange my_zset 0 -1 1) "third" 2) "second"
We use the
zrem command to remove members just like we've seen in other data types.
I just scratched the surface with what Redis is, how to install it, and the basic data types. The fun comes in the operations we can do with these data types and how we can leverage them to solve real world problems. In my next post, I'll get into a more exhaustive guide on what commands you can do for each data type.
After that, we'll talk about use cases with some examples.
comments powered by Disqus