Memcached Tutorial

By Maurizio Farina | Posted on December 2017

Overview

Memcached is an open-source, in-memory key-value store used to manage cache layer (strings, objects) in our application: results of database calls or API calls are few examples of object that can be managed using memcached.

Memcached API are available for most popular languages.

Memcached uses LRU (Least Recently Used): items expire after a specified amount of time.

Memcached

Why use a tool like Memcached?

Local Cache

Local Cache

In the above architecture, the cache is part of the application server. In this case, some of the memory is allocated for cache from the heap size allotted to the JVM. If the cache size increases, then the heap size need to increase. There is no better control over the cache, because, cache is bundled with app server. To have better control, we need to separate the cache from the app server. The below architecture depicts the same.

Cache managed using Memcached

Memcached Cache

In the above architecture , we have separated the cache from the application. So, either we can maintain the cache in the same server where the application is deployed or we can assign separate machine for the cache. If the cache size increases, we can increase the RAM size with out effecting the application. So, we have better control on the cache.

Memcached commands

The Memcached server will run by default on 11211 port so we can play with Memcached directly from telent.

Before to list the Memcached commands take a look to the storage commands parameters:

Set command

1
2
set key flags exptime bytes [noreply] 
value 
  • key: It is the name of the key by which data is stored and retrieved from Memcached.
  • flags: It is the 32-bit unsigned integer that the server stores with the data provided by the user, and returns along with the data when the item is retrieved.
  • exptime: It is the expiration time in seconds. 0 means no delay. If exptime is more than 30 days, Memcached uses it as UNIX timestamp for expiration.
  • bytes: It is the number of bytes in the data block that needs to be stored. This is the length of the data that needs to be stored in Memcached.
  • noreply (optional): It is a parameter that informs the server not to send any reply.
  • value: It is the data that needs to be stored. The data needs to be passed on the new line after executing the command with the above options.

Output:

  • STORED: STORED indicates success.
  • ERROR: indicates incorrect syntax or error while saving data.
Command Description Examples
get Reads a value get <>
set Set a key unconditionally
add Add a new key add <> 0 60 5
replace Overwrite existing key replace key 0 60 5
append Append data to existing key append key 0 60 15
prepend Prepend data to existing key prepend key 0 60 15
incr Increments numerical key value by given number incr mykey 2
decr Decrements numerical key value by given number decr mykey 5
delete Deletes an existing key delete mykey
flush_all Invalidate specific items immediately or Invalidate all items in n seconds flush all
stats Prints general statistics
version Prints server version
verbosity Increases log level

An Example

a real example:

1
2
3
4
5
6
7
set mkjava 0 1000 18
memcached tutorial
STORED
get mkjava
VALUE mkjava 0 9
memcached tutorial
END

a Java example:

In order to use Memcached with Java we need a Java Memcached client. We use spymemcached: a simple, asynchronous, single-threaded Memcached client written in Java.

Configuring pom file:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/net.spy/spymemcached -->
<dependency>
    <groupId>net.spy</groupId>
    <artifactId>spymemcached</artifactId>
    <version>2.12.3</version>
</dependency>

a simple Java main

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.listfeeds.runner.utils;

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.spy.memcached.AddrUtil;
import net.spy.memcached.MemcachedClient;

public class Memcached {

    private static final Logger log = LoggerFactory.getLogger(Memcached.class);

    public static void main(String[] args) {

        // establish a connection to the Memcached server
        MemcachedClient mcClient = null;
        try {
            mcClient = new MemcachedClient(AddrUtil.getAddresses("127.0.0.1:11211"));
            log.debug("Connection to server sucessfully");

            // setting a new key
            Future<Boolean> result = mcClient.set("mkjava", 1000, "memcached tutorial");
            log.debug("set status: {}", result.get());

            // getting value from memcached
            log.debug("Get from Cache: {}", mcClient.get("mkjava"));
        } catch (IOException e) {
            log.error("Trouble to establish a connection to the Memcahed", e);
        } catch (InterruptedException e) {
            log.error("Trouble to set key to the Memcahed", e);
        } catch (ExecutionException e) {
            log.error("Trouble to set key to the Memcahed", e);
        }

        log.error("Closing connection to the Memcahed");
        mcClient.shutdown();
    }
}

A small snippet to manage authentication (extracted from Redis web site):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class spymemcachedExample {
    public static void main(String[] args) {
        AuthDescriptor ad = new AuthDescriptor("PLAIN", 
            new PlainCallbackHandler("username", "password"));

        try {
            MemcachedClient mc = new MemcachedClient(
            new ConnectionFactoryBuilder()
                .setProtocol(ConnectionFactoryBuilder.Protocol.BINARY)
                .setAuthDescriptor(ad).build(),
            AddrUtil.getAddresses(new String[] { "hostname:port" }));
        } catch (IOException e) {
            // handle exception
        }
    }
}

Memcahed in High Availability

Memcached servers does not know about each other so all the routing and balancing is in the client library.

From Memcached wiki site:

1
2
3
4
5
Logic Half in Client, Half in Server

A "memcached implementation" is partially in a client, and partially in a server. Clients understand how to choose which server to read or write to for an item, what to do when it cannot contact a server.

The servers understand how store and fetch items. They also manage when to evict or reuse memory.

An example to clarify: A client library can use crc32 hash to match an available server.

For example, the key "mkjava" with crc32 return "0xB514C2B5" (equals to 3038036661 in decimal), If we immagine to have 45 Memcahed server the client performs 3038036661 mod 45; The result is 36 so the client will pick the 36'rd server.

Other strategie can depends from the client used.

It is possible to store Memcached repository to MySQL using The InnoDB memcached Plugin and Replication.

References