25
Redis Use Patterns An Introduction to the SQL Practitioner @ItamarHaber #DevConTLV 2014

Redis Use Patterns (DevconTLV June 2014)

Embed Size (px)

DESCRIPTION

An introduction to Redis for the SQL practitioner, covering data types and common use cases. The video of this session can be found at: https://www.youtube.com/watch?v=8Unaug_vmFI

Citation preview

Page 1: Redis Use Patterns (DevconTLV June 2014)

Redis Use PatternsAn Introduction to the SQL

Practitioner@ItamarHaber #DevConTLV 2014

Page 2: Redis Use Patterns (DevconTLV June 2014)

About

A Redis Geek and Chief Developers Advocate at .com

We provide Redis Cloud – an enterprise-class Redis service for developers (infinitely scalable, highly-available with auto failover, top performing hosted Redis off AWS, Google, Azure & IBM SoftLayer)

Get your free t-shirt and

sticker outside!

Page 3: Redis Use Patterns (DevconTLV June 2014)

What’s Redis? (REmote DIctionary Server)• Open-source (BSD), in-memory, persist-able key-value advanced datastore• Key-value means something like

CREATE TABLE redis (k VARCHAR(512MB) NOT NULL,v VARCHAR(512MB),PRIMARY KEY (k)

);

• 6 data types, 160 commands, blazing fast• Created in 2009 by @antirez

(a.k.a Salvatore Sanfilippo)• Source: https://github.com/antirez/redis• Website: http://redis.io

Page 4: Redis Use Patterns (DevconTLV June 2014)

Why Redis? Because It Is Fun!

• Simplicity rich functionality, great flexibility• Performance easily serves 100K’s of ops/sec• Lightweight ~ 2MB footprint• Production proven (name dropping)

Twitter Pintrest StackOverflow teowaki many more…

Page 5: Redis Use Patterns (DevconTLV June 2014)

RedisMakesYou....

THINK!

• about how data is stored• about how data is accessed• about efficiency• about performance• about the network• …

• Redis is a database construction kit

• Beware of Maslow's "Golden" Gavel/Law of Instrument:

"If all you have is a hammer, everything looks like a nail"

Page 6: Redis Use Patterns (DevconTLV June 2014)

Pattern: Caching Calls to the DB

Motivation: quick responses, reduce load on DBMSHow: keep the statement's results using the Redis STRING data type

def get_results(sql):

hash = md5.new(sql).digest()

result = redis.get(hash)

if result is None:

result = db.execute(sql)

redis.set(hash, result)

# or use redis.setex to set a TTL for the key

return result

Page 8: Redis Use Patterns (DevconTLV June 2014)

Pattern: Avoiding Calls to the DB

Motivation: server-side storage and sharing of data that doesn't need a full-fledged RDBMS, e.g. sessions and shopping cartsHow: depending on the case, use STRING or HASH to store data in Redis

def add_to_cart(session, product, quantity):

if quantity > 0:

redis.hset('cart:' + session, product, quantity)

else:

redis.hrem('cart:' + session, product)

def get_cart_contents(session):

return redis.hgetall('cart:' + session)

Page 9: Redis Use Patterns (DevconTLV June 2014)

The HASH Data Type

• Acts as a Redis-within-Redis contains key-value pairs• Have their own commands: HINCRBY, HINCRBYFLOAT, HLEN, HKEYS, HVALS

…• Usually used for aggregation, i.e. keeping related data together for easy

fetching/updating (remember that Redis is not a relational database). Example:

Using separate keys Using hash aggregationuser:1:id 1 user:1 id 1user:1:fname Foo fname Foouser:1:lname Bar lname Baruser:1:email [email protected] email [email protected]

Page 10: Redis Use Patterns (DevconTLV June 2014)

Denormalization

• Non relational no foreign keys, no referential integrity constraints• Thus, data normalization isn't practical• Be prepared to have duplicated data, e.g.:> HSET user:1 country Mordor

> HSET user:2 country Mordor

…• Tradeoff:

Processing Complexity ↔ Data Volume

Page 11: Redis Use Patterns (DevconTLV June 2014)

Pattern: Lists of Items

Motivation: keeping track of a sequence, e.g. last viewed profilesHow: use Redis' LIST data type

def view_product(uid, product):

redis.lpush('user:' + uid + ':viewed', product)

redis.ltrim('user:' + uid + ':viewed', 0, 9)

def get_last_viewed_products(uid):

return redis.lrange('user:' + uid + ':viewed', 0, -1)

Page 12: Redis Use Patterns (DevconTLV June 2014)

Key Points About Key Names

• Key names are "limited" to 512MB (also the values btw)• To conserve RAM & CPU, try avoid using

