poniedziałek, 10 marca 2014

[NoSQL] Redis: klient C#

Twórcy Redis-a nie dostarczają oficjalnych sterowników do C#, ale protokół komunikacji z bazą jest na tyle prosty, że mamy do wyboru kilka driverów Redisa dla C# jak i dla wielu innych języków. W tym poście przedstawione zostaną możliwości klienta będącego częścią frameworka ServiceStack. Driver instalujemy tradycyjnie NuGetem.



Najważniejszą klasą jest RedisClient implementująca kilka ważnych interfejsów. Poza IDisposable są to:
  • IRedisNativeClient - odpowiadający natywnemu API Redis-a
  • ICacheClient - wprowadza warstwę abstrakcji dla cache'y, wykorzystywany wewnątrz ServiceStacka
  • IRedisClient  - wyższy poziom abstrakcji
  • IRedisTypedClient - operacje na typach, dodatkowe funkcjonalności
Przykłady:

using (IRedisNativeClient client = new RedisClient())
{
    client.Set("urn:messages:1", Encoding.UTF8.GetBytes("Hello C# World"));
}

using (IRedisNativeClient client = new RedisClient())
{
    var result = Encoding.UTF8.GetString(client.Get("urn:messages:1"));
    Console.WriteLine(result);
}

using (IRedisClient client = new RedisClient())
{
    var customerNames = client.Lists["urn:customernames"];
    customerNames.Clear();
    customerNames.Add("Joe");
    customerNames.Add("Mary");
    customerNames.Add("Bob");
}

using (IRedisClient client = new RedisClient())
{
    var customerNames = client.Lists["urn:customernames"];
    foreach (var name in customerNames)
        Console.WriteLine(name);
}

Typowany klient daje możliwość tworzenia sekwencji zapewniających unikatowość identyfikatorów. Sekwencje takie tworzone są "per typ".

long lastId = 0;
using(IRedisClient client = new RedisClient())
{
    var customerClient = client.GetTypedClient<Customer>();
    var customer = new Customer()
                       {
                           Id = customerClient.GetNextSequence(),
                           Address = "123 Main Street",
                           Name = "Bob Green",
                           Orders =
                               new List<Order>()
                                   {
                                       new Order() {OrderNumber = "AB123"},
                                       new Order() {OrderNumber = "AB124"}
                                   }
                       };
    var storedCustomer = customerClient.Store(customer);
    lastId = storedCustomer.Id;
}

using(IRedisClient client = new RedisClient())
{
    var customerClient = client.GetTypedClient<Customer>();
    var customer = customerClient.GetById(lastId);
    Console.WriteLine("Got customer {0} with name {1}", customer.Id, customer.Name);
}

Klient C# udostępnia też możliwość definiowania transakcji w poniższy sposób:


using(IRedisClient client = new RedisClient())
{
  var transaction = client.CreateTransaction();
  transaction.QueueCommand(c => c.Set("abc",1));
  transaction.QueueCommand(c => c.Increment("abc",1));
  transaction.Commit();
  var result = client.Get<int>("abc");
  Console.WriteLine(result);
}

poniedziałek, 3 marca 2014

[NoSQL] Redis: Typy danych, funkcjonalności

Redis wspiera 5 typów danych:

1. STRING


Podstawowy typ danych, który może reprezentować całe obiekty zserializowane do stringów,  może też być liczbą.

Podstawowe operacje na stringach:

redis 127.0.0.1:6379> set user:1 "{'name': 'Joe', 'email': 'joe@joe.com'}"
OK
redis 127.0.0.1:6379> get user:1
"{'name': 'Joe', 'email': 'joe@joe.com'}"
redis 127.0.0.1:6379> set user:id 1
OK
redis 127.0.0.1:6379> incr user:id
(integer) 2
redis 127.0.0.1:6379> append user:1 " extra data"
(integer) 50
redis 127.0.0.1:6379> get user:1
"{'name': 'Joe', 'email': 'joe@joe.com'} extra data"
redis 127.0.0.1:6379> getrange user:1 5 9
"e': '"
redis 127.0.0.1:6379> mset order:1 "o1 data" order:2 "o2 data"
OK
redis 127.0.0.1:6379> mget order:1 order:2
1) "o1 data"
2) "o2 data"
redis 127.0.0.1:6379> strlen user:1
(integer) 50

