Un servizio ad alte prestazioni per lo storage di strutture dati



Michelangelo Altamore

Working Capital Barcamp Tour 2009

Catania, 29 Aprile 2009

Sommario

Informazioni su Michelangelo Altamore

Il contesto del problema

Consideriamo le prestazioni dei database relazionali
di applicazioni web su grande scala. Non appena
raggiungono una massa critica ed hanno successo:

Soddisfare velocemente una mole elevata di
richieste per unita' di tempo diventa cruciale

I database relazionali sono il collo di bottiglia

Nel tempo si sono consolidate varie tecniche
per aumentare il Request Rate dei RDBMS:

Middleware su middleware

Per ottenere cio' sono stati implementati diversi
middleware che ottengono risultati non sempre
soddisfacenti
ed hanno il loro prezzo da pagare

Proviamo ad andare a monte per discutere
i costi/benefici dei RDBMS

Perche' usiamo un RDBMS?

Versatilita' espressiva: fornisce SQL, un linguaggio
comprensibile per esprimere e manipolare
il modello di dati di un applicativo

Ma, dietro le quinte, la versatilita' ha dei costi:

Quando un RDBMS non aiuta

Purtroppo il modello relazionale risulta inadeguato
per modellare domini di dati gerarchici.
Es.: alberi, grafi.

Esempio: dati gerarchici implementati in SQL
http://dev.mysql.com/tech-resources/articles/
hierarchical-data.html

Effetto impedenza dei RDBMS

Inoltre, non essendoci un matching tra la
rappresentazione dei dati fornita dal database
e quella impiegata dall'applicativo

Es. ActiveRecord in RoR: acts_as_nested_set,
acts_as_a_list, acts_as_a_ordered_list

Alla ricerca di uniformita'

Del resto sarebbe comodo dotarsi proprio a
livello applicativo di un'interfaccia uniforme
e disaccoppiata ad Abstract Data Types

Es. Code di messaggi per elaborazioni asincrone,
deque, code con priorita', etc…

Key-Value store: un'alternativa al modello relazionale

Consiste praticamente in una tabella hash. E' piu'
semplice concettualmente rispetto ad un RDBMS

Alcuni dei piu' noti sono CouchDB, Tokyo Cabinet,
Voldemort e da poco meno di due mesi anche Redis

Il papa' di Redis

Concepito da antirez, al secolo
Salvatore Sanfilippo, uno dei
migliori Hacker della comunita'
italiana di programmatori

Grazie alla sua notevole esperienza,
fissato l'obiettivo, Salvatore e'
capace di valutare e prendere scelte
progettuali
con ottimi risultati

Obiettivi

Disaccoppiare una Struttura Dati dalla
memoria dell'elaboratore che la usa

Fornire operazioni che consentano
l'implementazione delle principali ADT.

Consentire gli approcci relazionali
a condizione che facciano uso
esclusivamente di chiavi surrogate

Decisioni su cosa fare..

Decisioni su come fare..

..che fanno la differenza

Con Redis un server ottiene le prestazioni di un cluster

Su un singolo Xeon L5420 64 bit, CPU @ 2.5 Ghz, Linux 2.6,
50 client con 100.000 req/s sull'interfaccia di loopback

Come si presenta Redis?

Una chiave in Redis

Matching di chiavi in Redis

Si usa il glob style per i pattern

Un valore in Redis

Un valore e' l'elemento associato ad una certa chiave.
Quando esiste, un elemento puo' essere:
una stringa, un insieme o una lista.

Suddivisione dei Comandi di Redis

Primi passi con Redis: Chiave-Valore

               
               require 'rubygems'
               require 'redis'
               
               r = Redis.new
               
               r['user:michelangelo:id'] = '1000'  # SET user:michelangelo:id 1000
               r['user:michelangelo:id']           # GET user:michelangelo:id  
               
               r['user:michel:id'] = '1001'       # SET user:michel:id 1001
               r['user:michel:id']                # GET user:michel:id 
               
               # Osserva che il : fa da separatore per namespacing delle chiavi
               # poteva anche essere un / , - , etc..
             


