This is the first in a series of posts about Redis, a key-value database and open source project created by Salvatore Sanfilippo. Most web applications are built on a relational database, and the key-value database (if used at all) is a volatile cache. My goal is to show that Redis is a suitable replacement for a relational database and an exciting and powerful new technology on which to build web applications.
I’ll start with setup and basic commands; continue with lists, sets, and some fancier stuff like key expiration; and finish with a simple Ruby application that will demonstrate the covered topics.
Redis is the coolest bit of software I’ve seen in ages and I hope this series helps to spread the word and get more people to try it.
What Is Redis?
Redis is a key-value database. Unlike a relational database, which is comprised of tables containing columns of data definitions and rows of data, a key-value database stores values, each of which is referenced by a unique key. To get a value out of the database, we send the corresponding key. It’s a little like the hash, associative array, or dictionary data type in your favorite programming language.
Redis has several features that make it unique among key-value database projects:
- It stores strings, yes, but also lists and sets, so it’s flexible.
- It loads the entire dataset into RAM, so it’s fast.
- It writes the database to disk, so it’s persistent.
- It supports master-slave replication, so it’s scalable.
- It’s easy to run, so you aren’t endlessly futzing with mammoth config files
- Maybe most important, it’s easy to use, so you spend less time working with the database and more time writing your application.
I’ll cover each of these topics in greater depth as we go. Right now, let’s install it and start playing.
Redis is cake to build1. Observe:
% curl -O http://redis.googlecode.com/files/redis-1.02.tar.gz % tar xzf redis-1.02.tar.gz % cd redis-1.02 % make
redis-server binary is in the current working directory; we can run it directly, copy to
/usr/local/bin, or whatever.
To start Redis:
Redis is now available on localhost at port 6379. In a real application, we would interact with Redis via a client library for our language of choice. For now, we’ll just use telnet.
Open a new shell and run:
% telnet 127.0.0.1 6379
We’re now connected to Redis; let’s send it some commands.
Here’s our first command (I will prefix commands with
% but you won’t see an actual prompt):
% DBSIZE :0
DBSIZE returns the number of keys in the database. This is a new database with no keys, so Redis says
The database is empty, but let’s try to
GET a value:
% GET hello $-1
GET fetches the value at the provided key. The key
hello doesn’t exist, so Redis says
$-1, which is like
null. Let’s set a value for that key:
% SET hello 5 % world +OK
SET takes three parameters: the name of the key, the size of the value in bytes, and, after a newline, the value itself. Here, we
SET the key
hello to the
world, and Redis says
+OK. Now if we
% GET hello $5 world
We receive the value we just set, but with an extra line that reads
$5. Just like with
SET, the size of the value in bytes is returned first, then a newline, then the value.
We can ask Redis if a key exists:
% EXISTS bigkahuna :0 % EXISTS hello :1
:1 mean false and true, respectively.
Let’s delete the
% DEL hello :1
DEL deletes the value stored at the key
hello. The command succeeds and Redis says
:1, the number of records deleted. We can delete multiple keys by separating them with a space.
Say we want to set the value of a key only if that key doesn’t exist. Enter
% SETNX bigkahuna 22 % that is a tasty burger :1 % GET bigkahuna $22 that's a tasty burger % SETNX bigkahuna 4 % d'oh :0 % GET bigkahuna $22 that's a tasty burger
SETNX fails because the key
bigkahuna, having been created by the first
SETNX, now exists.
Let’s create a few more keys:
englosh. Set the value to whatever you want, remembering to enter the number of bytes and a newline first, then the value.
Redis returns data only if we feed it a valid key–it’s impossible to search for some value and get back its key. Suppose we don’t know what keys are in the database. How do we figure it out?
KEYS command searches all the keys for the provided pattern. Let’s get a list of every key in the database:
% KEYS * $33 englosh englash english bigkahuna
The pattern can include globs:
% KEYS *a* $17 englash bigkahuna % KEYS engl?sh $23 englosh englash english % KEYS engl[ia]sh $15 englash english
Finally, we can purge all of the keys from the database:
% FLUSHDB +OK % DBSIZE :0
Increment & Decrement
Redis lets us increment and decrement our values. First, let’s create a counter and set it to 1:
% SET counter 1 % 1 +OK
% INCR counter :2
% DECR counter :1
Nothing to it, right? We can also increment and decrement by values other than one:
% INCRBY counter 5 :6 % DECRBY counter 6 :0
Why do the increment and decrement commands exist? Say we’re using Redis to track the pageviews for a web site. Every URL maps to a key whose value is a counter. When a user visits a URL, we increment the counter. Imagine two visitors,
v2, visit the same URL,
/about, at the same time. Here’s how it might play out in pseudocode, using only the
redis.get("/about") == 5 # 5 visits so far v1 = redis.get("/about") # visitor 1 grabs the count v2 = redis.get("/about") # visitor 2 grabs the count redis.set("/about", v1 + 1) # visitor 1 sets the count + 1 redis.set("/about", v2 + 1) # visitor 2 sets the count + 1 redis.get("/about") == 6 # wha?? should be 7
The problem is clear:
v2 changed the counter after
v1 retrieved the value but before setting the new one. The increment and decrement commands prevent this problem because they’re atomic: reading the value and changing it occur in one motion and can’t be interrupted.
We can close both Redis and our telnet session by sending the
% SHUTDOWN Connection closed by foreign host.
And that’s it for part 1. Already, we know enough commands to build a real application, but Redis has much more to offer–next time, we’ll explore lists.