Operacje set i get służą do wstawiania / pobierania wartości pod podany klucz. Podobnie można wykorzystać operacje mset i mget, do których można podać wiele par klucz - wartość i wstawić je do bazy w obrębie jednej operacji. Wartości, które Redis rozpozna jako numeryczne, można inkrementować operacją incr. Kolejne operacje dotyczące typowo ciągów znaków to: append - doklejanie stringa na koniec wartości znajdującej się pod podanym kluczem, getrange - pobranie podzbioru znaków spod podanego klucza oraz strlen, czyli operacja zwracająca długość stringa znajdującego się pod podaną wartością. 

2. LISTA


Zbiór danych posortowanych po kolejności, w jakiej zostały dodane. Podstawowe operacje na listach:


redis 127.0.0.1:6379> lpush mylist "a"
(integer) 1
redis 127.0.0.1:6379> lpush mylist "b"
(integer) 2
redis 127.0.0.1:6379> lpush mylist "c"
(integer) 3
redis 127.0.0.1:6379> lrange mylist 0 2
1) "c"
2) "b"
3) "a"
redis 127.0.0.1:6379> ltrim mylist 0 1
OK
redis 127.0.0.1:6379> lrange mylist 0 2
1) "c"
2) "b"
redis 127.0.0.1:6379> lindex mylist 1
"b"
redis 127.0.0.1:6379> lindex mylist 0
"c"
redis 127.0.0.1:6379> lpop mylist
"c"
redis 127.0.0.1:6379> lrange mylist 0 2
1) "b"
redis 127.0.0.1:6379> rpush mylist "e"
(integer) 2
redis 127.0.0.1:6379> lrange mylist 0 2
1) "b"
2) "e"

Dane dodajemy operatorem lpush. Pobieranie odbywa się po zakresie za pomocą lrange gdzie podajemy zakres indeksów. Element spod konkretnego indeksu pobieramy przez lindex. Lista może działać jako kolejka, gdzie dodajemy dane z prawej strony (rpush) i pobieramy z lewej (lpop).

3. SET


Kolekcja unikalnych stringów. Mamy do dyspozycji operacje z teorii zbiorów. Podstawowe operacje na setach:

redis 127.0.0.1:6379> sadd names "joe" "bob" "mary"
(integer) 3
redis 127.0.0.1:6379> scard names
(integer) 3
redis 127.0.0.1:6379> smembers names
1) "bob"
2) "mary"
3) "joe"
redis 127.0.0.1:6379> sadd names2 "joe" "tim" "mary"
(integer) 3
redis 127.0.0.1:6379> sdiff names names2
1) "bob"
redis 127.0.0.1:6379> sinter names names2
1) "mary"
2) "joe"

Elementy do zbioru dodajemy za pomocą operatora sadd. Liczność zbioru możemy podejrzeć za pomocą scard, a jego elementy pobieramy przez smembers. Przykładowe operacje na zbiorach to różnica (sdiff) i przecięcie (sinter).

4. HASH


Typ danych mapujący stringowe pola na stringowe wartości. Redis jest zoptymalizowany pod kątem hashy z niewielką liczbą wartości.

redis 127.0.0.1:6379> hset user:1:h name "joe"
(integer) 1
redis 127.0.0.1:6379> hset user:1:h email "joe@joe.com"
(integer) 1
redis 127.0.0.1:6379> hgetall user:1:h
1) "name"
2) "joe"
3) "email"
4) "joe@joe.com"
redis 127.0.0.1:6379> hmget user:1:h name email
1) "joe"
2) "joe@joe.com"
redis 127.0.0.1:6379> hkeys user:1:h
1) "name"
2) "email"
redis 127.0.0.1:6379> hvals user:1:h
1) "joe"
2) "joe@joe.com"