git clone git://github.com/antirez/redis.git
cd redis; make; ./redis-server &
sudo gem install ezmobius-redis-rb; irb

Primi passi con Redis: Contatori

              
               r.delete 'user_id'          # DEL user_id
               r.set('user_id', 0)         # SET user_id 0
               r['user_id']                #  => 0
               
               r.incr('user_id', 1000)     # INCRBY user_id integer
               r['user_id']                #  => 1000
               
               r.incr('user_id')           # INCR user_id 
               r['user_id']                #  => 1001
               
               r.incr('user_id')           # incrementiamo il contatore globale 
               r['user_id']                #  => 1002
               
               r['user:alessandro:id'] = r['user_id']  # SET user:alessandro:id 1002
               r['user:alessandro:id']                 # => 1002
               
               r.delete 'user:alessandro:id' # DEL user:alessandro:id
               
               r.decr('user_id')             # DECR  user_id
               r['user_id']                  #  => 1001
               

Primi passi con Redis: Insiemi

                        
            r.set_add 'user:michelangelo:barcamp2009:tags', 'redis'
            r.set_add 'user:michelangelo:barcamp2009:tags', 'storage'
            r.set_add 'user:michelangelo:barcamp2009:tags', 'data structure'
            r.set_add 'user:michelangelo:barcamp2009:tags', 'web 3.0'
            
            r.set_members 'user:michelangelo:barcamp2009:tags'
            # => #<Set: {"data structure", "redis", "storage", "web 3.0"}>
            
            r.set_add 'user:michel:barcamp2009:tags', 'semantic web'
            r.set_add 'user:michel:barcamp2009:tags', 'ontology'
            r.set_add 'user:michel:barcamp2009:tags', 'rdf/owl'
            r.set_add 'user:michel:barcamp2009:tags', 'web 3.0'
            
            r.set_members 'user:michel:barcamp2009:tags'
            # => <Set: {"rdf/owl", "semantic web", "ontology", "web 3.0"}>
            
            p 'intersezione'
            r.set_intersect 'user:michelangelo:barcamp2009:tags', 'user:michel:barcamp2009:tags'
            # => #<Set: {"web 3.0"}>
            

Primi passi con Redis: Liste

            
            r.delete 'coda' # inizializza il buffer della coda di messaggi
            
            # produttore 
            r.push_tail 'coda', 'vino'  # => "OK"
            r.push_tail 'coda', 'acqua' # => "OK"
            
            # consumatore
            r.pop_head 'coda' # => "vino"
            
            # arriva l'antipasto :)
            r.push_tail 'coda', 'pane'      # => "OK"
            r.push_tail 'coda', 'olive'     # => "OK"
            r.push_tail 'coda', 'caponata'  # => "OK"
            
            # consumatore
            r.pop_head 'coda' # => "acqua"
            r.pop_head 'coda' # => "pane"
            
            # il condimento arriva in ritardo..
            r.push_tail 'coda', 'olio'  # => "OK"
            
            r.pop_head 'coda' # => "olive"
            r.pop_head 'coda' # => "caponata"
            r.pop_head 'coda' # => "olio"
            r.pop_head 'coda' # => nil non e' rimasto piu' nulla :)
            

Ci sono molte lingue con cui puoi parlare a Redis

Sono attualmente disponibili client per i seguenti ambienti o
linguaggi di programmazione:

Chi sta usando Redis

Merzia, Llogg.com

Engine Yard, Nanite

MessagePub.com, Multichannel Message API

Contributori ai Redis Clients

Riferimenti Utili

Domande

?

Crediti

Antirez's face antirez @ flikr.com

Lamborghini Diablo apulloa @ sbcglobal.net

Lamborghini Diablo Engine jclo3313 @ flikr.com

JQuery Slideshow Plugin T. J. Van Slyke

Grazie per l'attenzione!

http://claimid.com/michelangelo_altamore

Mi trovate su: