Pokazywanie postów oznaczonych etykietą monitor. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą monitor. Pokaż wszystkie posty

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.

poniedziałek, 11 lutego 2013

[C#|Visual Studio] Threading: Synchronizacja

Synchronizacja wątków jest szczególnie ważna, gdy korzystają one z tych samych zasobów, jednak niezsynchronizowane wątki mogą także w wielu przypadkach doprowadzić do nieprzewidywalnego zachowania programu. Konstrukcje koordynujące wątki dzieli się na cztery kategorie:

Blokowanie

Wątek zablokowany, to taki, którego wykonywanie z jakiegoś powodu zostało wstrzymane, na przykład poprzez operację Sleep bądź Join względem innego wątku. Wątek w stanie zablokowania oddaje swoją część procesora do momentu wznowienia. Odblokować wątek można na cztery sposoby:
  • warunek wznowienia zostaje spełniony
  • przekroczony timeout (jeżeli został ustawiony)
  • przerwanie przez Thread.Interrupt
  • wykonanie instrukcji Thread.Abort
Podczas blokowania wątków można spotkać także tzw. spinning, technikę bardzo niefektywną dla CPU polegającą na ciągłym sprawdzaniu pewnego warunku. Nieco lepszym rozwiązaniem jest usypianie wątku na krótki czas. Technika ta jest efektywna jedynie dla krótkich blokowań, rzędu milisekund, gdyż wtedy nie zachodzi konieczność cennego przełączania kontekstu.

while (!proceed) Thread.Sleep (10);

Stan wątku jest dostępny w każdej chwili za pomocą property ThreadState. Możliwe stany wątków przedstawione są na diagramie poniżej.


Wątki zostają zablokowane na przykład, gdy użyjemy instrukcji lock. Instrukcja ta jest skrótem składniowym odpowiadającym instrukcjom Monitor.Enter oraz Monitor.Exit. Obiekt na którym wykonywana jest blokada musi dziedziczyć po typie referencyjnym.

static readonly object _locker = new object();
static int _x;

static void Increment() 
{ 
    lock (_locker) _x++;
}

static void Decrement()
{
    lock (_locker) _x--;
}

public static void Run()
{
    for (int i = 0; i < 5; i++)
    {
        Task.Factory.StartNew(Increment);
        Task.Factory.StartNew(Decrement);
    }
}

Lock jest instrukcją atomową, chyba że w jego wnętrzu zostanie rzucony wyjątek.Podobny efekt można uzyskać stosując Mutex. Konstrukcja ta mimo iż jest kilkadziesiąt razy wolniejsza, ma jedną ważną przewagę nad lockiem - można za jej pomocą wykonywać blokady pomiędzy procesami.

static void RunMutex()
{
    using (var mutex = new Mutex(false, "Sample Mutex String"))
    {
        if (!mutex.WaitOne(TimeSpan.FromSeconds(3), false))
        {
            Console.WriteLine("Another app instance is running. Bye!");
            return;
        }
        RunProgram();
    }
}

static void RunProgram()
{
    Console.WriteLine("Running. Press Enter to exit");
    Console.ReadLine();
}

Nazwa Mutex-a pozwoli mu blokować procesy w obrębie całego komputera. Jeżeli powyższy program zostanie uruchomiony w dwóch instancjach, to druga instancja poczeka 3 sekundy i zakończy swoją pracę, jeżeli kod pierwszej instancji nie wykona się (nie zostanie wciśnięty Enter).

Semafor

Semafor działa jak lock, z tą różnicą że jest w stanie obsłużyć więcej niż jeden wątek. Liczba wątków definiowana jest przez programistę, a każdy nadmiarowy wątek musi poczekać, aż któryś z poprzedników skończy swoje działanie.

static SemaphoreSlim _sem = new SemaphoreSlim(3);
public static void Run()
{
    for (int i = 1; i <= 5; i++) 
        new Thread(Enter).Start(i);
}

static void Enter(object id)
{
    Console.WriteLine(id + " wants to enter");
    _sem.Wait();
    Console.WriteLine(id + " is in!");
    Thread.Sleep(1000*(int) id);
    Console.WriteLine(id + " is leaving");
    _sem.Release();
}

SemaphoreSlim to klasa z .NET 4.0 zoptymalizowana pod kątem czasu wykonywania operacji Wait i Release.