LiveJournal then ran on about 70 machines for 2.5 million accounts. In his 2004 article in Linux Journal, Fitzpatrick wrote that he wanted "a global hash table that all Web processes on all machines could access simultaneously", and reported servers up 4–5 months at a time, averaging 7,000 queries per second.
Keeping an in-memory database alive; caching and memcached
After this lesson you will say what Redis does so that a crash or a dead machine costs little — snapshots, logs, slaves, Sentinel, Cluster and its 16384 slots — and when a cache that forgets on purpose, like memcached, is worth having at all.
Two programs keep all their data in RAM, and you pull the plug on both. The slide of the first is a list of machinery for surviving exactly that: snapshots on disk, a log of every write, copies on other machines, and automatic ways to put a copy in charge when a machine dies (p. 359). The slide of the second reads like a confession: "No persistency (if your server goes down, data in memcached is gone)", "No redundancy / fail-over", "No replication (single item in cache lives on one server only)" (p. 381).
Yet four slides earlier the second program is "used by Netlog, Facebook, Flickr, Wikipedia, Twitter, YouTube" (p. 377). Which of the two is badly designed?
Neither — and the reason sits in a single word of the definition of a cache. By the end of the lesson you will know that word, the four kinds of machinery the first program needs, and the one ratio that decides whether the second is worth having at all.
Four ways to keep RAM from forgetting
Redis answers from memory, which is why it is fast (lesson 17). Memory has two weaknesses: it forgets everything when the process or the machine stops, and one machine has only so much of it. The lecture is frank about the price of fixing the first: "increased persistency will tend to increase latency and decrease throughput" (p. 349).
Page 359, "Scaling Redis", answers with four headings, and each one guards against a different accident (Fig. 1):
Persistence — "Redis provides two mechanisms to deal with persistence: Redis database snapshots (RDB) and append-only files (AOF)." Against: the process stops and the RAM is wiped.
Replication — an instance known as the master "ensures that one or more instances … the slaves, become exact copies of the master. Clients can connect to the master or to the slaves. Slaves are read only by default." Against: a whole machine is lost.
Partitioning — "Breaking up data and distributing it across different hosts in a cluster", in one of three layers: the client ("Partitioning on client-side code"), a proxy ("An extra layer that proxies all redis queries and performs partitioning", e.g. Twemproxy) or a query router ("instances will make sure to forward the query to the right node", e.g. Redis Cluster). Against: the data outgrows one machine.
Failover — "Manual", "Automatic with Redis Sentinel (for master-slave topology)" or "Automatic with Redis Cluster (for cluster topology)". Against: the master dies and nobody accepts writes.
Persistence survives a restart, replication survives a lost machine, partitioning survives growth, failover keeps the writes going when the master dies.
Snapshot or log?
The slide only names the two; the mechanics are from the Redis documentation. An RDB snapshot is a photograph: every few minutes, or sooner when many keys change, Redis writes a point-in-time copy of the whole dataset into one compact file. An AOF is a ledger: every write command is appended to a log, which Redis replays at restart.
A crash therefore costs different amounts (Fig. 2). The snapshot loses everything written since it was taken — minutes. The log loses only what had not reached the disk: about a second with the default policy (one flush per second), nothing if the file is synced after every write. That last setting is the "increased persistency" of p. 349: every write waits for the disk. In exchange, a snapshot is smaller and faster to reload than a log of every command. With both switched on, Redis restarts from the log, the more complete of the two.
RDB: a snapshot every few minutes
AOF: a log of every write
Four topologies on one two-by-two
Page 360 lists four arrangements, each with its promise in brackets (the rendering jumbles the words): "I. Standalone", "II. Sentinel (automatic failover)", "III. Twemproxy (distribute data)", "IV. Cluster (automatic failover and distribute data)". Two capabilities, four combinations.
I · Standalone: copies, and nobody to promote them
Page 361 draws three shapes: a master alone, a master with slaves, and slaves of slaves ("multi-level"). The slaves give "data redundancy, reads offloading and save-to-disk offloading": a copy if the master's machine dies, more machines for reads, and a machine that writes the snapshots and logs instead of the master. "Clients can connect to the Master for read/write operations or to the Slaves for read operations." Then the line that matters: "There is no automatic failover."
Why read-only slaves? One machine orders the writes, so the copies never disagree about them. The copying is asynchronous (from the Redis documentation): the master answers before the slaves have the write, so a slave promoted after a crash may lack the last writes.
II · Sentinel: a watchdog that promotes
"Redis Sentinel provides a reliable automatic failover in a master/slave topology, automatically promoting a slave to master if the existing master fails" (p. 362). The figure puts a Sentinel on each of three servers, watching the master and the slaves and talking to each other. Why several? So that one Sentinel with a broken cable cannot depose a healthy master: the Redis documentation asks for at least three, and a failover needs the vote of a majority of them. The second bullet: "Sentinel does not distribute data across nodes" — every slave still holds the whole dataset.
III · Twemproxy: a proxy that splits
"Twemproxy works as a proxy between the clients and many Redis instances" — the second picture runs three of them behind a load balancer — and "Is able to automatically distribute data among different standalone Redis instances" (p. 363). It "Supports consistent hashing": servers and keys are hashed onto the same circle, and a key belongs to the first server point after it, clockwise; when a server joins or leaves, only the keys next to its points move, about one in n for n servers, where "hash mod the number of servers" would move almost all. Twemproxy "is a project from Twitter and is not part of redis"; "Multi-key commands and transactions are not supported" — such a command may need several servers — and there is no failover: a dead instance takes its keys with it.
"Not supported" — really none of them?
Twemproxy's own notes are finer: it splits MGET, MSET and DEL by server (MSET is then not atomic); SINTER or RPOPLPUSH work only when all their keys hash to one server, which hash tags arrange; MULTI/EXEC transactions do not work at all. At the exam, give the slide's sentence.
IV · Cluster: slots with owners
"Redis Cluster distributes data across different Redis instances and perform automatic failover if any problem happens to any master instance" (p. 364). "All nodes are directly connected with a service channel": they check on each other and agree on who is alive. "The keyspace is divided into hash slots. Different nodes will hold a subset of hash slots." The figure adds the numbers — "Slots 0-6000", "Slots 6001-12000", "Slots 12001-16383": 16384 hash slots in all, each owned by one master. Which slot a key falls into is fixed by a formula of the Redis Cluster specification:
- \(k\)
- the key, as a string
- CRC16
- a 16-bit checksum of the key's bytes, a number from 0 to 65535
Key foo lands in slot 12182, so in the slide's cluster it lives on master C. When a master fails, one of its slaves is promoted and takes over its range of slots; the formula never changes, so every key keeps its slot. The last bullet: "Multi-key commands are only allowed for keys in the same hash slot." A command that needs two keys must run on one machine, atomically. user:42:name and user:42:city land in slots 6755 and 8210 — both on master B, yet the command is refused, because slots, not machines, are the unit that moves between masters:
redis> MGET user:42:name user:42:city
(error) CROSSSLOT Keys in request don't hash to the same slot
The way out is a hash tag: when a key contains {…}, only the part inside the first pair of braces is hashed. {user:42}:name and {user:42}:city both hash user:42 and share slot 15880:
redis> MSET {user:42}:name "Ada" {user:42}:city "Milano"
OK
Now kill some masters yourself. The widget shows one insight: copies are not failover, and failover is not distribution — only Cluster has both.
| every master holds all the data | data distributed over masters | |
|---|---|---|
| no automatic failover | I · Standalone (master only, master-slave, multi-level) | III · Twemproxy (consistent hashing; no multi-key commands or transactions) |
| automatic failover | II · Sentinel (slaves promoted by vote) | IV · Cluster (16384 slots; multi-key only within one slot) |
"Redis is highly available." The advantages slide (p. 365) lists "Availability" and "Fault-Tolerance" — true only for the bottom row of the table: of a standalone deployment p. 361 says "There is no automatic failover."
When a cache pays
Back to the hook's second program. The slides define a cache with Wikipedia (p. 367): "A cache is a collection of data duplicating original values stored elsewhere or computed earlier, where the original data is expensive to fetch (owing to longer access time) or to compute, compared to the cost of reading the cache" — a term they credit to IBM, in the 1960s. Its anatomy (p. 368) is the key-value store of lesson 17 cut to the bone: "simple key/value storage" and three operations, save, get, delete.
The vocabulary of p. 369, with what each name means:
Storage cost — the memory spent on holding the copies.
Retrieval cost — the price of one look into the cache: "network load / algorithm load", a hop to another server or the work of the lookup itself.
Invalidation — "keeping data up to date / removing irrelevant data": throwing away copies whose original has changed.
Replacement policy — which entry to evict when the cache is full: "FIFO/LFU/LRU/MRU/RANDOM vs. Belady's algorithm". FIFO evicts the oldest entry, LFU the least frequently used, LRU the least recently used, MRU the most recently used, RANDOM any entry. Belady's algorithm (Laszlo Belady, IBM, 1966) evicts the entry that will be needed again furthest in the future: the best possible choice, and impossible in practice, because it needs to know the future. It is the yardstick for the others, the way the Bayes classifier of statistics is for real classifiers: optimal, and out of reach without information you do not have.
Cold cache / warm cache — just started and empty, so nearly every request misses; or filled with the entries the requests keep asking for.
A request that finds its key is a cache hit; one that does not is a miss, and goes to the original. Page 370 takes 45 hits and 10 misses:
\[ \begin{aligned} h &= \frac{\text{hits}}{\text{hits} + \text{misses}} \\ &= \frac{45}{45 + 10} \approx 0.818 \end{aligned} \] \[ \text{miss ratio} = 1 - h \approx 0.182 \]The slide rounds them to "82% hit ratio" and "18% miss ratio".
More hits than misses?
Page 371, "When to cache?", gives a principle and a shortcut: "caches are only efficient when the benefits of faster access outweigh the overhead of checking and keeping your cache up to date" — "more cache hits then cache misses". The derivation below, not on the slides, turns the principle into a number. Call \(t_c\) the cost of one look into the cache and \(t_o\) the cost of the original, the database query:
A hit costs only the look: \(t_c\). A miss costs the look and then the trip to the original: \(t_c + t_o\).
A fraction \(h\) of the requests hit and \(1 - h\) miss, so the average request costs
\[ T = h\,t_c + (1-h)(t_c + t_o) . \]Collect \(t_c\), which every request pays:
\[ T = t_c + (1-h)\,t_o . \]Without the cache every request costs \(t_o\). The cache pays when \(T \lt t_o\):
\[ t_c + (1-h)\,t_o \lt t_o \iff h \gt \frac{t_c}{t_o} . \]So the break-even hit ratio is \(h^* = t_c / t_o\): the price of a look, measured in trips to the original. "More hits than misses", \(h \gt 0.5\), is the same rule only when a look costs half a trip. (Writing the copies and invalidating them adds to \(t_c\): the "keeping your cache up to date" of the slide.)
Where caches live
Caches sit wherever something slow stands behind something fast. Page 372 lists hardware ("cpu, hdd"), operating systems ("ram"), the "web stack", applications — even "your own short term vs long term memory". The web stack holds a ladder of them (p. 373): browser cache, DNS cache, Content Delivery Networks, proxy servers and, in the application, "full output caching" and the data caching of section 5. Along the ladder (p. 374), "the closer to the original request(er), the faster" — "but probably also the harder to get it right", since "the closer to the requester the more parameters the cache depends on": a copy in your browser depends on you, your cookies, your language; one next to the database serves every user.
Memcached: a cache that forgets on purpose
Memcached is the cache of the slides: a "Free & open source, high-performance, distributed memory object caching system", "Generic in nature, intended for use in speeding up dynamic web applications by alleviating database load", a "key/value dictionary" (p. 376). It was "Developed by Brad Fitzpatrick for LiveJournal in 2003" (p. 377).
"Technically" (p. 378): "It's a server", reached by "Client access over TCP or UDP". "Servers can run in pools": "eg. 3 servers with 64GB mem each give you a single pool of 192GB storage for caching". And the design decision: "Servers are independent, clients manage the pool". No server knows the others; the client library hashes the key and picks the server (Fig. 3) — the client layer of partitioning from p. 359. Each key has one home, so every server added adds its whole memory.
What deserves a place in it (pp. 379–380): data that is in "high demand (used often)", "expensive (hard to compute)" and "common", shared across users — "Best: All three". The typical tenants: "user sessions (often)", "user data (often, shared)", "homepage data (eg. often, shared, expensive)".
Its principles are its limits
Page 381 opens with one strength, "Fast network access (memcached servers close to other application servers)", then four refusals: "No persistency (if your server goes down, data in memcached is gone)", "No redundancy / fail-over", "No replication (single item in cache lives on one server only)", "No authentication (not in shared environments)". Page 382 adds limits and two more refusals: "1 key is maximum 1MB", "keys are strings of 250 characters (in application typically MD5 of user readable string)", "No enumeration of keys (thus no list of valid keys in cache at certain moment)", "No active clean-up (only clean up when more space needed, LRU: Least Recently Used )".
Read the list against p. 359: by design, memcached lacks three of the four kinds of machinery of section 1 — persistence, replication, failover — and keeps only the simplest form of the fourth, partitioning done by the client. It does not need the other three: every value in it duplicates something stored elsewhere, so a dead server costs time — its keys miss until reloaded from the database — not data. Its client API (p. 383) is the small vocabulary of any key-value store: add, set, get, getMulti, delete, increment, decrement, flush, plus addServer to grow the pool and getStats to watch it.
Memcached and Redis side by side
Page 365 lists Redis's advantages: "Performance", "Availability", "Fault-Tolerance", "Scalability (adaptability)", "Portability"; "Support for complex data types and structures", "Atomic operations and Transactions", "Pipelining (send multiple commands at once)", "LUA scripting support", "LRU eviction of keys", "Keys with limited time-to-live"; and an open-source server, simple to set up, with clients in many languages. Against memcached's principles:
| memcached | Redis | |
|---|---|---|
| values | a "key/value dictionary" of opaque items, at most 1 MB each (pp. 376, 382) | data structures: strings, hashes, lists, sets, sorted sets… (p. 354) |
| persistence | "No persistency" (p. 381) | RDB snapshots and AOF log (p. 359) |
| replication | "No replication": one server per item (p. 381) | master → read-only slaves (p. 359) |
| failover | "No redundancy / fail-over" (p. 381) | manual, Sentinel or Cluster (p. 359) |
| partitioning | "clients manage the pool" (p. 378) | client, proxy (Twemproxy) or Cluster (p. 359) |
| clean-up | LRU, only when space is needed (p. 382) | "LRU eviction of keys", "Keys with limited time-to-live" (p. 365) |
| speed (Fig. 4) | 80,000 | 80,000; pipelined 250,000 |
The last row is the chart of Redis's advantages slide (p. 365), redrawn in Fig. 4. It prints no unit and no description of the benchmark, and comes from a vendor's blog (redislabs.com): read it for orders of magnitude.
Two facts to take from it. Redis and Memcached tie at 80,000: memcached's refusals buy simplicity, not speed. And "Redis pipelined" reaches 250,000, three times as much. Pipelining, "send multiple commands at once" (p. 365), removes waiting: instead of one network round trip per command, the client sends a batch and collects the replies together. Page 393 makes the same point for reads: even a hit costs a trip to the memcached server, so use multiget, "fetch multiple keys from memcached in one single call" — the "result is array of items". Redis's MGET does the same. In PHP it is the getMulti of p. 383 (the line is ours):
<?php
// multiget: three keys, one round trip; returns an array of the items found
$users = $cache->getMulti(array('user_1', 'user_2', 'user_3'));
?>
Cache-aside in code, and the two hard things
The slides show memcached at work in PHP. The first program caches a whole page, the "full output caching" of p. 373 (p. 384):
<?php
// p. 384: prints the page, built only on a miss
$html = $cache->get('mypage');
if (!$html)
{
ob_start();
echo "<html>";
// all the fancy stuff goes here
echo "</html>";
$html = ob_get_contents();
ob_end_clean();
$cache->set('mypage', $html);
}
echo $html;
?>
Read it aloud: ask the cache for 'mypage'; if nothing comes back, build the page (PHP's output buffer, ob_start … ob_get_contents, captures everything echoed in between), store it under the key, print it. The next visitor gets the stored copy. This shape — look in the cache; on a miss compute or fetch, store, return — is called cache-aside (the name is not on the slides; Fig. 5).
Caching a whole page is fast but fragile: the page depends on everything shown on it. Page 385 moves the cache "on a lower level", to data caching: "easier to find all dependencies", the "ideal solution for offloading database queries", because "the database is almost always the biggest bottleneck in backend performance problems". The slide's code caches one user's row (p. 386; the key in set is restored, see the red pen):
<?php
// p. 386: the row of user $UID, from the cache if there
function getUserData($UID)
{
$key = 'user_' . $UID;
$userData = $cache->get($key);
if (!$userData)
{
$queryResult = Database::query(
"SELECT * FROM USERS WHERE uid = " . (int) $UID);
$userData = $queryResult->getRow();
$cache->set($key, $userData);
}
return $userData;
}
?>
The key is the user's business key, prefixed: 'user_' . $UID. On a miss one query by primary key runs — (int) lets only a number reach the SQL — and the row is stored.
The two hard things
Page 387 is one quote on a blackboard: "There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton. For the first, the slides give two strategies (Fig. 6).
Time-based (pp. 388–389): "Caching for a certain amount of time", "eg. 10 minutes"; "don't delete caches"; "thus: You can't trust that data coming from cache is correct". It is "Great for summaries", an "Overview", "Pages where it's not that big a problem if data is a little bit out of date (eg. search results)", and "Good for quick and dirty optimizations". In Redis this is the TTL of lesson 18: SET key value EX 600.
Event-based (pp. 390–392): "Store forever, and expire on certain events" — "store userdata for ever", and "when user changes any of his preferences, throw cache away". The read side keeps the query result with no expiry, and can be told to invalidate instead (p. 391):
<?php
// p. 391: the row of user $UID, or drop its cached copy
function getUserData($UID, $invalidateCache = false)
{
$db = DB::getInstance();
$db->prepare("SELECT * FROM USERS WHERE uid = {UID}");
$db->assignInt('UID', $UID);
$db->setCacheTTL(0); // cache forever
if ($invalidateCache)
{
return $db->invalidateCache();
}
$db->execute(); return $db->getRow();
}
?>
and the write side throws the copy away after every update (p. 392):
<?php
// p. 392: update the row, then drop its cached copy
function updateUserData($UID, $data)
{
$db = DB::getInstance();
$db->prepare("UPDATE USERS SET ... WHERE uid = {UID}");
...
getUserData($UID, true); // invalidate cache
return $result;
}
?>
The cache lives inside a database wrapper (setCacheTTL(0), "cache forever"; invalidateCache()) that the slides do not show: read the pattern, not the API. After an update the next read misses and reloads the fresh row: the old copy is gone as soon as the write drops it, at the price of a delete per write and a miss after it. In the widget of section 3, a TTL traded stale reads for misses and never reached zero; deleting on write reached zero and kept nearly the whole hit ratio.
Time-based: a 10-minute TTL
Event-based: delete on every write
The deck closes with three tips (p. 394): be careful when security matters — remember "no authentication" (SASL authentication was then in the works); "Caching is not an excuse not to do database tuning. (Remember cold cache?)" — after a restart every request misses and the database takes the whole load; and write unit tests for your caching code, because "Debugging problems related to out-of-date cache data is hard and boring. Very boring."
Back to the hook. The word is duplicating: a cache holds "data duplicating original values stored elsewhere" (p. 367). When memcached loses a server, the originals are still in the database: the loss is time — a cold cache, a database under full load — never data. Redis, used as a database, may hold the only copy, so a crash would be a real loss: hence snapshots, logs, slaves, Sentinel and Cluster. Neither program is badly designed; they answer different questions. And whether a cache is worth having is one ratio: its hit ratio against the break-even \(t_c / t_o\).
Exam-style questions
Theory questions in the forms the readme of the past papers names — multiple choice, true or false, open answers; wrong answers may carry a penalty, announced just before the exam — then a computation the slides make themselves, and one practical exercise worded like the Redis exercises of the past papers.
Which Redis topology provides both automatic failover and the distribution of data across nodes?
Show model answer
Redis Cluster. Its keyspace is divided into 16384 hash slots shared among the masters, and if a master fails one of its slaves is promoted automatically. Sentinel gives only automatic failover, Twemproxy only data distribution, and the standalone topology neither.
The two-by-two of p. 360 is the whole question. The trap to avoid is Sentinel: it is about availability, never about distribution.
Which of the following statements are true? Select all that apply.
Show model answer
True: the second, the third and the fifth. Sentinel provides automatic failover without distributing data; the standalone topology has no automatic failover; Twemproxy is an external proxy from Twitter that distributes data and has no failover.
With a possible penalty for wrong answers, tick only what a slide sentence supports. Each false statement here swaps one capability of the two-by-two for another.
Redis keeps its data in memory. Describe the two mechanisms it provides for persistence, what each of them can lose in a crash, and the trade-off between persistence and performance.
Show model answer
Redis provides Redis database snapshots (RDB) and append-only files (AOF). An RDB snapshot is a point-in-time copy of the whole dataset, written to disk periodically: it is compact and fast to reload, but everything written after the last snapshot is lost in a crash, typically minutes of data. The AOF appends every write command to a log that is replayed at restart: it loses at most what was not yet flushed to disk (about one second with the default once-per-second flush, nothing if the file is synced after every write), but the file is larger and slower to replay. Both can be enabled together. In general, increased persistency tends to increase latency and decrease throughput — syncing after every write makes each write wait for the disk. Redis is therefore not a replacement for relational databases or document stores: it is best used for rapidly changing data whose size is foreseeable and fits mostly in memory, often next to such a store.
The slides give the two names (p. 359) and the trade-off sentence (p. 349); the rest of the answer explains them, as a theory answer should. Three things to earn: both mechanisms, what each loses, and the persistence-versus-latency trade-off.
A cache served 45 cache hits and 10 cache misses. What is its hit ratio, in percent?
Show model answer
Hit ratio = hits / (hits + misses) = 45 / (45 + 10) = 0.818, about 82 %. The miss ratio is 1 − 0.818, about 18 %.
The numbers of p. 370, which prints 82 % and 18 %. Write the brackets: the slide's "hits / hits + misses", read literally, is not a ratio. If a question goes on to ask whether such a cache pays, the answer depends on the hit ratio against \(t_c / t_o\), not on 82 % being above 50 %.
Memcached offers no persistence, no replication, no fail-over and no authentication. Explain why it is nevertheless widely used, and compare it with Redis.
Show model answer
Memcached is a free and open-source, high-performance, distributed in-memory key/value cache, intended to speed up dynamic web applications by alleviating database load. Its servers are independent: the client manages the pool and sends each key to one server (for example three servers of 64 GB form one 192 GB pool). Items are at most 1 MB, keys at most 250 characters; keys cannot be enumerated, and space is reclaimed with LRU only when needed. It can do without persistence, replication and fail-over because it is a cache: every item duplicates data whose original is stored elsewhere, usually in the database, so losing a server costs time — the next requests miss and reload from the database — but not data. Redis instead offers persistence (RDB snapshots, AOF), replication to read-only slaves, automatic failover (Sentinel, Cluster), partitioning by proxy or by Cluster, and values that are data structures. In the benchmark shown in the lecture the two are equally fast (80,000 each). Memcached fits a pure cache; Redis fits data that must survive or needs structures.
The point the examiner looks for is the link between the "No"s and being a cache. A list of features without that "why" answers only half the question.
An exercise in the style of the past papers' Redis exercises. Consider the ER model of a hospital heating management system (the scenario of the Exam of 30 Jan 2026): Hospital (Hospital_ID, Name) has Departments (Department_ID, Specialty, N_Beds, Head_Physician_ID), Buildings and Heating Systems; Persons (P_ID, Email) work for Departments with a Role. A dashboard shows, for each Department, a summary page computed by a slow query over its floors and heaters.
Consider the Department entity and write the Redis commands for the following operations. (a) Cache the summary of Department "D7" under a key, so that it expires after 10 minutes. (b) Check how long the cached summary will remain valid. (c) The Head Physician of "D7" changes: invalidate the cached summary immediately. (d) The dashboard runs on a Redis Cluster: store the Specialty "Cardiology" and the N_Beds 24 of "D7" under two keys that a single command can read together, and read them.
Show model answer
SET department:D7:summary "Cardiology - 3 floors - 24 beds - 2 heaters" EX 600
TTL department:D7:summary
DEL department:D7:summary
MSET {department:D7}:specialty "Cardiology" {department:D7}:n_beds 24
MGET {department:D7}:specialty {department:D7}:n_beds
(a) A time-based invalidation: 10 minutes = 600 seconds (equivalently SET followed by EXPIRE department:D7:summary 600). (b) TTL returns the remaining seconds, at most 600, and -2 once the key has expired. (c) An event-based invalidation: DEL returns 1, and the next read misses and recomputes the summary. (d) The hash tag {department:D7} makes both keys hash the same string, so they share a hash slot and the multi-key commands are allowed; MGET returns "Cardiology" and "24".
The exercise is ours, built on the past papers' Redis wording ("Create a key-value pair … with TTL equal to …", "check its lifespan"). Three ideas are checked: TTL in seconds, invalidation on the event as an explicit DEL, and the same-slot rule of Redis Cluster with its hash tag — without the braces, MGET on two keys in different slots is refused.
Check yourself
A standalone master-slave deployment: the master crashes at 3 a.m. What do the clients experience until someone intervenes?
You need automatic failover. Which topologies give it to you? Select all that apply.
Failover and distribution are two separate questions; only Cluster answers yes to both.
In a Redis Cluster, user:42:name is in slot 6755 and user:42:city in slot 8210 — both owned by master B. Does a command on both keys run?
A look into the cache costs \(t_c = 2\) ms, a database query \(t_o = 10\) ms, and the hit ratio is 0.3. What does the cache do to the average read?
Why can memcached afford "No persistency" and "No replication"?
User data is cached with time-based invalidation, TTL 10 minutes. A user changes their name. Which statements are true? Select all that apply.
Belady's algorithm is on the replacement-policy slide (p. 369), yet no real cache runs it. Why?
Key take-aways
- Page 359 keeps an in-memory database alive with four kinds of machinery: persistence (RDB snapshots, AOF log), replication (master to read-only slaves), partitioning (in the client, a proxy such as Twemproxy, or a query router such as Redis Cluster) and failover (manual, Sentinel, Cluster).
- A snapshot loses what was written after it, minutes; the log loses at most what was not yet flushed, about a second by default; more persistence means more latency and less throughput (p. 349).
- The topologies sit on a two-by-two: Standalone has neither automatic failover nor distribution, Sentinel only failover, Twemproxy only distribution, Cluster both — with 16384 hash slots, slot = CRC16(key) mod 16384, and multi-key commands only within one slot (hash tags put keys together).
- A cache duplicates values stored elsewhere. Hit ratio = hits / (hits + misses), 45/55 ≈ 82 % on p. 370; the cache pays when the hit ratio exceeds \(t_c / t_o\) — "more hits than misses" is only a rule of thumb. FIFO, LFU, LRU, MRU and RANDOM are judged against Belady's optimum, which needs the future.
- Memcached (Fitzpatrick, LiveJournal, 2003) is a pool of independent servers that the client manages; it refuses persistence, redundancy and fail-over, replication, authentication, key enumeration and active clean-up, caps items at 1 MB and keys at 250 characters, and evicts with LRU. Being a cache is what lets it refuse.
- Cache-aside:
get; on a miss, query the database,set(key, value), return. Invalidate by time (a TTL: simple, but stale reads are possible) or by event (store forever, delete on every write); fetch many keys in one round trip with multiget or pipelining.
What to watch and read
-
Distributed Caching with Memcached
Brad Fitzpatrick, Linux Journal, 2004
Memcached explained by its author, a year after he wrote it for LiveJournal: the "global hash table" he wanted for all the web processes, spread over many independent servers — the pool of p. 378.
-
Caching Strategies and How to Choose the Right One
Umer Mansoor, codeahoy
One diagram per pattern: cache-aside, the shape of the slides' PHP code, next to read-through, write-through and write-back, and what can go wrong with each. Read it next to section 5.
-
Redis persistence
Redis documentation
RDB and AOF in full, with the advantages and the risks of each, the flush policies, and what happens when both are on — the mechanics behind the two names of p. 359.
-
Running Redis at Scale: Scalability
Redis tutorials
Why a fixed 16384 slots instead of "hash mod the number of shards", how resharding moves slots, and a hands-on six-node cluster — topology IV of p. 364, built.
-
Cache Consistency: Memcached at Facebook
Robert Morris, MIT 6.824, lecture 16
Cache-aside at the largest scale, and everything that goes wrong with invalidation: why a write deletes the cached copy instead of updating it, and what a cold cache does to a database.