Inhaltsverzeichnis
Redis - REmote Directory Service - Ein InMemory Key Value Store
Aufgaben:
Im aktuellen Projekt soll eine Redis Datenbank als eine Art „Queue“ für sehr schnelle Anfragen für eine Mobil App dienen.
Redis ist bereits sei 2009 von Salvatore Sanfilippo und mittlerweise in der Version 3.3.5 (Dezember 2016) verfügbar.
Grundaufbau:
Redis ist im Prinzip ein Key Value InMemory Data Store, d.h. die ideale Komponente für den Speed Layer einer Lampda Architektur.
Redis ist konsequent auf Performance ausgelegt und kann mit sehr vielen Abfragen umgehen.
Auch ein Verfallsdatum für die Daten bereits beim Einfügen definiert werden, um zu verhindern das der „Cache Layer“ überläuft. Damit lässt sich ein Round Robin Verfahren für die Daten sehr einfach implementieren.
Abgrenzung zur Oracle NoSQL DB
Die Oracle NoSQL DB ist ideal für die verteilte Datenhaltung in einem Cluster Konstrukt, auch für sehr große Datenmengen. (siehe ⇒ Die Oracle NoSQL Database - Ein Key Value Store)
Die Oracle NoSQL DB ist in Java entwickelt und baut auf die Clusterfähigkeit der Berkely Java DB auf, d.h, die Datenhaltung ist der klare Fokus dieser Datenbank.
Im Gegensatz dazu ist Redis mit der InMemory Struktur ideal für sehr schnelles Einfügung und Abfragen.
Der Speicherinhalt kann zwar auch persistiet werden, aber darin sehe ich zur Zeit nicht den Fokus diese Datenbank Lösung. Dieses schreiben auf Platte dient mehr dazu, im Fehlerfall schneller (bzw. überhaupt wieder) den Inhalt der DB im Speicher zu rekonstruieren.
In unseren Fall mit unserer Lampda Architektur schreiben wir ja parallel beim Hinzufügen der Daten in den Speed Layer mit der Redis DB und gleich zeit mehr transactional traditionell in eine relationale Datenbank wie PostgreSQL oder eben auch eine Oracle.
Was können wir mit der Redis DB erreichen
Key => Value
Ein Schlüssel zeigt auf ein Objekt wie einem String oder einen Integer Wert.
<KEY>|<Value>
Mit „set KEY value“ ein Key ⇒ String Paar definieren
SET Beispiel:
set barcode:X89F894 '{"hersteller" : "GPI","Modell" : "TERM1", "Baujahr" : "2015"}' set barcode:X89F895 '{"hersteller" : "GPI","Modell" : "P23", "Baujahr" : "2016"}' set barcode:X89F896 '{"hersteller" : "GPI","Modell" : "RED24", "Baujahr" : "2013"}' set barcode:X89F899 '{"hersteller" : "GPI","Modell" : "DE5678", "Baujahr" : "2011"}' keys barcode:* 1) "barcode:X89F899" 2) "barcode:X89F895" 3) "barcode:X89F894" 4) "barcode:X89F896" 5) "barcode:896777" get barcode:X89F899 "{\"hersteller\" : \"GPI\",\"Modell\" : \"DE5678\", \"Baujahr\" : \"2011\"}" mget barcode:X89F899 barcode:896777 1) "{\"hersteller\" : \"GPI\",\"Modell\" : \"DE5678\", \"Baujahr\" : \"2011\"}" 2) "{\"hersteller\":\"GPI\",\"Modell\":\"P24\",\"Baujahr\":\"2016\"}"
Hash mit key Value Paaren
Ein Hashwert identifiziert eine Menge von Key Value Paaren.
<HASH> <FELD> <WERT> | <FELD> <WERT> | <FELD> <WERT> …
HMSET / HVALS:
hmset barcodes:X89F894 hersteller "GPI" modell "TERM1" baujahr "2015" hmset barcodes:X89F895 hersteller "GPI" modell "P23" baujahr "2016" hmset barcodes:X89F896 hersteller "GPI" modell "RED24" baujahr "2013" hmset barcodes:X89F899 hersteller "GPI" modell "DE5678" baujahr "2011" #Felder hkeys barcodes:X89F899 1) "hersteller" 2) "modell" 3) "baujahr" # Werte hvals barcodes:X89F899 1) "GPI" 2) "DE5678" 3) "2011" #den Wert von genau einem Feld hget barcodes:X89F899 hersteller "GPI"
Liste von Werten
Ein Schlüssel identifiziert eine Liste von werten, es kann das letzte Element der Liste gelöscht oder in eine neue Liste an den Anfang geschoben werden.
RPUSCH
RPUSH GPI:barcodes X89F894 X89F895 RPUSH GPI:barcodes X89F896 RPUSH GPI:barcodes X89F899 LLEN GPI:barcodes (integer) 4 LRANGE GPI:barcodes 0 4 1) "X89F894" 2) "X89F895" 3) "X89F896" 4) "X89F899" #Element von der Liste entfernen LPOP GPI:barcodes "X89F894" LRANGE GPI:barcodes 0 4 1) "X89F895" 2) "X89F896" 3) "X89F899" # Wert vom Ende einer Liste entfernen und am Anfang einer anderen Liste wieder einfügen # element anlegen RPUSH SIE:barcodes X89F894 # letztes element an den Anfang der zweiten Liste legen # Quell LISTE zu Ziel Liste RPOPLPUSH SIE:barcodes GPI:barcodes
SET
Mit einem Set läßt sich eine Liste abbilden
Unsortiert
sadd barcodes:gpi X89F894 X89F895 X89F896 X89F899 sadd barcodes:sim Z78AB453 Z78AB456 Z78AB478 X89F899 SMEMBERS barcodes:sim 1) "Z78AB453" 2) "Z78AB445" 3) "Z78AB478" 4) "Z78AB456" 5) "X89F899" #Zeige das Element das in beiden Mengen vorkommt sinter barcodes:sim barcodes:gpi 1) "X89F899" #Differenz zwischen den Mengen sdiff barcodes:sim barcodes:gpi 1) "Z78AB456" 2) "Z78AB445" 3) "Z78AB453" 4) "Z78AB478" # Union SUNION barcodes:sim barcodes:gpi 1) "X89F894" 2) "Z78AB453" 3) "Z78AB445" 4) "Z78AB478" 5) "X89F895" 6) "Z78AB456" 7) "X89F896" 8) "X89F899"
Sortiert
Liste mit einem SCore Wert pro Element
ZADD key [NX|XX] [CH] [INCR] score member [score member …]:
zadd barcode:usage 10 "X89F895" 899 "X89F896" 100 "X89F899" 400 "X89F894" # alle mit diesem Score zcount barcode:usage 0 1000 (integer) 4 # Wer liegt an welcher Stelle zrevrank barcode:usage X89F895 (integer) 4
Eine einfaches Quing mit blockierenen Listen
ein einfache Queing dann mit einer blockierenen Liste erreicht werden, einen Client öffnen und mit „BRPOP <listename> <zeit>“ eine Liste „abonnieren“, zweiten Client öffnen und dort in diese Liste schreiben „LPUSh <listenname> “<wert>„.
Komplexere Aufgaben
Json indizieren ?
Eine Direkte Indizierung eines Wert des Key Value Paares ist nicht möglich, die Daten merfach unterschiedlich anzugelegen ist aber eher ungeschikt.
Eine Lösung kann sein mit einem entsprechenden Hash zu auf das zu indizierende element zu arbeiten.
Daten:
set barcode:X89F894 '{"hersteller" : "GPI","Modell" : "TERM1", "Baujahr" : "2015"}' hset barcode:index:modell TERM1 X89F894 # hole den barcode für dieses Modell hget barcode:index:modell TERM1 "X89F894"
Funktioniert nur mit unique Werten für den „pseudo“ Index!
Betrieb
Wie lässt sich das System nun in Betrieb nehmen?
Installation
Linux Basis System wie ⇒ Linux 7
Vorbereitung:
yum install gcc
als User root:
mkdir /srv/redis cd /srv/redis wget http://download.redis.io/releases/redis-3.2.5.tar.gz tar -xvfz redis-3.2.5.tar.gz cd redis-3.2.5 make make install #Bei Fehler erst ein clean vor dem nächste Versuch #hatte am Anfang gcc nicht mit installiert make distclean
Verzeichnis für DB Snapshots anlegen:
mkdir /srv/redisDB
Konfiguraton anpassen/erstellen:
cp /srv/redis/redis-3.2.5/redis.conf /etc/redis.conf #Datenbank Verzeichnis für Snapshots angeben .. # Note that you must specify a directory here, not a file name. dir /srv/redisDB/ ..
TCP Settings bei Bedarf für hohe Last anpassen:
cat /proc/sys/net/core/somaxconn vi /etc/sysctl.conf #Redis High Performance Settings net.core.somaxconn=1024 net.ipv4.tcp_max_syn_backlog=1024 vm.overcommit_memory = 1
Start
Erster Start mit:
Noch als root:
redis-server /etc/redis.conf &
Mit dem Redis Client an der DB anmelden
redis-cli 127.0.0.1:6379> ping PONG
Konfiguration auslesen mit CONFIG:
config get *
Mit Python an der Redis DB anmelden
Auch unter Windows kann der Redis Client verwendet werden, sehr gut, das macht es flexibler .-).
Lib unter der Powershell von Windows mit pip installieren:
PS C:\Python34> .\python -m pip install redis
siehe https://pypi.python.org/pypi/redis
Ein erster Test:
PS C:\Python34> .\python.exe Python 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 19:28:18) [MSC v.1600 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import redis >>> conn = redis.StrictRedis(host='10.10.10.105', port=6379, db=0) >>> conn.set('barcode:X89675','{"hersteller" : "GPI","Modell" : "TERM1", "Baujahr" : "2015"}') True >>> conn.get('barcode:X89675') '{"hersteller" : "GPI","Modell" : "TERM1", "Baujahr" : "2015"}'
Damit das hier klappt, muss natürlich zuvor das Binding auf die gewünschte IP Adresse erfolgen und die Securtiy ausgeschaltet werden. Klappt dann alles nicht vergessen das System wieder zu härten!