Created
December 29, 2009 05:06
-
-
Save radar/265164 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env ruby | |
| # My name is Nate Sutton | |
| # I work for Sevenwire <http://sevenwire.com> | |
| # Redis, yet another key-value store: | |
| # * Dataset is non-volatile (unlike memcached) | |
| # * Has four datatypes (unlike memcached, which just has string) | |
| !!! What benchmark? You had to have seen that number somewhere? People get off on that kind of crap. | |
| # * Fast. One benchmark put it at 110k SETs/s and 81k GETs/s. (lies, damned lies, and benchmarks) | |
| # * Supports expiring keys | |
| # | |
| # Non-volatile: | |
| # * In order to not be volatile but still be fast, it asynchronously saves | |
| # to disk. | |
| # * Save triggers are configurable (time and number of changes) | |
| # * alternate Append-Only-File mode for complete persistence (much, much slower) | |
| # | |
| # Four data types: | |
| # String | |
| # List | |
| # Set | |
| # Sorted Set | |
| # To use: | |
| # gem install redis ohm json | |
| # git clone http://github.com/ezmobius/redis-rb.git | |
| # cd redis-rb | |
| # rake redis:install # this is to get latest redis. use package manager for stable | |
| # Start the redis server: redis-server | |
| # Run this script: ruby redis-presentation.rb | |
| # Used commands closest to the actual redis commands, but there is some | |
| # syntactic sugar to be had with the ruby redis client library | |
| # ex: | |
| # this given code: r['foo'] = 'bar' | |
| # is equivalent to: r.set 'foo', 'bar' | |
| require 'rubygems' | |
| require 'redis' | |
| require 'json' | |
| r = Redis.new | |
| r.flushall | |
| ############################################################################## | |
| ### Some basic string commands | |
| puts '-' * 80 | |
| # Setting and getting a value - O(1) | |
| puts 'setting foo to "bar"' | |
| r.set 'foo', 'bar' | |
| puts 'value of foo' | |
| puts r.get('foo') | |
| puts '-' * 80 | |
| # Incrementing/Decrementing a key - O(1) | |
| r.incr 'counter' | |
| r.incr 'counter' | |
| r.incr 'counter' | |
| r.decr 'counter' | |
| r.decr 'counter' | |
| puts 'value of "counter"' | |
| puts r.get('counter') | |
| puts '-' * 80 | |
| ############################################################################## | |
| ### Some basic list commands | |
| # Push a value onto a list - lpush/rpush O(1), lrange O(n) (n is the length of the list) | |
| r.rpush 'mylist', 'foo' | |
| r.lpush 'mylist', 'bar' | |
| r.lpush 'mylist', 'baz' | |
| r.lpush 'mylist', 'bonk' | |
| r.lpush 'mylist', 'bing' | |
| r.lpush 'mylist', 'hi' | |
| puts "value of mylist" | |
| puts r.lrange('mylist', 0, -1).inspect | |
| puts '-' * 80 | |
| # Pop a value off a list - lpop/rpush O(1) | |
| r.lpop('mylist') | |
| r.rpop('mylist') | |
| puts 'value of mylist sans the beginning and end elements' | |
| puts r.lrange('mylist', 0, -1).inspect | |
| puts '-' * 80 | |
| # Trim values from a list - ltrim O(n) (with n being len of list - len of range) | |
| r.ltrim('mylist', 0, 1) | |
| puts 'value of mylist trimmed to the first two values' | |
| puts r.lrange('mylist', 0, -1).inspect | |
| puts '-' * 80 | |
| # Pop a value off one list onto another list atomically - rpoplpush O(1) (Redis >= 1.1) | |
| (1..5).each do |i| | |
| r.lpush 'ids_of_jobs_to_do', i | |
| end | |
| r.rpoplpush 'ids_of_jobs_to_do', 'worker1' | |
| r.rpoplpush 'ids_of_jobs_to_do', 'worker2' | |
| r.rpoplpush 'ids_of_jobs_to_do', 'worker3' | |
| puts 'worker1: ' + r.lrange('worker1', 0, -1).inspect | |
| puts 'worker2: ' + r.lrange('worker2', 0, -1).inspect | |
| puts 'worker3: ' + r.lrange('worker3', 0, -1).inspect | |
| puts 'ids_of_jobs_to_do: ' + r.lrange('ids_of_jobs_to_do', 0, -1).inspect | |
| puts '-' * 80 | |
| ############################################################################## | |
| ### Some basic set commands | |
| # Adding members to the set - O(1) | |
| %w(foo bar baz bonk).each do |value| | |
| r.sadd 'myset1', value | |
| !!! Why are you adding it again? | |
| r.sadd 'myset1', value | |
| end | |
| puts 'members of myset1' | |
| puts r.smembers('myset1').inspect | |
| puts '-' * 80 | |
| # Finding a random member of the set - O(1) | |
| puts 'random members of the set' | |
| puts r.srandmember('myset1') | |
| puts r.srandmember('myset1') | |
| puts r.srandmember('myset1') | |
| puts '-' * 80 | |
| # Finding the union of sets - O(N) (N is the total number of elements of all the sets) | |
| %w(baz bonk bing bang).each do |value| | |
| r.sadd 'myset2', value | |
| r.sadd 'myset2', value | |
| end | |
| puts 'union of myset1 and myset2' | |
| puts r.sunion('myset1', 'myset2').inspect | |
| puts '-' * 80 | |
| # Finding the intersection of sets - O(N*M) worst case where N is the cardinality of the smallest set and M the number of sets | |
| puts 'intersection of myset1 and myset2' | |
| puts r.sinter('myset1', 'myset2').inspect | |
| puts '-' * 80 | |
| # Finding the diff of sets - O(N) with N being the total number of elements of all the sets | |
| puts 'diff of myset1 and myset2' | |
| puts r.sdiff('myset1', 'myset2').inspect | |
| puts '-' * 80 | |
| ############################################################################## | |
| ### Some basic sorted set commands | |
| # Adding values to a sorted set or updates score | |
| # O(log(N)) with N being the number of elements in the sorted set | |
| # Expensive writes, cheap reads | |
| r.zadd 'myzset', 3, 'third' | |
| r.zadd 'myzset', 1, 'first' | |
| r.zadd 'myzset', 2, 'second' | |
| puts 'my sorted set, sorted' | |
| puts r.zrange('myzset', 0, -1).inspect | |
| puts '-' * 80 | |
| ############################################################################## | |
| ### Using it to store larger data structures (both ways are naive) | |
| # Way 1 (slow, key queries scan all the keys in the db) | |
| id = r.incr 'Event:id' | |
| puts "New event ID: #{id}" | |
| r.set "Event:#{id}:name", "Nate's Lightning Talk" | |
| r.sadd "Event:#{id}:participants", 'Participant:1' | |
| r.sadd "Event:#{id}:participants", 'Participant:2' | |
| r.sadd "Event:#{id}:participants", 'Participant:3' | |
| r.lpush "Event:#{id}:comments", 'Wow that was awesome!' | |
| r.lpush "Event:#{id}:comments", 'That was almost a spiritual experience!' | |
| r.lpush "Event:#{id}:comments", 'I CANNOT CONTROL THE VOLUME OF MY TEXT!' | |
| r.incr "Event:#{id}:votes" | |
| r.keys("Event:#{id}:*").each do |key| | |
| value = case r.type(key) | |
| when "string" | |
| r.get(key) | |
| when "set" | |
| r.smembers(key) | |
| when "list" | |
| r.lrange(key, 0, -1) | |
| end | |
| puts "#{key} - #{value.inspect}" | |
| end | |
| puts '-' * 80 | |
| # Way 2 (more like a document DB, strings can only be 1 GB though ;) ) | |
| id = r.incr 'Event:id' | |
| puts "New event ID: #{id}" | |
| attributes = { :name => "Nate's Lightning Talk", | |
| :participants => ['Participant:1', 'Participant:2', 'Participant:3'], | |
| :votes => 2, | |
| :comments => ['Wow that was awesome!', | |
| 'That was almost a spiritual experience!', | |
| 'I CANNOT CONTROL THE VOLUME OF MY TEXT!'] } | |
| r.set "Event:#{id}", attributes.to_json | |
| puts JSON.parse(r.get("Event:#{id}")).inspect | |
| puts '-' * 80 | |
| ############################################################################## | |
| ### Ohm | |
| # Comment out redis first | |
| # require 'rubygems' | |
| # require 'ohm' | |
| # | |
| # puts 'OHM' | |
| # puts '===' | |
| # puts | |
| # | |
| # Ohm.connect | |
| # Ohm.redis.flushall | |
| # | |
| # class Person < Ohm::Model; end | |
| # class Event < Ohm::Model; end | |
| # | |
| # class Person < Ohm::Model | |
| # attribute :name | |
| # set :events, Event | |
| # | |
| # index :name | |
| # | |
| # def validate | |
| # assert_present :name | |
| # end | |
| # end | |
| # | |
| # class Event < Ohm::Model | |
| # attribute :name | |
| # set :participants, Person | |
| # list :comments | |
| # counter :votes | |
| # | |
| # index :name | |
| # | |
| # def validate | |
| # assert_present :name | |
| # end | |
| # end | |
| # | |
| # puts "Creating a person" | |
| # person = Person.create(:name => 'Bob Smith') | |
| # | |
| # puts "Creating an event" | |
| # event = Event.create(:name => "Nate's Lightning Talk") | |
| # | |
| # puts "Associating records" | |
| # event.participants.add(person) | |
| # person.events.add(event) | |
| # | |
| # puts "Finding records" | |
| # person = Person.find(:name => 'Bob Smith').first | |
| # event = Event.find(:name => "Nate's Lightning Talk").first | |
| # | |
| # puts | |
| # puts "Found object information:" | |
| # puts "Person ID: #{person.id}" | |
| # puts "Event ID: #{event.id}" | |
| # puts "Person Name: #{person.name}" | |
| # puts "Event Name: #{event.name}" | |
| # puts "Person found through Event: #{event.participants.first.name}" | |
| # puts "Event found through Person: #{person.events.first.name}" | |
| # puts | |
| # puts "Redis keys:" | |
| # puts Ohm.redis.keys('*') | |
| ############################################################################## | |
| ### New Stuff Coming Soon | |
| # BLPOP (and maybe BLPOPRPUSH?) | |
| # Virtual Memory | |
| # Redis Cluster | |
| # Redis can be found at: | |
| # http://code.google.com/p/redis/ | |
| # http://github.com/antirez/redis | |
| # | |
| # This talk can be found at: | |
| # http://gist.github.com/265161 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment