What is Memcached?
--
Memcached is a free and open-source high-performance memory caching system. It’s typically used to cache database data, API calls or page rendering chunks in RAM to increase the application performance.
It can store data as little as a number, or as big as a finished HTML page.
The system is designed to be accessed through TCP so it can work in a separate server, and can also be distributed among several servers, summing up a big hash table to store data.
When running out of space, the older data is overwritten. So it must be treated as a transitory (not-persisted) cache, meaning that your data may not still be there.
Installation
If you’re using macOS, it’s very easy to install Memcached using Homebrew:
$ brew install memcached
Then you can start it using this command:
$ /usr/local/opt/memcached/bin/memcached
If you are using Linux, you can use your distribution’s package manager to install it easily.
Connecting
The default port is 11211. Let’s try to connect using Telnet:
$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Great! The server is up. Now quit Telnet (using the quit
command) and let’s install a Ruby gem to interact with the server more comfortably:
gem install dalli
Run a Ruby console (irb
) and connect to the server using the default port:
require 'dalli'
cache = Dalli::Client.new('localhost')
=> #<Dalli::Client:0x007f999d867e80 @servers=["localhost"], @options={}, @ring=nil>
Basic storage
Let’s start playing with Memcached storing a string and then getting it:
cache.set 'greet', 'hello'
cache.get 'greet'
=> "hello"
As you see, set
is used to set a value to a key, which will be overwritten if it already exists. get
is used to get the key value.
Data can also be limited by some time in seconds:
cache.set 'greet', 'hello', 5
cache.get 'greet'
=> "hello"
sleep 5
cache.get 'greet'
=> nil
Multiple values can be get in a unique query using get_multi
, resulting in a hash:
cache.set 'greet1', 'hello'
cache.set 'greet2', 'good morning'
cache.get_multi 'greet1', 'greet2'
=> {"greet1"=>"hello", "greet2"=>"good morning"}
Let’s free some memory by deleting those greetings:
cache.delete 'greet1'
cache.delete 'greet2'
cache.get_multi 'greet1', 'greet2'
=> {}
If you want to delete all keys, the flush
operation is what you need:
cache.flush
=> [true]
Adding data
The set
command overwrites the value if the key already exists:
cache.set 'greet', 'hello'
cache.get 'greet'
=> "hello"
cache.set 'greet', 'good morning'
cache.get 'greet'
=> "good morning"
To avoid this, the add
command can be used instead, which will create the value if the key doesn’t exist:
# It doesn't overwrite an existing key (defined in the previous code block)
cache.add 'greet', 'good evening'
cache.get 'greet'
=> "good morning"# It creates the value for new keys
cache.add 'greet2', 'good evening'
=> "good evening"
Replacing data
replace
is used to replace data for an existing key:
cache.replace 'greet', 'hi there!'
cache.get 'greet'
=> "hi there!"
But it will do nothing if the key doesn’t exist:
cache.replace 'non existing greet', 'hi there!'
=> nil
Appending and prepending data
In order to append or prepend data, Dalli requires it to be previously set as raw:
cache.set 'greet', 'hello', 0, raw: true
Now we can append:
cache.append 'greet', '!'
cache.get 'greet'
=> "hello!"
And prepend:
cache.prepend 'greet', 'hey! '
cache.get 'greet'
=> "hey! hello!"
Increment and decrement data
Another useful case is to have a counter, so it can be incremented or decremented.
cache.set 'counter', 1, 0, raw: true
cache.incr 'counter'
=> 2
cache.incr 'counter', 2
=> 4
cache.decr 'counter'
=> 3
cache.decr 'counter', 3
=> 0
Check and set
Imagine having a value that can be updated by any concurrent client (trying to update it at the same time). Wouldn’t that be a problem? That’s a race condition that can be controlled using the cas
operation, that checks if the value has been changed by other client before updating it.
It works like a set
operation, but it requires a block:
cache.set 'config', 'foo'
cache.cas('config') { 'bar' }
cache.get 'config'
=> "bar"
Therefore, this is an atomic operation.
Conclusion
We have seen what’s Memcached and how it can be used directly using the Dalli gem. It may seem simple, but it can help a big web application to work faster like many important companies are doing, such as YouTube, Reddit, Facebook, Twitter, and Wikipedia.
Sometimes it’s essential to work with Memcached directly, for instance when developing a service with special needs. There are other times where the framework (Ruby on Rails for example) can store its cache out of the box in Memcached using a gem like Dalli.