unnecessarily_longish_names_for_your_redis_keys because they are more expensive to store and compare (unlike an RDBMS's column names, key names are saved for each key-value pair)• On the other hand, don't be too stringent (e.g 'u:<uid>:r')• Although not mandatory, the convention is to use colons

(':') to separate the parts of the key's name• Your schema is your keys' names so keep them in order

Page 13: Redis Use Patterns (DevconTLV June 2014)

Pattern: Queues (apropos the list data type)Motivation: a producer-consumer use case, asynchronous job management, e.g. processing photo uploads

def enqueue(queue, item):

redis.lpush(queue, item)

def dequeue(queue):

return redis.rpop(queue)

# or use brpop for blocking pop

Page 14: Redis Use Patterns (DevconTLV June 2014)

Is Redis ACID? (mostly) Yes!

• Redis is (mostly) single threaded, hence every operation is• Atomic• Consistent• Isolated

• WATCH/MULTI/EXEC allow something like transactions (no rollbacks)• Server-side Lua scripts ("stored procedures")

also behave like transactions• Durability is configurable and is a tradeoff

between efficiency and safety

Page 15: Redis Use Patterns (DevconTLV June 2014)

Pattern: Searching

Motivation: finding keys in the database, for example all the usersHow #1: use a LIST to store key namesHow #2: the *SCAN commands

def do_something_with_all_users():

first = True

cursor = 0

while cursor != 0 or first:

first = False

cursor, data = redis.scan(cursor, 'user:*')

do_something(data)

Page 16: Redis Use Patterns (DevconTLV June 2014)

Pattern: Indexing

Motivation: Redis doesn't have indices, you need to maintain themHow: the SET data type (a collection of unordered unique members)

def update_country_idx(country, uid):

redis.sadd('country:' + country, uid)

def get_users_in_country(country):

return redis.smembers('country:' + country)

Page 17: Redis Use Patterns (DevconTLV June 2014)

Pattern: Relationships

Motivation: Redis doesn't have foreign keys, you need to maintain them

> SADD user:1:friends 3 4 5 // Foo is social and makes friends

> SCARD user:1:friends // How many friends does Foo have?

> SINTER user:1:friends user:2:friends // Common friends

> SDIFF user:1:friends user:2:friends // Exclusive friends

> SUNION user:1:friends user:2:friends // All the friends

Page 18: Redis Use Patterns (DevconTLV June 2014)

ZSETs (Sorted Sets)I HAVE

CDOIT'S LIKE

OCDBUT ALL THE LETTERS ARE

IN ALPHABETICAL ORDERAS THEY SHOULD BE

• Are just like SETs:• Members are unique• ZADD, ZCARD, ZINCRBY, …

• ZSET members have a score that's used for sorting• ZCOUNT, ZRANGE, ZRANGEBYSCORE

• When the scores are identical, members are sorted alphabetically• Lexicographical ranges are also supported:

• ZLEXCOUNT, ZRANGEBYLEX

Page 19: Redis Use Patterns (DevconTLV June 2014)

Pattern: Sorting

Motivation: anything that needs to be sortedHow: ZSETs

> ZADD friends_count 3 1 1 2 999 3

> ZREVRANGE friends_count 0 -1

3

1

2

Set members (uids)

Scores (friends count)

Page 20: Redis Use Patterns (DevconTLV June 2014)

The SORT Command

• A command that sorts LISTs, SETs and SORTED SETs• SORT's syntax is the most complex (comparatively) but SQLers should

feel right at home with it:SORT key [BY pattern] [LIMIT offset count][GET pattern [GET pattern ...]][ASC|DESC] [ALPHA][STORE destination]

• SORT is also expensive in terms of complexity O(N+M*log(M))• BTW, SORT is perhaps the only ad-hoc-like command in Redis

Page 21: Redis Use Patterns (DevconTLV June 2014)

Pattern: Counting Things

Motivation: statistics, real-time analytics, dashboards, throttlingHow #1: use the *INCR commandsHow #2: use a little bit of BIT*def user_log_login(uid):

joined = redis.hget('user:' + uid, 'joined')

d0 = datetime.strptime(joined, '%Y-%m-$d')

d1 = datetime.date.today()

delta = d1 – d0

redis.setbit('user:' + uid + ':logins', delta, 1)

def user_logins_count(uid):

return redis.bitcount(

'user:' + uid + ':logins', 0, -1)

I love to COUNT(*) data!

One datum,two data,

three data!HA HA HA HA!

Page 22: Redis Use Patterns (DevconTLV June 2014)

Pattern: Counting Unique Items

How #1: SADD items and SCARD for the count Problem: more unique items more RAM

How #2: the HyperLogLog data structure> PFADD counter item1 item2 item3 …• HLL is a probabilistic data structure that counts (PFCOUNT) unique items• Sacrifices accuracy: standard error of 0.81%• Gains: constant complexity and memory – 12KB per counter• Bonus: HLLs are merge-able with PFMERGE

Page 23: Redis Use Patterns (DevconTLV June 2014)

Wait, There's More!

• There are 107 additional commands that we didn't cover • Expiration and eviction policies• Publish/Subscribe• Data persistency and durability• Server-side scripting with Lua• Master-Slave(s) replication• High availability with Sentinel• Redis v3 == Cluster (currently in beta)• …

Page 24: Redis Use Patterns (DevconTLV June 2014)

Where Next?

• Try the interactive demo and get a free 25MB Redis database in the cloud at http://redislabs.com• Need help?• RTFM: http://redis.io/documentation• Ask the redis-db mailing list• Visit #redis on Freenode IRC• Email me: itamar@ .com

Page 25: Redis Use Patterns (DevconTLV June 2014)