Różnice

Różnice między wybraną wersją a wersją aktualną.

Odnośnik do tego porównania

Nowa wersja
Poprzednia wersja
pl:dydaktyka:ztb:2010:projekty:nosql_casandra:start [2010/06/02 12:57]
ztb2010 utworzono
pl:dydaktyka:ztb:2010:projekty:nosql_casandra:start [2019/06/27 15:50] (aktualna)
Linia 1: Linia 1:
-hello world+====== Wstęp ====== 
 + 
 +Cassandra jest magazynem klucz-wartość. Łączy ona rozproszone technologie Dynamo oraz model danych z Google BigTable. Podobnie ja Dynamo zakłada, iż po odpowiednio długim czasie bez nowych zmian wszystkie repliki bedą identyczne, natomiast tak jak BigTable dostarcza modele przypominające tabele. 
 + 
 +Cassandra została otwarta przez Facebooka w 2008 roku. Aktualnie rozwijana jest przez Apache oraz współpracowników z różnych firm. 
 + 
 +====== Instalacja i Konfiguracja ====== 
 + 
 + ​Poniższy opis dotyczy instalacji i konfiguracji na systemie linuksowym a konkretniej na Debianie. W innych systemach operacyjnych kroki te przebiegają analogicznie. Debian został wybrany ze względu na częste wykorzystywanie go jako OS dla serwera baz danych. 
 + 
 +===== Apache Thrift ===== 
 + 
 +Thrift wykorzystywany jest do tworzenie skalowalnych,​ multiplatformowych usług webowych. Generuje klienty RPC w dowolnym języku (z pośród C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk, OCaml) które mogą komunikować się z tym samym serwerem RPC. Dzięki takiemu podejściu Cassandra może być dostępna w łatwy i szybki sposób z pośród wielu języków programowania. 
 + 
 + 
 +instalacja dodatkowych pakietów na debianie 
 + 
 +    * libboost-all-dev 
 +    * bison 
 +    * flex 
 + 
 +konfiguracja (bez wsparcia dla ruby): 
 + 
 +<​code="​bash">​ 
 +./configure --disable-gen-rb --without-ruby 
 +make 
 +make install 
 +</​code>​ 
 + 
 +do poprawnego działania z pythonem należy jeszcze ręcznie zainstalować biblioteki:​ 
 +<​code>​ 
 +cd lib/py 
 +python setup.py install 
 +</​code>​ 
 + 
 +linki:\\ 
 +tutorial do thrifta [[http://​wiki.apache.org/​thrift/​Tutorial]]\\ 
 + 
 +===== Cassandra ===== 
 + 
 +najprostsza instalacja polega na rozpakowaniu tarballa z plikami cassandry i skonfigurowaniu ścieżek dostępu do logów i do danych w pliku conf/​storage-conf.xml 
 + 
 +Jeżeli nie chcemy zmieniać defaultowych ścieżek wystarczy że stworzymy poniższe katalogi tak aby odpowiadały tym zdefiniowanym:​ 
 + 
 +<​code="​bash">​ 
 + sudo mkdir -p /​var/​log/​cassandra 
 + sudo chown -R `whoami` /​var/​log/​cassandra 
 + sudo mkdir -p /​var/​lib/​cassandra 
 + sudo chown -R `whoami` /​var/​lib/​cassandra 
 +</​code>​ 
 + 
 +Uruchmienie pojedynczego węzła: 
 + 
 +<​code="​bash">​ 
 +bin/​cassandra -f  # -f loguje na stdout 
 +</​code>​ 
 + 
 +testowanie węzła poprzez konsole cli: 
 + 
 +<​code="​bash">​ 
 +bin/​cassandra-cli --host localhost --port 9160 
 +</​code>​ 
 + 
 +====== Model ====== 
 + 
 +Cassandra posiada model danych, który najlepiej wyobrazić sobie jako cztero lub pięcio wymiarowy hash. Podstawowe pojęcia to: 
 + 
 +  * "​Cluster"​ - maszyny/​węzły w logicznej instancji Cassandry. 
 +  * "​Keyspace"​ - przestrzeń nazw ColumnFamilies,​ zazwyczaj jedna na wniosek. 
 +  * "​ColumnFamilies"​ - zawierają określoną ilość column, z których każda posiada nazwę, wartość, timestamp i odwołuje się do nich poprzez row keys. 
 +  * "​SuperColumns"​ - mogą być rozumiane jako kolumny, które same są podkolumnami. 
 + 
 +===== Opis kolejnych struktur danych Cassandry ===== 
 + 
 +==== Kolumny (Columns) ==== 
 + 
 +Jest to najmniejszy/​niższy przyrost danych. Określane są jako trójki zawierające nazwę, wartość oraz czas. Definicja:​ 
 + 
 +<​code>​ 
 +struct Column { 
 +  1: binary ​                       name, 
 +  2: binary ​                       value, 
 +  3: i64                           ​timestamp,​ 
 +
 +</​code>​ 
 + 
 +Wystkie wartości wypełnia klient, dlatego ważna jest synchronizacja na komputerach klienckich. Wartość timestamp może być dowolna ale konwenją jest czas w milisekundach od 1970 roku. Używa się go do rozwiązywania konfliktów. W wielu przypadkach pomija się to pole i myśli o kolumnie jako o parze nazwa/​wartość. 
 + 
 +==== Rodziny kolumn (Column Families) ==== 
 + 
 +Jest to pojemnik na kolumny (analogiczny do tabeli w relacyjnym systemie). Posiadają upożadkowaną listę kolumn, do których można odwaołać się poprzez nazwę. Definiuje się je w pliku storage-conf.xml,​ kórego nie można modyfikować bez restartowania Casandry. 
 + 
 +==== Wiersze (Rows) ==== 
 + 
 +W Casasndrze, każda ColumnFamily jest przechowywana w oddzielnym pliku, który z kolei jest posortowany w wierszu (Row) okreslonym pożądku np. po kluczu. Powiązane kolumny, do których jest wspólny dostęp powinny być w jedej rodzinie kolumn. Klucz wiersza (row key) określa jakie dane są przechowywane na maszynie. Tak więc każdy klucz może być powiązany z danymi z różnych rodzin kolumn. Jest to jednak odrębne logicznie, dlatego interfejs Thrift jest zorientowany tak aby możliwy był dostęp do jedenego ColumnFamily poprze klucz w danej chwili. 
 + 
 +Przykład:​ 
 + 
 +<​code>​ 
 +
 +   "​klucz":​{ 
 +      "​Uzytkownicy":​{ 
 +         "​adresEmail":​{"​name":"​adresEmail",​ "​value":"​user.email@email.com"​},​ 
 +         "​stronaWWW":​{"​name":"​stronaWWW",​ "​value":"​http://​userstrona.com"​} 
 +      }, 
 +      "​Statystyki":​{ 
 +         "​wejscia":​{"​name":"​wejscia",​ "​value":"​1000"​} 
 +      } 
 +   }, 
 +   "​user2":​{ 
 +      "​Uzytkownicy":​{ 
 +         "​adresEmail":​{"​name":"​adresEmail",​ "​value":"​user2@email.com"​},​ 
 +         "​twitter":​{"​name":"​twitter",​ "​value":"​user2"​} 
 +      } 
 +   } 
 +
 +</​code>​ 
 + 
 +Klucz "​klucz"​ identyfikuje dane w dwóch rożnych rodzinach kolumn ("​Uzytkownicy"​ i "​Statystyki"​),​ co wcale nie oznacza, że dane z tych kolumn są powiązane. Podsiadanie danych z tym samym kluczem dla róznych rodzin kolumn zalezy wyłacznie od aplikacji. Co więcej, rodzina kolumn "​Uzytkownicy"​ posiada różne nazwy kolumn dla "​klucz"​ oraz "​user2"​ co jest dozwolone w Cassandrze. 
 + 
 +==== Keyspaces ==== 
 + 
 +Jest to pierwszy wymiar cassandrowego hasha. Są kontenerem dla column families. Są mnie j więcej tej ziarnistości co schemat lub baza w relaycjnym systemie. Stanowią punkt konfiguracyjny i zarządzający dla column families. Są rownież strukturami na których wykonuje się "batch insert"​. 
 + 
 +==== Super kolumny (Super Columns) ==== 
 + 
 +Są to kolumny, których wartościami są super kolumny. Oznacza to ze super kolumna jest posortowana tablicą asocjacyjną kolumn. O kolumnach i super kolumnach można myśleć jako o mapach: 
 + 
 +  * wiersz w rodzinie kolumn jest posortowaną mapą nazw kolumn do wartości. 
 + 
 +  * super rodzina kolumn to posortowana mapa nazw super kolumn do wartości super kolumn. 
 + 
 +<​code>​ 
 +
 +  "​mccv":​ { 
 +    "​Tags":​ { 
 +      "​cassandra":​ { 
 +        "​incubator":​ {"​incubator":​ "​http://​incubator.apache.org/​cassandra/"​},​ 
 +        "​jira":​ {"​jira":​ "​http://​issues.apache.org/​jira/​browse/​CASSANDRA"​} 
 +      }, 
 +      "​thrift":​ { 
 +        "​jira":​ {"​jira":​ "​http://​issues.apache.org/​jira/​browse/​THRIFT"​} 
 +      } 
 +    }   
 +  } 
 +
 +</​code>​ 
 + 
 +"​Tags"​ - rodzina kolumn, 
 + 
 +"​cassandra",​ "​thrift"​ - super kolumny. 
 + 
 +==== Range queries ==== 
 + 
 +Cassandra wspiera ​ schematy partycjionowania o stosunkowo niewielkiej ilości kodu. Dostarcza ona dwóch narzędzi:​ 
 + 
 +  * RandomPartitioner - dostarcza równoważenie obciążenia bez konieczności dalszej pracy. 
 + 
 +  * OrderPreservingPartitioner -  umożliwia wykonanie zapytań na przechowywanych kluczach, ale wymaga uważnego wyboru tokenów węzłów oraz oktywnego równoważenia obciążenia. 
 + 
 +System, który wspiera wyłącznie partycjonowanie oparte na has'​ach,​ nie może efektywnie wywoływać dużych zapytań. 
 + 
 +====== Hello World w Javie ====== 
 + 
 + 
 +===== Wstępna konfiguracja ===== 
 + 
 + 
 +Do korzystania z Cassandry w Javie, będziemy używać klienta **Thrift**. Thrift pozwala oprócz Javy na pracę z wieloma innymi językami np. C++, PHP, Python. Aby móc zacząć pracę musimy najpierw skonfigurować bazę Cassandry poprzez modyfikacje pliku konfiguracyjnego **storage-conf.xml** znajdującego się w katalogu **CASSANDRA_HOME/​conf**. Pomiędzy elementy **<​Keyspaces>​** i **</​Keyspaces>​** wstawiamy następujące linie: 
 + 
 +  
 +<code xml> 
 +<​Keyspace Name="​Geography">​ 
 +</​Keyspace>​ 
 +</​code>​ 
 +  
 + 
 +Keyspace odpowiada schematowi w relacyjnej bazie danych. Dajemy mu nazwę Geography. Właśnie pomiędzy tymi znacznikami umieszczamy wszystkie pozostałe instrukcje. 
 + 
 +<code xml> 
 +<​ColumnFamily CompareWith="​UTF8Type"​ Name="​Cities"/>​ 
 +</​code>​ 
 +  
 + 
 +ColumnFamily jest strukturą w której możemy przechowywać "​wiersze",​ możemy utożsamiać ją z tabelą w RDBMS. Naszą "​tabelę"​ nazywamy Cities. Atrybut CompareWith służy do sortowania danych. W Cassandrze nie mamy możliwości tak jak w relacyjnej bazie wykonywania zapytań i otrzymywania posortowanych wyników. Ustawienie atrybutu powoduje że dane zostają posortowane w momencie wkładania ich do bazy i takie w niej już pozostają. W naszym przypadku wartość **"​UTF8Type"​** powoduje że klucze kolumn będą traktowane jako stringi UTF8. 
 + 
 +  
 +<code xml> 
 +<​ReplicaPlacementStrategy>​org.apache.cassandra.locator.RackUnawareStrategy</​ReplicaPlacementStrategy>​ 
 +<​ReplicationFactor>​1</​ReplicationFactor>​ 
 +<​EndPointSnitch>​org.apache.cassandra.locator.EndPointSnitch</​EndPointSnitch>​ 
 +</​code>​ 
 +  
 + 
 +Powyższe linie są niezbędne aby móc operować i pracować z bazą Cassandry. Zostawiamy tutaj domyślne wartości. 
 + 
 +==== Środowisko ==== 
 + 
 + 
 +Przykładowy program obrazujący pracę z Cassandrą zostanie wykonany w środowisku **Netbeans**. Aby móc pracować bez przeszkód i aby w ogóle mieć możliwość zaczęcia pracy z Cassandrą należy po stworzeniu nowego projektu dodać odpowiednie biblioteki do niego. Dokonujemy tego poprzez kliknięcie prawym przyciskiem na **Libraries** w drzewku z projektem. Wybieramy **Add JAR/​Folder** i wskazujemy pliki znajdujące się w folderze **CASSANDRA_HOME/​lib**. 
 + 
 + 
 +===== Opis programu ===== 
 +  
 +Po użytych nazwach widzimy że nasz przykładowy program będzie przechowywał informacje o miastach. Tabela „Cities” zawiera odwzorowanie klucz-wartość. Kluczami będą nazwy miast np. „Kraków”,​ „Warszawa”,​ natomiast wartości będą „kolumnami”. Mówiąc kolumna mamy tutaj jednak na myśli strukturę „Column” z Cassandry która to zawiera trzy pola nazwę, wartość oraz czas. Kolumnami w naszej bazie będą „country” - kraj w którym leży dane miasto, „population” - jego ludność, „area” - jego powierzchnia. 
 + 
 +===== Implementacja w Cassandrze ===== 
 +  
 + 
 +Ok, możemy teraz już zacząć pracę z kodem. 
 + 
 +==== Nawiązanie połączenia ==== 
 + 
 + 
 +Pierwsze co musimy to połączyć się z bazą Cassandry – łączymy się na porcie 9160 domyślnym porcie na którym nasłuchuje Cassandra, po czym przekazujemy połączenie klientowi który zajmuje się utrzymaniem komunikacji z serwerem. Poniższa funkcja wykonuje to zadanie. 
 + 
 +  
 +<code java> 
 +    private static Cassandra.Client setupConnection() throws TTransportException { 
 +        try { 
 +            tr = new TSocket("​localhost",​ 9160); 
 +            TProtocol proto = new TBinaryProtocol(tr);​ 
 +            Cassandra.Client client = new Cassandra.Client(proto);​ 
 +            tr.open();​ 
 + 
 +            return client; 
 +        } catch (TTransportException exception) { 
 +            exception.printStackTrace();​ 
 +        } 
 + 
 +        return null; 
 +    } 
 + 
 +</​code>​ 
 +  
 + 
 +Zamknięcie połączenia polega na wymuszeniu wykonania wszystkich zadań które mogą znajdować się w buforze i zamknięciu Socketa: 
 + 
 +<code java> 
 +    private static void closeConnection() { 
 +        try { 
 +            tr.flush();​ 
 +            tr.close();​ 
 +        } catch (TTransportException exception) { 
 +            exception.printStackTrace();​ 
 +        } 
 +    } 
 +</​code>​ 
 + 
 +==== Zapis do bazy ==== 
 + 
 +Przyjrzymy się teraz jak  będziemy dodawać i przechowywać nasze miasta w bazie. 
 + 
 +<code java> 
 +    private static void createCities(Cassandra.Client client, String key, String country, String population, String area) { 
 +        try { 
 +            long timestamp = System.currentTimeMillis();​ 
 +            Map<​String,​ List<​ColumnOrSuperColumn>>​ job = new HashMap<​String,​ List<​ColumnOrSuperColumn>>​();​ 
 + 
 +            List<​ColumnOrSuperColumn>​ columns = new ArrayList<​ColumnOrSuperColumn>​();​ 
 +            Column column = new Column("​country"​.getBytes(KODOWANIE),​ country.getBytes(KODOWANIE),​ timestamp);​ 
 +            ColumnOrSuperColumn columnOrSuperColumn = new ColumnOrSuperColumn();​ 
 +            columnOrSuperColumn.setColumn(column);​ 
 +            columns.add(columnOrSuperColumn);​ 
 + 
 +            column = new Column("​population"​.getBytes(KODOWANIE),​ population.getBytes(KODOWANIE),​ timestamp);​ 
 +            columnOrSuperColumn = new ColumnOrSuperColumn();​ 
 +            columnOrSuperColumn.setColumn(column);​ 
 +            columns.add(columnOrSuperColumn);​ 
 + 
 +            column = new Column("​area"​.getBytes(KODOWANIE),​ area.getBytes(KODOWANIE),​ timestamp);​ 
 +            columnOrSuperColumn = new ColumnOrSuperColumn();​ 
 +            columnOrSuperColumn.setColumn(column);​ 
 +            columns.add(columnOrSuperColumn);​ 
 + 
 +            job.put(TABELA,​ columns); 
 + 
 +            client.batch_insert(SCHEMA,​ key, job, ConsistencyLevel.ALL);​ 
 +        } catch (Exception exception) { 
 +            exception.printStackTrace();​ 
 +        } 
 +    } 
 +</​code>​ 
 + 
 +Jak już wspomnieliśmy wyżej każda kolumna musi zawierać pole **timestamp**. Pierwszą rzeczą którą robimy jest stworzenie ​ zmiennej przechowującej czas utworzenia. Jest ona używana do rozwiązywania konfliktów gdy mamy kilka podobnych kluczy. Dlatego nie należy nigdy ustawiać takich samych wartości tego pola; najczęściej stosuje się liczbę milisekund które upłynęły od roku 1970.  
 +W następnej linii tworzymy mapę w której będziemy umieszczać wiersze. ​ Kluczem w naszej mapie będzie String który jest nazwą ColumnFamily czyli naszą „tabelą” zdefiniowaną wcześniej w pliku konfiguracyjnym. Wartością mapy będzie lista specjalnej struktury mogącej przechowywać Columnę lub SuperColumnę,​ u nas to będą jak już wcześniej wspomnieliśmy Columny czyli konstrukcja przechowująca trzy pola. 
 + 
 +W kolejnych ​ liniach tworzymy ​ instacje naszych Column: country, population i area oraz wstawiamy do nich odpowiednie wartości przesłane jako argumenty funkcji, a następnie każdą Columne dodajemy do listy. 
 + 
 +Na końcu używamy metody **batch_insert** do zapisania wszystkich danych jednorazowo w bazie. W przypadku gdybyśmy chcieli zapisać tylko pojedynczą Columne należy użyć metody **insert**. Jako parametry dla metody batch_insert wykorzystujemy klucz naszej tabeli czyli miasto do którego odnoszą się wszystkie dane zapisane w strukturze Column, nazwę schematu oraz mapę o której mówiliśmy wcześniej. Ostatnim parametrem jest tzw. **ConsistencyLevel** który jest wykorzystywany przy wszystkich operacjach zapisu oraz czytania z bazy i wskazuje kiedy żądanie klienta zostaje wykonane. Typ **ALL** mówi, że zapis powinien się odbyć przed udzieleniem odpowiedzi klientowi. Dokładne znaczenie każdego ​ parametru jest wyjaśnione na stronie wiki Cassandry [[http://​wiki.apache.org/​cassandra/​API#​ConsistencyLevel]]. 
 + 
 +==== Odczyt z bazy ==== 
 + 
 +Jeśli zapisaliśmy dane do bazy, pewnie chcielibyśmy je z niej odczytać. Poniżej znajduje się funkcja realizująca odczytanie wszystkich naszych kolumn z tabeli: 
 + 
 +<code java> 
 +   ​private static void selectAllCities(Cassandra.Client client) { 
 +        try { 
 +            KeyRange keyRange = new KeyRange(3);​ 
 +            keyRange.setStart_key(""​);​ 
 +            keyRange.setEnd_key(""​);​ 
 + 
 +            SliceRange sliceRange = new SliceRange();​ 
 +            sliceRange.setStart(new byte[] {}); 
 +            sliceRange.setFinish(new byte[] {}); 
 + 
 +            SlicePredicate slicePredicate = new SlicePredicate();​ 
 +            slicePredicate.setSlice_range(sliceRange);​ 
 + 
 +            ColumnParent columnParent = new ColumnParent(TABELA);​ 
 +            List<​KeySlice>​ keySlices = client.get_range_slices(SCHEMA,​ columnParent,​ slicePredicate,​ keyRange, ConsistencyLevel.ONE);​ 
 + 
 +            for (KeySlice keySlice : keySlices) { 
 +                printToConsole(keySlice.getKey(),​ keySlice.getColumns());​ 
 +            } 
 + 
 +        } catch (Exception exception) { 
 +            exception.printStackTrace();​ 
 +        } 
 +    } 
 +</​code>​ 
 + 
 +**KeyRange** jest używane do wyspecyfikowania jaki zakres kluczy chcemy wyciągnąć z bazy. W naszym przypadku w rzeczywistości nie definiujemy zakresu a jedynie ilość wierszy które chcemy wczytać z bazy, dlatego w konstruktorze przekazujemy liczbę 3 co odpowiada trzem wierszom. 
 + 
 +**SliceRange** jest strukturą przechowującą informacje o zakresie, kolejności oraz ilości dla zapytania które zwraca wiele kolumn. Można ją utożsamiać z poleceniami LIMIT oraz ORDER BY z języka SQL. Metoda **setStart** ustawia kolumnę ​ od której powinniśmy zacząć pobieranie danych. Pusta tablica byte oznacza rozpoczęcie od pierwszej kolumny. Metoda **setFinish** ustawia Columne na której powinniśmy skończyć pobieranie danych. Pusta tablica byte oznacza odzyskanie wszystkich Column dopóki nie uzyskamy wartości count. 
 +SliceRange posiada jeszcze jedną funkcje **setCount** której nie użyliśmy tutaj, a oznaczającą liczbę Column które chcemy zwrócić – coś jak LIMIT w SQL. 
 + 
 +Następnie dzięki **SlicePredicate** ustawiamy dane które chcemy odzyskać, u nas jest to zdefiniowany wcześniej przy użyciu SliceRange pewien zakres kolumn. 
 + 
 +W końcu dzięki metodzie **get_range_slices** wczytujemy dane i przypisujemy je do listy obiektów **KeySlice**. Jest to obiekt przechowujący ​ odzyskane wiersze zawierający klucz i kolumny należące do danego wiersza. 
 + 
 + 
 + 
 +Instrukcje wykonywane w funkcji main to: 
 + 
 +<code java> 
 +   ​Cassandra.Client client = setupConnection();​ 
 + 
 +        removeCities(client,​ "​Mediolan"​);​ 
 +        removeCities(client,​ "​Warszawa"​);​ 
 +        removeCities(client,​ "​Kraków"​);​ 
 + 
 +        System.out.println("​Dodaje miasta"​);​ 
 +        createCities(client,​ "​Mediolan",​ "​włochy",​ "​1308735",​ "​18377"​);​ 
 +        createCities(client,​ "​Warszawa",​ "​polska",​ "​1714446",​ "​51724"​);​ 
 +        createCities(client,​ "​Kraków",​ "​polska",​ "​754854",​ "​32700"​);​ 
 + 
 +        System.out.println("​Wyciagam dane z bazy:"​);​ 
 +        selectAllCities(client);​ 
 + 
 +        closeConnection();​ 
 +</​code>​ 
 + 
 +Na początku ustanawiamy połączenie. Kolejne trzy instrukcje to usuniecie ​ wierszy z bazy na wypadek gdyby już z danym kluczem znajdowały się w bazie. W kolejnych liniach dodajemy trzy miasta: „Mediolan”,​ „Warszawa” „Kraków”,​ a następnie wczytujemy i wypisujemy dopiero co włożone informacje na ekran. 
 +Otrzymany rezultat wygląda następująco:​ 
 + 
 +  Dodaje miasta 
 +  Wyciągam dane z bazy: 
 +  Key: '​Warszawa'​ 
 +  name: '​area',​ value: '​51724',​ timestamp: 1276876711965 
 +  name: '​country',​ value: '​polska',​ timestamp: 1276876711965 
 +  name: '​population',​ value: '​1714446',​ timestamp: 1276876711965 
 +   
 +  Key: '​Kraków'​ 
 +  name: '​area',​ value: '​32700',​ timestamp: 1276876711967 
 +  name: '​country',​ value: '​polska',​ timestamp: 1276876711967 
 +  name: '​population',​ value: '​754854',​ timestamp: 1276876711967 
 +   
 +  Key: '​Mediolan'​ 
 +  name: '​area',​ value: '​18377',​ timestamp: 1276876711924 
 +  name: '​country',​ value: '​włochy',​ timestamp: 1276876711924 
 +  name: '​population',​ value: '​1308735',​ timestamp: 1276876711924 
 + 
 + 
 +Wpis z deskryptora:​ 
 + 
 + 
 +<code xml> 
 +<​Keyspace Name="​Geography">​  
 +    <​ColumnFamily CompareWith="​UTF8Type"​ Name="​Cities"/>​ 
 +    <​ReplicaPlacementStrategy>​org.apache.cassandra.locator.RackUnawareStrategy</​ReplicaPlacementStrategy>​ 
 +    <​ReplicationFactor>​1</​ReplicationFactor>​ 
 +    <​EndPointSnitch>​org.apache.cassandra.locator.EndPointSnitch</​EndPointSnitch>​ 
 +</​Keyspace>​ 
 +</​code>​ 
 + 
 + 
 + 
 +===== Cały plik z kodem do ściągnięcia ===== 
 + 
 + 
 + 
 +<code java> 
 +package cassandracities;​ 
 + 
 +/** 
 + * 
 + * @author johnny 
 + */ 
 + 
 +import java.io.UnsupportedEncodingException;​ 
 +import java.util.ArrayList;​ 
 +import java.util.HashMap;​ 
 +import java.util.List;​ 
 +import java.util.Map;​ 
 + 
 +import org.apache.cassandra.thrift.Cassandra;​ 
 +import org.apache.cassandra.thrift.Column;​ 
 +import org.apache.cassandra.thrift.ColumnOrSuperColumn;​ 
 +import org.apache.cassandra.thrift.ColumnParent;​ 
 +import org.apache.cassandra.thrift.ColumnPath;​ 
 +import org.apache.cassandra.thrift.ConsistencyLevel;​ 
 +import org.apache.cassandra.thrift.InvalidRequestException;​ 
 +import org.apache.cassandra.thrift.KeyRange;​ 
 +import org.apache.cassandra.thrift.KeySlice;​ 
 +import org.apache.cassandra.thrift.NotFoundException;​ 
 +import org.apache.cassandra.thrift.SlicePredicate;​ 
 +import org.apache.cassandra.thrift.SliceRange;​ 
 +import org.apache.cassandra.thrift.TimedOutException;​ 
 +import org.apache.cassandra.thrift.UnavailableException;​ 
 +import org.apache.thrift.TException;​ 
 +import org.apache.thrift.protocol.TBinaryProtocol;​ 
 +import org.apache.thrift.protocol.TProtocol;​ 
 +import org.apache.thrift.transport.TSocket;​ 
 +import org.apache.thrift.transport.TTransport;​ 
 +import org.apache.thrift.transport.TTransportException;​ 
 + 
 +public class Main { 
 + 
 +    private static final String SCHEMA = "​Geography";​ 
 +    private static final String TABELA = "​Cities";​ 
 +    public static final String KODOWANIE = "​utf-8";​ 
 + 
 +    private static TTransport tr = null; 
 + 
 +    public static void main(String[] args) throws TException, InvalidRequestException,​ UnavailableException,​ UnsupportedEncodingException,​ NotFoundException,​ TimedOutException { 
 +        Cassandra.Client client = setupConnection();​ 
 + 
 + 
 +        //​System.out.println("​Usuwam miasta o danym kluczu, na wypadek gdyby już znajdowały się w bazie"​);​ 
 +        removeCities(client,​ "​Mediolan"​);​ 
 +        removeCities(client,​ "​Warszawa"​);​ 
 +        removeCities(client,​ "​Kraków"​);​ 
 + 
 +        System.out.println("​Dodaje miasta"​);​ 
 +        createCities(client,​ "​Mediolan",​ "​włochy",​ "​1308735",​ "​18377"​);​ 
 +        createCities(client,​ "​Warszawa",​ "​polska",​ "​1714446",​ "​51724"​);​ 
 +        createCities(client,​ "​Kraków",​ "​polska",​ "​754854",​ "​32700"​);​ 
 + 
 +        System.out.println("​Wyciagam dane z bazy:"​);​ 
 +        selectAllCities(client);​ 
 + 
 +        closeConnection();​ 
 +    } 
 + 
 +    private static Cassandra.Client setupConnection() throws TTransportException { 
 +        try { 
 +            tr = new TSocket("​localhost",​ 9160); 
 +            TProtocol proto = new TBinaryProtocol(tr);​ 
 +            Cassandra.Client client = new Cassandra.Client(proto);​ 
 +            tr.open();​ 
 + 
 +            return client; 
 +        } catch (TTransportException exception) { 
 +            exception.printStackTrace();​ 
 +        } 
 + 
 +        return null; 
 +    } 
 + 
 +    private static void closeConnection() { 
 +        try { 
 +            tr.flush();​ 
 +            tr.close();​ 
 +        } catch (TTransportException exception) { 
 +            exception.printStackTrace();​ 
 +        } 
 +    } 
 + 
 +    private static void removeCities(Cassandra.Client client, String key) { 
 +        try { 
 +            ColumnPath columnPath = new ColumnPath(TABELA);​ 
 +            client.remove(SCHEMA,​ key, columnPath, System.currentTimeMillis(),​ ConsistencyLevel.ALL);​ 
 +        } catch (Exception exception) { 
 +            exception.printStackTrace();​ 
 +        } 
 +    } 
 + 
 +    private static void createCities(Cassandra.Client client, String key, String country, String population, String area) { 
 +        try { 
 +            long timestamp = System.currentTimeMillis();​ 
 +            Map<​String,​ List<​ColumnOrSuperColumn>>​ job = new HashMap<​String,​ List<​ColumnOrSuperColumn>>​();​ 
 + 
 +            List<​ColumnOrSuperColumn>​ columns = new ArrayList<​ColumnOrSuperColumn>​();​ 
 +            Column column = new Column("​country"​.getBytes(KODOWANIE),​ country.getBytes(KODOWANIE),​ timestamp);​ 
 +            ColumnOrSuperColumn columnOrSuperColumn = new ColumnOrSuperColumn();​ 
 +            columnOrSuperColumn.setColumn(column);​ 
 +            columns.add(columnOrSuperColumn);​ 
 + 
 +            column = new Column("​population"​.getBytes(KODOWANIE),​ population.getBytes(KODOWANIE),​ timestamp);​ 
 +            columnOrSuperColumn = new ColumnOrSuperColumn();​ 
 +            columnOrSuperColumn.setColumn(column);​ 
 +            columns.add(columnOrSuperColumn);​ 
 + 
 +            column = new Column("​area"​.getBytes(KODOWANIE),​ area.getBytes(KODOWANIE),​ timestamp);​ 
 +            columnOrSuperColumn = new ColumnOrSuperColumn();​ 
 +            columnOrSuperColumn.setColumn(column);​ 
 +            columns.add(columnOrSuperColumn);​ 
 + 
 +            job.put(TABELA,​ columns); 
 + 
 +            client.batch_insert(SCHEMA,​ key, job, ConsistencyLevel.ALL);​ 
 +        } catch (Exception exception) { 
 +            exception.printStackTrace();​ 
 +        } 
 +    } 
 + 
 +    private static void selectAllCities(Cassandra.Client client) { 
 +        try { 
 +            KeyRange keyRange = new KeyRange(3);​ 
 +            keyRange.setStart_key(""​);​ 
 +            keyRange.setEnd_key(""​);​ 
 + 
 +            SliceRange sliceRange = new SliceRange();​ 
 +            sliceRange.setStart(new byte[] {}); 
 +            sliceRange.setFinish(new byte[] {}); 
 + 
 +            SlicePredicate slicePredicate = new SlicePredicate();​ 
 +            slicePredicate.setSlice_range(sliceRange);​ 
 + 
 +            ColumnParent columnParent = new ColumnParent(TABELA);​ 
 +            List<​KeySlice>​ keySlices = client.get_range_slices(SCHEMA,​ columnParent,​ slicePredicate,​ keyRange, ConsistencyLevel.ONE);​ 
 + 
 +            for (KeySlice keySlice : keySlices) { 
 +                printToConsole(keySlice.getKey(),​ keySlice.getColumns());​ 
 +            } 
 + 
 +        } catch (Exception exception) { 
 +            exception.printStackTrace();​ 
 +        } 
 +    } 
 + 
 +    private static void printToConsole(String key, List<​ColumnOrSuperColumn>​ result) { 
 +        try { 
 +            System.out.println("​Key:​ '"​ + key + "'"​);​ 
 +            for (ColumnOrSuperColumn c : result) { 
 +                if (c.getColumn() != null) { 
 +                    String name = new String(c.getColumn().getName(),​ KODOWANIE);​ 
 +                    String value = new String(c.getColumn().getValue(),​ KODOWANIE);​ 
 +                    long timestamp = c.getColumn().getTimestamp();​ 
 +                    System.out.println(" ​ name: '"​ + name + "',​ value: '"​ + value + "',​ timestamp: " + timestamp);​ 
 +                } else { 
 + 
 +                } 
 +            } 
 +        } catch (UnsupportedEncodingException exception) { 
 +            exception.printStackTrace();​ 
 +        } 
 +    } 
 +
 +</​code>​ 
 + 
 +====== Hello World w pythonie ====== 
 + 
 +===== Testowanie cassandry w pythonie ===== 
 + 
 + 
 +  * generujemy interfejs do cassandry 
 + 
 +<​code>​ 
 +thrift -v -gen py path_to_cassandra/​interface/​cassandra.thrift 
 +</​code>​ 
 + 
 +  * wygenerowało nam pakiet znajdujacy się w folderze path_to_cassandra/​interface/​gen-py/​cassandra 
 +  * możemy teraz ten pakiet zaimportować w pythonie: 
 +<​code>​ 
 +import Cassandra 
 +</​code>​ 
 + 
 +  * oprócz biblioteki wygenerowany został plik do testowania: path_to_cassandra/​interface/​gen-py/​cassandra/​Cassandra-remote 
 + 
 +Jest to program pythonowy wykorzystujący wygenerowaną bibliotekę z pliku Cassandra.py 
 + 
 +  * Możemy przetestować jego działanie:​ 
 + 
 +<​code="​bash">​ 
 +./​Cassandra-remote -h localhost:​9160 describe_version 
 +./​Cassandra-remote -h localhost:​9160 describe_keyspace Keyspace1 
 +</​code>​ 
 + 
 +===== implementacja Hello World ===== 
 + 
 +Została wykonana na podstawie wygenerowanego pliku Cassandra-remote. 
 + 
 +Konfiguracja w pliku conf/​storage-conf.xml 
 +<​code="​xml">​ 
 +    <​Keyspace Name="​Dictionary">​ 
 +      <​ColumnFamily Name="​Words"​ 
 +                    ColumnType="​Super"​ 
 +                    CompareWith="​UTF8Type"​ 
 +                    CompareSubcolumnsWith="​UTF8Type"​ 
 +                    RowsCached="​10000"​ 
 +                    KeysCached="​50%"​ 
 +                    Comment="​A column family with supercolumns,​ whose column and subcolumn names are UTF8 strings"/>​ 
 +      <​ReplicaPlacementStrategy>​org.apache.cassandra.locator.RackUnawareStrategy</​ReplicaPlacementStrategy>​ 
 + 
 +      <!-- Number of replicas of the data --> 
 +      <​ReplicationFactor>​1</​ReplicationFactor>​ 
 + 
 +      <​EndPointSnitch>​org.apache.cassandra.locator.EndPointSnitch</​EndPointSnitch>​ 
 +    </​Keyspace>​ 
 +</​code>​ 
 + 
 +<​code="​python">​ 
 +#​!/​usr/​bin/​env python 
 + 
 +import sys 
 +import time 
 +import pprint 
 +from urlparse import urlparse 
 +from thrift.transport import TTransport 
 +from thrift.transport import TSocket 
 +from thrift.transport import THttpClient 
 +from thrift.protocol import TBinaryProtocol 
 + 
 +import Cassandra 
 +from ttypes import * 
 + 
 +pp = pprint.PrettyPrinter(indent = 2) 
 +host = '​localhost'​ 
 +port = 9160 
 +uri = ''​ 
 +framed = False 
 +http = False 
 +argi = 1 
 + 
 +if len(sys.argv)>​1 and sys.argv[argi] == '​-h':​ 
 +  parts = sys.argv[argi+1].split(':'​)  
 +  host = parts[0] 
 +  port = int(parts[1]) 
 +  argi += 2 
 + 
 + 
 +if http: 
 +  transport = THttpClient.THttpClient(host,​ port, uri) 
 +else: 
 +  socket = TSocket.TSocket(host,​ port) 
 +  if framed: 
 +    transport = TTransport.TFramedTransport(socket) 
 +  else: 
 +    transport = TTransport.TBufferedTransport(socket) 
 +protocol = TBinaryProtocol.TBinaryProtocol(transport) 
 +client = Cassandra.Client(protocol) 
 +transport.open() 
 + 
 + 
 +class Dict: 
 +    def translate(self,​ lang_from, word_from, lang_to): 
 +        colPath = ColumnPath("​Words",​ lang_from, lang_to) 
 +        try: 
 +            ret = client.get('​Dictionary',​ word_from, colPath, ConsistencyLevel.ONE,​) 
 +            if ret: 
 +                print "​translate " + word_from + " (" + lang_from + " - " +lang_to+"​):​ " + ret.column.value 
 +        except NotFoundException:​ 
 +            print "there is no translate for " + word_from + " (" + lang_from + " - " +lang_to+"​)"​ 
 + 
 +    def getAllTranslates(self,​ lang_from, word_from):​ 
 +        colParent = ColumnParent("​Words",​ lang_from) 
 +        slPred = SlicePredicate(None,​ SliceRange('',''​)) 
 +        ret = client.get_slice('​Dictionary',​ word_from, colParent, slPred, ConsistencyLevel.ONE,​) 
 +        print "all transalte for word " + word_from + " (" + lang_from + "​):"​ 
 +        for item in ret: 
 +            print " ​ translate " + word_from + " (" + lang_from + " - " +item.column.name+"​):​ " + item.column.value 
 +         
 + 
 +    def addWord(self,​ lang_from, lang_to, word_from, word_to): 
 +        colPath = ColumnPath("​Words",​ lang_from, lang_to) 
 +        timestamp = int(time.time()) 
 +        ret = client.insert('​Dictionary',​ word_from, colPath, word_to, timestamp, ConsistencyLevel.ONE,​) 
 +        if ret is None: 
 +            print "​inserted word "​+word_from+"​ ("​+lang_from+"​)"​ + " - " +word_to+"​ ("​+lang_to+"​)"​ 
 +        else: 
 +            print ret 
 + 
 +    def removeWord(self,​ lang_from, lang_to, word_from):​ 
 +        colPath = ColumnPath("​Words",​ lang_from, lang_to) 
 +        timestamp = int(time.time()) 
 +        ret = client.remove('​Dictionary',​ word_from, colPath, timestamp, ConsistencyLevel.ONE,​) 
 +        if ret is None: 
 +            print "​removed word " + word_from + " ("​+lang_from+"​ - "​+lang_to+"​)"​ 
 + 
 + 
 + 
 +dic = Dict() 
 +dic.addWord("​pl","​en","​dom","​home"​) 
 +dic.addWord("​pl","​de","​dom","​house"​) 
 +dic.addWord("​pl","​cz","​dom","​holpiczka"​) 
 + 
 +print 
 + 
 +dic.removeWord("​pl","​de","​dom"​) 
 + 
 +print  
 + 
 +dic.translate("​pl",​ "​dom",​ "​en"​) 
 +dic.translate("​pl",​ "​dom",​ "​cz"​) 
 +dic.translate("​pl",​ "​dom",​ "​de"​) 
 + 
 +print 
 + 
 +dic.getAllTranslates("​pl","​dom"​) 
 + 
 +transport.close() 
 +</​code>​ 
 + 
 +====== Wady oraz zalety ====== 
 + 
 +Jak każda technologia Cassandra posiada w sobie zarówno wady jak i zalety. Oto część z nich: 
 + 
 +===== Wady ===== 
 + 
 +  * jest to nowa, innowacyjna technologia dlatego nie cieszy sie dużym zaufaniem wśród korporacji/​firm,​ 
 +  * w niektórych dziedzinach zastosowanie jej jest niewystaczające np. systemy bankowe 
 +  * wymaga zmiany "​podejścia"​ programistów (z relacyjnego do magazynu). 
 + 
 +===== Zalety ===== 
 + 
 +  * Szybka i wydajna - zapis 50GB w 0.12 milisekundy (ponad 2500 razy szybciej od MySQL). 
 +  * Zdecentralizowana – każdy węzeł w klastrze jest identyczny. Nie ma słabego punktu którego usterka spowodowałaby zatrzymanie pracy całej bazy. 
 +  * Odporna na uszkodzenia – dane są automatycznie replikowane do wielu węzłów. Zepsute węzły mogą być zastąpione bez żadnego przestoju. 
 +  * Elastyczna – wydajność zapisywania i odczytywania wzrasta liniowo wraz z dodawaniem nowych maszyn bez żadnego przestoju lub przerw w działaniu aplikacji. 
 +  * Wytrzymała - Cassandra jest odpowiednia dla aplikacji, które nie mogą sobie pozwolić na utratę danych, nawet gdy całe centrum danych padnie 
 +  * "​Sprawdzona"​ - mimo wciąż niezbyt wielkiej popularności z Cassandry korzystają takie firmy jak Digg, Facebook, Twitter, Reddit, Rackspace, Cloudkick, Cisco, SimpleGeo, Ooyala, OpenX, i wiele innych posiadających duże zbiory danych. Największy klaster danych zawiera ponad 100TB danych na ponad 150 urządzeniach. 
 +   
 + 
 +==== Pozostałe możliwości Cassandry ==== 
 + 
 +  * Posiada bogaty model danych, który umożliwia bardziej efektywne użytkowanie w porównaniu do prostych magazynów klucz-wartość. 
 +  * Posiada obsługę wersjonowania oraz rozwiązywania konfliktów (z wbudowaną polityką "​ostatnia aktualizacja wygrywa"​). 
 +  * Dane są automatycznie replikowane pomiędzy wieloma węzłami (również replikacja pomiędzy datacenters). 
 +  * Cassandra stosuje model "​eventually consistent",​ lecz ma też zaawansowane rozwiązania zmniejszające okresy braku spójności. 
 +  * Zapisy zawsze się udają, dwie ścieżki odczytu. 
 +  * Zapisy i odczyty są atomowe dla pojedynczej ColumnFamily. 
 + 
 + 
pl/dydaktyka/ztb/2010/projekty/nosql_casandra/start.1275476233.txt.gz · ostatnio zmienione: 2019/06/27 15:56 (edycja zewnętrzna)
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0