Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Redis for the Everyday Developer

C26bfcbd5f786591e036fa0958a11e8b?s=47 Ross Tuck
August 11, 2012

Redis for the Everyday Developer

As presented at Northeast PHP. #nephp

More than some arcane NoSQL tool, Redis is a simple but powerful swiss army knife you can begin using today.

This talk introduces Redis and focuses on using it to cleanly solve common problems. Along the way, you'll learn how Redis can be used as an alternative to several common PHP tools.

C26bfcbd5f786591e036fa0958a11e8b?s=128

Ross Tuck

August 11, 2012
Tweet

Transcript

  1. Ross Tuck Redis For The Everyday Developer Northeast PHP August

    11th, #nephp
  2. Who Am I?

  3. Ross Tuck

  4. Team Lead at Ibuildings Codemonkey HTTP nut Hat guy

  5. None
  6. @rosstuck

  7. Boring.

  8. None
  9. NoSQL

  10. NoNoSQL

  11. Evaluation

  12. • Created by Salvatore Sanfilippo ( @antirez ) • Sponsored

    by VMware • 3 years old • redis.io
  13. “Redis is an open source, advanced key-value store.”

  14. “It is often referred to as a data structure server...”

  15. Defancypants

  16. Server A Server B Redis

  17. In-Memory

  18. In-Memory

  19. The Cool Stuff

  20. Fast.

  21. Demo.

  22. Oh wait, it's already done.

  23. 120,000~ ops per sec

  24. Flexible.

  25. (optionally) Persistent.

  26. Replication Pub/Sub Scripting Transactions Slices Dices Makes julienne fries

  27. None
  28. MySQL Redis +

  29. MySQL + Redis 4ever

  30. Setup

  31. apt-get install redis-server Easy but old

  32. http://redis.io/download

  33. PHP Libraries • 5.3+: Predis • 5.2: Predis or Rediska

    • C Extension: phpredis • redis-cli
  34. $client = new \Predis\Client();

  35. The Surprise Ending

  36. PHP MySQL Memcache

  37. PHP MySQL Memcache

  38. PHP MySQL Redis

  39. It's about 80% cases.

  40. Use Case #1: Caching

  41. Redis has commands.

  42. Commands have parameters.

  43. Think functions.

  44. $client->commandGoesHere($params, $go, $here);

  45. SET

  46. $client->set('ross:mood', 'nervous'); // Later $client->get('ross:mood'); // returns “nervous”

  47. $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
  48. $client->set('ross:mood', 'nervous'); $client->expire('ross:mood', 5);

  49. $client->setex('ross:mood', 5, 'nervous');

  50. Great for caching

  51. Use Case #2: Session Storage

  52. You already know it.

  53. Use Case #3: Hit Counters

  54. None
  55. increment

  56. $client->incr('page:42:views'); $client->incr('page:42:views'); $client->incr('page:42:views'); // 1 // 2 // 3

  57. Redis is hard ;)

  58. How is this better?

  59. None
  60. Fast?

  61. 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
  62. Fast enough.

  63. $client->incr('cookies:eaten'); $client->incrBy('cookies:eaten', 2); $client->incrByFloat('cookies:eaten', 0.5); version 2.6+

  64. Use Case #4: Latest News

  65. None
  66. “It is often referred to as a data structure server...”

  67. “...since keys can contain strings, hashes, lists, sets and sorted

    sets.”
  68. $redis = array();

  69. Generic Hash List Set Sorted Set

  70. Generic Hash List Set Sorted Set // strings and numbers

    $redis['ross:mood'] = "happy"; $redis['foo'] = 9;
  71. Generic Hash List Set Sorted Set // associative array $redis['foo']['name']

    = 'Bob'; $redis['foo']['age'] = 31; Objects, forms, records
  72. Generic Hash List Set Sorted Set // not associative $redis['foo'][]

    = 'zip'; $redis['foo'][] = 'zap'; Lists, stacks, queues
  73. Generic Hash List Set Sorted Set // No dupes, no

    order shuffle( array_unique($redis['foo']) ); Relations, stats, matching
  74. Generic Hash List Set Sorted Set // Ordered by *score*

    array_unique($redis['foo']); Curing cancer, world peace Sets but with order or scores
  75. Y U NO STOP Y U NO STOP LISTING THINGS

    LISTING THINGS
  76. Generic Hash List Set Sorted Set

  77. Generic Hash List Set Sorted Set

  78. // Code $client->lpush('news:latest', 'Aliens Attack!'); // Redis ['Aliens Attack!']

  79. // Redis ['Takei 2012', 'Aliens Attack!'] // 2 hours later...

    $client->lpush('news:latest', 'Takei 2012');
  80. // That evening... $client->lpush('news:latest', 'Eggs = Cancer!'); // Redis ['Eggs

    = Cancer!', 'Takei 2012', 'Aliens Attack!']
  81. Recap

  82. // Code $client->lpush('news:latest', 'Aliens Attack!'); $client->lpush('news:latest', 'Takei 2012'); $client->lpush('news:latest', 'Eggs

    = Cancer!'); // Redis ['Eggs = Cancer!', 'Takei 2012', 'Aliens Attack!']
  83. Getting it back out?

  84. $client->lrange('news:latest', 0, 1); End Index Start Index

  85. var_dump( $client->lrange('news:latest', 0, 1) ); array(2) { [0]=> string(14) "Eggs

    = Cancer!" [1]=> string(10) "Takei 2012" }
  86. That's it. Really.

  87. None
  88. What about size?

  89. $client->lpush('news:latest', 'Free Jetpacks!'); $client->lpush('news:latest', 'Elvis Found!'); $client->lpush('news:latest', 'Takei Wins!'); //...and

    so on...
  90. ltrim

  91. $client->ltrim('news:latest', 0, 2); // Only the three latest stories remain!

  92. Cron

  93. or better...

  94. $client->lpush('news:latest', 'Cats Leave Euro'); $client->ltrim('news:latest', 0, 2);

  95. Great option for notifications

  96. None
  97. None
  98. None
  99. None
  100. SELECT * FROM Articles INNER JOIN Authors ON (complicated joins)

    -- More joins WHERE (complicated logic) LIMIT 0, 20
  101. SELECT Articles.id FROM Articles INNER JOIN Authors ON (complicated joins)

    -- More joins WHERE (complicated logic)
  102. $client->lpush('search:a17f3', $ids);

  103. $client->lrange('search:a17f3', $limit, $offset);

  104. None
  105. Use Case #5: Recently Viewed

  106. None
  107. Generic Hash List Set Sorted Set

  108. Generic Hash List Set Sorted Set No duplicates

  109. Generic Hash List Set Sorted Set Needs to be ordered

  110. Goldilocks Just Right Generic Hash List Set Sorted Set

  111. zadd

  112. $client->zadd('mykey', 1, 'mydata'); Any integer or float

  113. $client->zadd('recent', 1, '/p/first');

  114. $client->zadd('recent', time(), '/p/first');

  115. $client->zadd('recent', 1338020901, '/p/first'); $client->zadd('recent', 1338020902, '/p/second'); $client->zadd('recent', 1338020903, '/p/third'); $client->zadd('recent',

    1338020904, '/p/fourth');
  116. Reading it back out?

  117. array(3) { [0]=> string(8) "/p/first" [1]=> string(9) "/p/second" [2]=> string(8)

    "/p/third" } $client->zrange('recent', 0, 2);
  118. $client->zrevrange('recent', 0, 2); Reverse array(3) { [0]=> string(9) "/p/fourth" [1]=>

    string(8) "/p/third" [2]=> string(9) "/p/second" }
  119. Duplicates?

  120. $client->zadd('recent', 1338020901, '/p/first'); // later... $client->zadd('recent', 1338020928, '/p/first');

  121. array(3) { [0]=> string(8) "/p/first" [1]=> string(9) "/p/fourth" [2]=> string(8)

    "/p/third" } $client->zrevrange('recent', 0, 2);
  122. Cool.

  123. Other things we can do?

  124. $client->zrangebyscore('recent', $low, $high);

  125. $yesterday = time()-(60*60*24); $client->zrangebyscore('recent', $yesterday, '+inf');

  126. Intersections

  127. zinterstore

  128. $client->zinterstore('omg', 2, 'recent', 'favorite'); $client->zrange('omg', 0, 4);

  129. zrem

  130. zremrangebyscore

  131. $yesterday = time()-(60*60*24); $client->zremrangebyscore( 'recent', '-inf', $yesterday );

  132. We can do a lot.

  133. Scores can be anything.

  134. Use Case #6: Sharing Data

  135. Redis PHP Node.js Python

  136. • 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
  137. $client = new \Predis\Client(); $client->set('foo', 'bar');

  138. var redis = require("redis"); var client = redis.createClient(); client.get("foo", redis.print);

  139. Step Further...

  140. Pub/Sub

  141. $client->publish('broadcast', 'batkitty');

  142. client.on("message", function (channel, message) { console.log(channel + ": " +

    message); }); client.subscribe("broadcast"); // prints “broadcast: batkitty”
  143. Not everyday yet

  144. Use Case #7: Worker Queues

  145. $client->lpush('jobs:pending', 'clear_cache');

  146. $client->lpush('jobs:pending', '{"do":"email", …}');

  147. $client->lpush('jobs:pending', 'job:45');

  148. // worker $client = new \Predis\Client(array( 'read_write_timeout' => -1 ));

    do { $job = $client->brpop('jobs:pending', 0); doJob($job); } while(true);
  149. This will work.

  150. However...

  151. Things break.

  152. Things break.

  153. Clients break.

  154. Clients break.

  155. Clients crash.

  156. do { $job = $client->brpop('jobs:pending', 0); doJob($job); } while(true);

  157. Multiple keys is the redis way.

  158. 'jobs:pending' 'jobs:working'

  159. What we need is: blocking rpop lpush

  160. brpoplpush No, really.

  161. do { $job = $client->brpoplpush( 'jobs:pending', 'jobs:working', 0 ); doJob($job);

    } while(true);
  162. do { $job = $client->brpoplpush( 'jobs:pending', 'jobs:working', 0 ); if(doJob($job))

    { $client->lrem('jobs:working', 0, $job); } } while(true);
  163. Epilogue

  164. Simple = Powerful

  165. Fun.

  166. Keep it in RAM.

  167. Redis 2.6 will be amazing.

  168. Great docs. Use them.

  169. Bad for the DB? Might be good for Redis.

  170. Bad for the DB? Might be good for Redis.

  171. None
  172. @svdgraaf QuickMeme IAEA Kotaku

  173. http://joind.in/6829