Upload
ross-tuck
View
8.888
Download
3
Tags:
Embed Size (px)
DESCRIPTION
As presented at Confoo 2013. More than some arcane NoSQL tool, Redis is a simple but powerful swiss army knife you can begin using today. This talk introduces the audience to Redis and focuses on using it to cleanly solve common problems. Along the way, we'll see how Redis can be used as an alternative to several common PHP tools.
Citation preview
Ross Tuck
Redis For The Everyday Developer
ConfooMarch 1st, 2013
Who Am I?
Ross Tuck
Team Lead at IbuildingsCodemonkeyHTTP nutHat guy
@rosstuck
Boring.
NoSQL
NoNoSQL
Evaluation
• Created by Salvatore Sanfilippo ( @antirez )• Sponsored by VMware• 4 years old• redis.io
“Redis is an open source, advanced key-value store.”
“It is often referred to as a data structure server...”
Defancypants
Server A Server B
Redis
In-Memory
In-Memory
The Cool Stuff
Fast.
Demo.
Oh wait, it's already done.
120,000~ ops per sec
Flexible.
(optionally)Persistent.
ReplicationPub/Sub
TransactionsScripting
SlicesDices
Makes julienne fries
“Well, Ross, that sounds cool...”
MySQL Redis
+
MySQL+
Redis
4ever
Setup
apt-get install redis-server
Easy but old
http://redis.io/download
PHP Libraries
• 5.3+: Predis
• 5.2: Predis or Rediska
• C Extension: phpredis
• redis-cli
$client = new \Predis\Client();
The Surprise Ending
PHPMySQL
Memcache
PHPMySQL
Memcache
PHPMySQLRedis
It's about 80% cases.
Use Case #1: Caching
Redis has commands.
Commands have parameters.
Think functions.
$client->commandGoesHere($params, $go, $here);
SET
$client->set('ross:mood', 'nervous');
// Later
$client->get('ross:mood'); // returns “nervous”
$client->set('ross:mood', 'nervous');
$client->expire('ross:mood', 5);
// 4 seconds later...
$client->get('ross:mood'); // “nervous”
// 5 seconds later...
$client->get('ross:mood'); // null
$client->set('ross:mood', 'nervous');
$client->expire('ross:mood', 5);
$client->setex('ross:mood', 5, 'nervous');
Great for caching
Ideal for sessions
Use Case #2: Simple Data
Search...
omg cheezburgers in the lunchroomtoday. Better hurry if u want 1!!! ^_^
How do I store this?
key value
key value
homepage_message omg cheezburgers...
key value
homepage_message omg cheezburgers...
tps_reports new cover pages on...
You already know it.
$client->set('home:message', 'cheezburgers...');
$client->get('home:message');
EqualEasier
More Fun
Use Case #3: Hit Counters
increment
$client->incr('page:42:views');
$client->incr('page:42:views');
$client->incr('page:42:views');
// 1
// 2
// 3
Redis is hard ;)
How is this better?
Fast?
redis-benchmark
====== INCR ======
10000 requests completed in 0.08 seconds
50 parallel clients
3 bytes payload
keep alive: 1
100.00% <= 0 milliseconds
119047.62 requests per second
Fast enough.
$client->incr('cookies:eaten');
$client->incrBy('cookies:eaten', 2);
$client->incrByFloat('cookies:eaten', 0.5); version 2.6+
Use Case #4: Latest News
“It is often referred to as a data structure server...”
“...since keys can contain strings, hashes, lists, sets and sorted sets.”
$redis = array();
GenericHashListSetSorted Set
GenericHashListSetSorted Set
// strings and numbers
$redis['ross:mood'] = "happy";
$redis['foo'] = 9;
GenericHashListSetSorted Set
// associative array
$redis['foo']['name'] = 'Bob';
$redis['foo']['age'] = 31;
Objects, forms, records
GenericHashListSetSorted Set
// not associative
$redis['foo'][] = 'zip';
$redis['foo'][] = 'zap';
Lists, stacks, queues
GenericHashListSetSorted Set
// No dupes, no order
shuffle(
array_unique($redis['foo'])
);
Relations, stats, matching
GenericHashListSetSorted Set
// Ordered by *score*
array_unique($redis['foo']);
Curing cancer, world peaceSets but with order or scores
Y U NO STOPY U NO STOP
LISTING THINGSLISTING THINGS
GenericHashListSetSorted Set
GenericHashListSetSorted Set
// Code
$client->lpush('news:latest', 'Aliens Attack!');
// Redis
['Aliens Attack!']
// Redis
['Takei 2016', 'Aliens Attack!']
// 2 hours later...
$client->lpush('news:latest', 'Takei 2016');
// That evening...
$client->lpush('news:latest', 'Eggs = Cancer!');
// Redis
['Eggs = Cancer!', 'Takei 2016', 'Aliens Attack!']
Recap
// Code
$client->lpush('news:latest', 'Aliens Attack!');
$client->lpush('news:latest', 'Takei 2016');
$client->lpush('news:latest', 'Eggs = Cancer!');
// Redis
['Eggs = Cancer!', 'Takei 2016', 'Aliens Attack!']
Getting it back out?
$client->lrange('news:latest', 0, 1);
End Index
Start Index
var_dump(
$client->lrange('news:latest', 0, 1)
);
array(2) {
[0]=> string(14) "Eggs = Cancer!"
[1]=> string(10) "Takei 2016"
}
That's it.Really.
What about size?
$client->lpush('news:latest', 'Free Jetpacks!');
$client->lpush('news:latest', 'Elvis Found!');
$client->lpush('news:latest', 'Takei Wins!');
//...and so on...
ltrim
$client->ltrim('news:latest', 0, 2);
// Only the three latest stories remain!
Cron
or simpler...
$client->lpush('news:latest', 'Cats Leave Euro');
$client->ltrim('news:latest', 0, 2);
Great option for notifications
Use Case #5: Tricksy Caching
SELECT * FROM Articles
INNER JOIN Authors ON (complicated joins)
-- More joins
WHERE (complicated logic)
LIMIT 0, 20
SELECT Articles.id FROM Articles
INNER JOIN Authors ON (complicated joins)
-- More joins
WHERE (complicated logic)
$client->lpush('search:a17f3', $ids);
$client->lrange('search:a17f3', $limit, $offset);
SELECT * FROM Articles
INNER JOIN Authors ON (complicated joins)
-- More joins
WHERE Articles.id IN (1, 2, 3)
Use Case #6: Recently Viewed
GenericHashListSetSorted Set
GenericHashListSetSorted Set
No duplicates
GenericHashListSetSorted Set
Needs to be ordered
Just Right
GenericHashListSetSorted Set
zadd
$client->zadd('mykey', 1, 'mydata');
Any integer
or float
$client->zadd('recent', 1, '/p/first');
$client->zadd('recent', time(), '/p/first');
$client->zadd('recent', 1338020901, '/p/first');
$client->zadd('recent', 1338020902, '/p/second');
$client->zadd('recent', 1338020903, '/p/third');
$client->zadd('recent', 1338020904, '/p/fourth');
Reading it back out?
array(3) {
[0]=> string(8) "/p/first"
[1]=> string(9) "/p/second"
[2]=> string(8) "/p/third"
}
$client->zrange('recent', 0, 2);
$client->zrevrange('recent', 0, 2);
Reverse
array(3) {
[0]=> string(9) "/p/fourth"
[1]=> string(8) "/p/third"
[2]=> string(9) "/p/second"
}
Duplicates?
$client->zadd('recent', 1338020901, '/p/first');
// later...
$client->zadd('recent', 1338020928, '/p/first');
array(3) {
[0]=> string(8) "/p/first"
[1]=> string(9) "/p/fourth"
[2]=> string(8) "/p/third"
}
$client->zrevrange('recent', 0, 2);
Cool.
Other things we can do?
$client->zrangebyscore('recent', $low, $high);
$yesterday = time()-(60*60*24);
$client->zrangebyscore('recent', $yesterday, '+inf');
Intersections
zinterstore
$client->zinterstore('omg', 2, 'recent', 'favorite');
$client->zrange('omg', 0, 4);
Deletion
zrem
zremrangebyscore
$yesterday = time()-(60*60*24);
$client->zremrangebyscore(
'recent', '-inf', $yesterday
);
We can do a lot.
Scores can be anything.
Use Case #7: Sharing Data
Redis
PHP
Node.js Python
• ActionScript• C• C#• C++• Clojure• Common Lisp• Erlang• Fancy• Go• Haskell• haXe• Io
• Java• Lua• Node.js• Objective-C• Perl• PHP• Pure Data• Python• Ruby• Scala• Smalltalk• Tcl
$client = new \Predis\Client();
$client->set('foo', 'bar');
var redis = require("redis");
var client = redis.createClient();
client.get("foo", redis.print);
Step Further...
Pub/Sub
$client->publish('bids:42', '$13.01');
client.on("message", function (channel, message) {
console.log(channel + "= " + message);
});
client.subscribe("bids:42");
// prints “bids:42= $13.01”
Not everydayyet
Use Case #8: Worker Queues
$client->lpush('jobs:pending', 'clear_cache');
$client->lpush('jobs:pending', '{"do":"email", …}');
$client->lpush('jobs:pending', 'job:45');
// worker
$client = new \Predis\Client(array(
'read_write_timeout' => -1
));
do { $job = $client->brpop('jobs:pending', 0);
doJob($job);
} while(true);
This will work.
However...
Things break.
Things break.
Clients break.
Clients break.
Clients crash.
do { $job = $client->brpop('jobs:pending', 0);
doJob($job);
} while(true);
Multiple keys is the redis way.
'jobs:pending'
'jobs:working'
What we need is: blockingrpoplpush
brpoplpush
No, really.
do {
$job = $client->brpoplpush(
'jobs:pending', 'jobs:working', 0
);
doJob($job);
} while(true);
do {
$job = $client->brpoplpush(
'jobs:pending', 'jobs:working', 0
);
if(doJob($job)) {
$client->lrem('jobs:working', 0, $job);
}
} while(true);
Use Case #9: Scripted Commands
Use Case #9: Impressing People
$client->set('jesse', 'dude');
$client->set('chester', 'sweet');
Lua
local first = redis.call('get', KEYS[1]);
local second = redis.call('get', KEYS[2]);
redis.call('set', KEYS[1], second);
redis.call('set', KEYS[2], first);
return {first, second};
var arguments
// jesse: dude
// chester: sweet
EVAL 'local first...' 2 jesse chester
// jesse: sweet
// chester: dude
Eval != Evil
*Offer only appliesin Redis-Land.
Void where pedantic.
Don't over do it.
Reusing scripts?
SCRIPT LOAD 'local first...'
// 591d1b681192f606d8cb658e1e173e771a90e60e
EVAL 'local first...' 2 jesse chester
EVALSHA 591d1... 2 jesse chester
Sweet.
Epilogue
Simple = Powerful
Fun.
Keep it in RAM.
Extensible + Ecosystem
Great docs.Use them.
Bad for the DB?Might be good for Redis.
Bad for the DB?Might be good for Redis.
@svdgraaf
QuickMeme
IAEA Kotaku
Alltheragefaces
http://joind.in/7971