Dane do hasha o nazwie user:1:h  pod pole name wstawiamy poleceniem hset. Wartości możemy pobrać na kilka sposobów. Jawnie podając pola (hmget), pobierając wszystkie (hvals). Same nazwy pól pobieramy poleceniem hkeys, a klucze i wartości komendą hgetall.

5. SORTED SET


Zbiory unikalnych danych posortowane po rankingu.

redis 127.0.0.1:6379> zadd hs 120 "joe" 100 "bob" 100 "mary" 90 "tim"
(integer) 4
redis 127.0.0.1:6379> zrank hs "bob"
(integer) 1

Dodajemy dane jako pary ranking - klucz poleceniem zadd, natomiast ranking konkretnego klucza (indeks) pobieramy za pomocą zrank.

Pozostałe funkcjonalności:


Redis może pracować jako message bus poprzez proste subskrybowanie / publikowanie wiadomości.

subscribe greetings //klient 1
publish greetings "hello redis" //klient 2
psubscribe greet* // subskrybuje na wszystkie kanały spełniające pattern
unsubscribe / punsubscribe

Ciekawą funkcjonalnością niekoniecznie spotykaną w bazach NoSQL jest zaimplementowana transakcyjność. Transakcję otwieramy poleceniem multi, po którym podajemy operacje mające się wykonać w obrębie transakcji. Kończymy poleceniem exec.
Z poziomu aplikacji klienckiej możemy także włączyć tryb monitorowania operacji, jakie wykonywane są na bazie poleceniem monitor. 

Redis przechowuje dane w pamięci z opcją "persisted to disk". Klient komunikuje się z serwerem po TCP wykorzystując specjalny protokół Redis-a. Baza domyślnie robi snapshoty danych na dysk co określoną liczbę zmian wartości. Plik redis.conf przechowuje konfigurację w sposób tekstowy. Można tam ustawić, co ile sekund lub ile zmian kluczy ma się robić snapshot. Przykładowo:

save 300 10 (co najmniej 300 sekund i 10 zmian kluczy)

Replikacja: działa na zasadzie master / slave(s) - slave'y pozostają domyślnie w trybie read only.

Prosta autentykacja polega na tym, że klient podaje hasło, które jest ustawione w pliku redis.conf na serwerze.

[NoSQL] Redis: Wprowadzenie

Redis to wg. ostatnich statystyk najpopularniejsza baza NoSQL typu key - value. Główną jest zaletą jest bardzo duża szybkość, dzięki czemu korzystają z niej takie marki jak stackoverflow czy instagram. Dane przechowywane są w pamięci w postaci przypominającej słownik, z możliwością zapisu danych na dysk.

Podstawowe cechy:
  • ekstremalnie szybki
  • przechowujemy nie tylko stringi
  • możliwość trwałego zapisu danych na dysk
  • replikacja
  • wbudowane wsparcie dla języka LUA
Podstawowe wykorzystanie Redis-a polega na zapisaniu danych pod podanym kluczem i odczytaniu ich spod tego klucza. 


Instalacja:
Binaria dla windowsów możemy pobrać pod adresem: https://github.com/dmajkic/redis/downloads

Pobieramy paczkę, w której znajdują się pliki .exe. Serwer uruchamiamy jako plik redis-server.exe. Domyślnie startuje on na porcie 6379. Dodatkowo ważna jest aplikacja kliencka, z której możemy wykonywać zapytania do serwera, przykładowo:

redis 127.0.0.1:6379> set key "Hello world"
OK
redis 127.0.0.1:6379> get key
"Hello world"
redis 127.0.0.1:6379> del key
(integer) 1
redis 127.0.0.1:6379> get key
(nil)