Sprievodca po Cassandre s jazykom Java

1. Prehľad

Tento výukový program je úvodným sprievodcom databázou Apache Cassandra pomocou jazyka Java.

Nájdete vysvetlené kľúčové pojmy spolu s pracovným príkladom, ktorý obsahuje základné kroky pre pripojenie a začatie práce s touto databázou NoSQL z Javy.

2. Cassandra

Cassandra je škálovateľná databáza NoSQL, ktorá poskytuje nepretržitú dostupnosť bez jediného bodu zlyhania a umožňuje pracovať s veľkým objemom údajov s mimoriadnym výkonom.

Táto databáza používa namiesto použitia architektúry master-slave kruhový dizajn. V dizajne krúžku neexistuje žiadny hlavný uzol - všetky zúčastnené uzly sú identické a navzájom komunikujú ako rovnocenní partneri.

Toto robí z Cassandry horizontálne škálovateľný systém tým, že umožňuje prírastkové pridávanie uzlov bez nutnosti rekonfigurácie.

2.1. Kľúčové koncepty

Začnime krátkym prieskumom niektorých kľúčových konceptov Cassandry:

  • Klaster - súbor uzlov alebo dátových centier usporiadaných do kruhovej architektúry. Každému klastru musí byť priradený názov, ktorý budú následne používať zúčastnené uzly
  • Kľúčový priestor - Ak pochádzate z relačnej databázy, potom je schéma príslušným priestorom kľúčov v Cassandre. Klávesový priestor je najvzdialenejším kontajnerom pre dáta v Cassandre. Hlavné atribúty, ktoré sa majú nastaviť v každom kľúčovom priestore, sú Replikačný faktor, Stratégia umiestnenia replík a Stĺpové rodiny
  • Rodina stĺpcov - Rodiny stĺpcov v Cassandre sú ako tabuľky v relačných databázach. Každá rodina stĺpcov obsahuje kolekciu riadkov, ktoré sú reprezentované znakom a Mapa. Tento kľúč umožňuje spoločný prístup k súvisiacim údajom
  • Stĺpec - Stĺpec v Cassandre je dátová štruktúra, ktorá obsahuje názov stĺpca, hodnotu a časovú pečiatku. Stĺpce a počet stĺpcov v každom riadku sa môžu líšiť v porovnaní s relačnou databázou, kde sú údaje dobre štruktúrované

3. Používanie klienta Java

3.1. Maven závislosť

Musíme definovať nasledujúcu závislosť Cassandry v pom.xml, ktorej najnovšiu verziu nájdete tu:

 com.datastax.cassandra cassandra-driver-core 3.1.0 

Aby sme mohli otestovať kód na zabudovanom databázovom serveri, mali by sme pridať aj znak jednotka Cassandra závislosť, ktorej najnovšiu verziu nájdete tu:

 org.cassandraunit cassandra-unit 3.0.0.1 

3.2. Pripojenie k Cassandre

Aby sme sa mohli pripojiť k Cassandre z Javy, musíme si vybudovať a Klaster objekt.

Ako kontaktný bod je potrebné uviesť adresu uzla. Ak nezadáme číslo portu, použije sa predvolený port (9042).

Tieto nastavenia umožňujú vodičovi zistiť aktuálnu topológiu klastra.

verejná trieda CassandraConnector {súkromný klastrový klaster; relácia súkromnej relácie; public void connect (reťazcový uzol, celočíselný port) {Builder b = Cluster.builder (). addContactPoint (uzol); if (port! = null) {b.withPort (port); } klaster = b.build (); session = cluster.connect (); } public Session getSession () {return this.session; } public void close () {session.close (); cluster.close (); }}

3.3. Vytvorenie kľúčového priestoru

Poďme vytvoriť náš “knižnica”Kľúčový priestor:

public void createKeyspace (String keyspaceName, String replicationStrategy, int replicationFactor) {StringBuilder sb = new StringBuilder ("CREATE KEYSPACE IF NOT EXISTS") .append (keyspaceName) .append ("WITH replication = {") .append ("'class' : '"). append (replicationStrategy) .append ("', 'replication_factor': "). append (replicationFactor) .append ("}; "); Reťazcový dopyt = sb.toString (); session.execute (dopyt); }

Okrem z keyspaceName musíme definovať ďalšie dva parametre, replicationFactor a replicationStrategy. Tieto parametre určujú počet replík a spôsob distribúcie replík v kruhu.

Vďaka replikácii Cassandra zaisťuje spoľahlivosť a odolnosť voči chybám ukladaním kópií údajov do viacerých uzlov.

V tomto okamihu môžeme otestovať, že náš priestor kľúčov bol úspešne vytvorený:

private KeyspaceRepository schemaRepository; relácia súkromnej relácie; @ Pred public void connect () {klient CassandraConnector = nový CassandraConnector (); client.connect ("127.0.0.1", 9142); this.session = client.getSession (); schemaRepository = nový KeyspaceRepository (relácia); }
@Test public void whenCreatingAKeyspace_thenCreated () {String keyspaceName = "library"; schemaRepository.createKeyspace (keyspaceName, "SimpleStrategy", 1); ResultSet result = session.execute ("SELECT * FROM system_schema.keyspaces;"); Zoznam matchedKeyspaces = result.all () .stream () .filter (r -> r.getString (0) .equals (keyspaceName.toLowerCase ())) .map (r -> r.getString (0)) .collect ( Collectors.toList ()); assertEquals (matchedKeyspaces.size (), 1); assertTrue (matchedKeyspaces.get (0) .equals (keyspaceName.toLowerCase ())); }

3.4. Vytvorenie rodiny stĺpcov

Teraz môžeme pridať prvé „knihy“ rodiny stĺpcov do existujúceho priestoru kľúčov:

súkromný statický konečný reťazec TABLE_NAME = "knihy"; relácia súkromnej relácie; public void createTable () {StringBuilder sb = new StringBuilder ("CREATE TABLE IF NOT EXISTS") .append (TABLE_NAME) .append ("(") .append ("id uuid PRIMARY KEY,") .append ("text nadpisu, ") .append (" predmetový text); "); Reťazcový dopyt = sb.toString (); session.execute (dopyt); }

Kód na testovanie vytvorenia rodiny stĺpcov je uvedený nižšie:

súkromné ​​BookRepository bookRepository; relácia súkromnej relácie; @ Pred public void connect () {klient CassandraConnector = nový CassandraConnector (); client.connect ("127.0.0.1", 9142); this.session = client.getSession (); bookRepository = nový BookRepository (relácia); }
@Test public void whenCreatingATable_thenCreatedCorrectly () {bookRepository.createTable (); ResultSet result = session.execute ("SELECT * FROM" + KEYSPACE_NAME + ".books;"); Zoznam stĺpcovNázvy = výsledok.getColumnDefinitions (). AsList (). Stream () .map (cl -> cl.getName ()) .collect (Collectors.toList ()); assertEquals (columnNames.size (), 3); assertTrue (columnNames.contains ("id")); assertTrue (columnNames.contains ("title")); assertTrue (columnNames.contains ("subject")); }

3.5. Zmena rodiny stĺpcov

Kniha má aj vydavateľa, ale žiadny takýto stĺpec nenájdete v vytvorenej tabuľke. Pomocou nasledujúceho kódu môžeme zmeniť tabuľku a pridať nový stĺpec:

public void alterTablebooks (String columnName, String columnType) {StringBuilder sb = new StringBuilder ("ALTER TABLE") .append (TABLE_NAME) .append ("ADD") .append (columnName) .append ("") .append (columnType) .append (";"); Reťazcový dopyt = sb.toString (); session.execute (dopyt); }

Uistite sa, že nový stĺpec vydavateľ bol pridaný:

@Test public void whenAlteringTable_thenAddedColumnExists () {bookRepository.createTable (); bookRepository.alterTablebooks ("vydavateľ", "text"); ResultSet result = session.execute ("SELECT * FROM" + KEYSPACE_NAME + "." + "Knihy" + ";"); boolean columnExists = result.getColumnDefinitions (). asList (). stream () .anyMatch (cl -> cl.getName (). equals ("publisher")); assertTrue (columnExists); }

3.6. Vkladanie údajov do rodiny stĺpcov

Teraz, keď kníh bola vytvorená tabuľka, sme pripravení začať pridávať údaje do tabuľky:

public void insertbookByTitle (Book book) {StringBuilder sb = new StringBuilder ("INSERT INTO") .append (TABLE_NAME_BY_TITLE) .append ("(id, title)") .append ("VALUES (") .append (book.getId ( )) .append (", '") .append (book.getTitle ()). append ("');"); Reťazcový dopyt = sb.toString (); session.execute (dopyt); }

Do tabuľky „knihy“ bol pridaný nový riadok, aby sme mohli otestovať, či daný riadok existuje:

@Test public void whenAddingANewBook_thenBookExists () {bookRepository.createTableBooksByTitle (); Názov reťazca = "Efektívna Java"; Kniha kniha = nová kniha (UUIDs.timeBased (), názov, "Programovanie"); bookRepository.insertbookByTitle (kniha); Kniha savedBook = bookRepository.selectByTitle (názov); assertEquals (book.getTitle (), savedBook.getTitle ()); }

Vo vyššie uvedenom testovacom kóde sme použili inú metódu na vytvorenie tabuľky s názvom booksByTitle:

public void createTableBooksByTitle () {StringBuilder sb = new StringBuilder ("CREATE TABLE IF NOT EXISTS") .append ("booksByTitle"). append ("(") .append ("id uuid,") .append ("text nadpisu, ") .append (" PRIMÁRNY KLÍČ (titul, id)); "); Reťazcový dopyt = sb.toString (); session.execute (dopyt); }

V Cassandre je jedným z najlepších postupov použitie vzoru jednej tabuľky na dotaz. To znamená, že pre iný dopyt je potrebná iná tabuľka.

V našom príklade sme sa rozhodli vybrať knihu podľa názvu. Za účelom uspokojenia selectByTitle dotaz, vytvorili sme tabuľku so zloženou PRIMÁRNY KĽÚČ pomocou stĺpcov, titul a id. Stĺpec titul je kľúč na rozdelenie, zatiaľ čo id stĺpec je klastrovým kľúčom.

Týmto spôsobom obsahuje veľa tabuliek vo vašom dátovom modeli duplicitné údaje. Toto nie je negatívum tejto databázy. Naopak, tento postup optimalizuje výkon čítania.

Pozrime sa na údaje, ktoré sú momentálne uložené v našej tabuľke:

public List selectAll () {StringBuilder sb = new StringBuilder ("SELECT * FROM") .append (TABLE_NAME); Reťazcový dopyt = sb.toString (); ResultSet rs = session.execute (dopyt); Zoznam kníh = new ArrayList (); rs.forEach (r -> {books.add (nová kniha (r.getUUID ("id"), r.getString ("názov"), r.getString ("predmet")));}); návratky; }

Test na dopyt, ktorý vracia očakávané výsledky:

@Test public void whenSelectingAll_thenReturnAllRecords () {bookRepository.createTable (); Book book = new Book (UUIDs.timeBased (), "Effective Java", "Programming"); bookRepository.insertbook (kniha); kniha = nová kniha (UUIDs.timeBased (), "Čistý kód", "Programovanie"); bookRepository.insertbook (kniha); Zoznam kníh = bookRepository.selectAll (); assertEquals (2, books.size ()); assertTrue (books.stream (). anyMatch (b -> b.getTitle () .equals ("Efektívna Java"))); assertTrue (books.stream (). anyMatch (b -> b.getTitle () .equals ("Čistý kód"))); }

Všetko je zatiaľ v poriadku, ale treba si uvedomiť jednu vec. Začali sme pracovať so stolom knihy, ale medzitým za účelom uspokojenia vyberte dopyt podľa titul stĺpci, museli sme vytvoriť ďalšiu tabuľku s názvom booksByTitle.

Tieto dve tabuľky sú identické a obsahujú duplicitné stĺpce, ale do súboru sme vložili iba údaje booksByTitle stôl. Dôsledkom toho sú údaje v dvoch tabuľkách v súčasnosti nekonzistentné.

Môžeme to vyriešiť pomocou a šarža dotaz, ktorý obsahuje dva príkazy vloženia, jeden pre každú tabuľku. A šarža dotaz vykoná viac príkazov DML ako jednu operáciu.

Uvádza sa príklad takéhoto dotazu:

public void insertBookBatch (Book book) {StringBuilder sb = new StringBuilder ("BEGIN BATCH") .append ("INSERT INTO") .append (TABLE_NAME) .append ("(id, title, subject)") .append ("VALUES (") .append (book.getId ()). append (", '") .append (book.getTitle ()). append ("', '") .append (book.getSubject ()). append ( "');") .append ("INSERT INTO") .append (TABLE_NAME_BY_TITLE) .append ("(id, titul)") .append ("VALUES (") .append (book.getId ()). append ( ", '") .append (book.getTitle ()). append ("');") .append ("APPLY BATCH;"); Reťazcový dopyt = sb.toString (); session.execute (dopyt); }

Opäť testujeme výsledky dávkového dotazu takto:

@Test public void whenAddingANewBookBatch_ThenBookAddedInAllTables () {bookRepository.createTable (); bookRepository.createTableBooksByTitle (); Názov reťazca = "Efektívna Java"; Kniha kniha = nová kniha (UUIDs.timeBased (), názov, "Programovanie"); bookRepository.insertBookBatch (kniha); Zoznam kníh = bookRepository.selectAll (); assertEquals (1, books.size ()); assertTrue (books.stream (). anyMatch (b -> b.getTitle (). equals ("Efektívna Java"))); Zoznam kníhByTitle = bookRepository.selectAllBookByTitle (); assertEquals (1, booksByTitle.size ()); assertTrue (booksByTitle.stream (). anyMatch (b -> b.getTitle (). equals ("Efektívna Java"))); }

Poznámka: Od verzie 3.0 je k dispozícii nová funkcia s názvom „Materializované zobrazenia“, ktorú môžeme používať namiesto šarža dotazy. Dobre zdokumentovaný príklad pre „materializované zobrazenia“ je k dispozícii tu.

3.7. Odstraňuje sa rodina stĺpcov

Nasledujúci kód ukazuje, ako odstrániť tabuľku:

public void deleteTable () {StringBuilder sb = new StringBuilder ("DROP TABLE IF EXISTS") .append (TABLE_NAME); Reťazcový dopyt = sb.toString (); session.execute (dopyt); }

Výsledkom výberu tabuľky, ktorá neexistuje v priestore kľúčov, je znak InvalidQueryException: nenakonfigurované stolové knihy:

@Test (očakáva sa = InvalidQueryException.class) public void whenDeletingATable_thenUnconfiguredTable () {bookRepository.createTable (); bookRepository.deleteTable ("knihy"); session.execute ("SELECT * FROM" + KEYSPACE_NAME + ".books;"); }

3.8. Vymazanie kľúčového priestoru

Nakoniec odstránime priestor kľúčov:

public void deleteKeyspace (String keyspaceName) {StringBuilder sb = new StringBuilder ("DROP KEYSPACE") .append (keyspaceName); Reťazcový dopyt = sb.toString (); session.execute (dopyt); }

A otestujte, či bol kľúčový priestor odstránený:

@Test public void whenDeletingAKeyspace_thenDoesNotExist () {String keyspaceName = "library"; schemaRepository.deleteKeyspace (keyspaceName); ResultSet result = session.execute ("SELECT * FROM system_schema.keyspaces;"); boolean isKeyspaceCreated = result.all (). stream () .anyMatch (r -> r.getString (0) .equals (keyspaceName.toLowerCase ())); assertFalse (isKeyspaceCreated); }

4. Záver

Tento výukový program sa zaoberal základnými krokmi pripojenia a používania databázy Cassandra s programom Java. Niektoré z kľúčových pojmov tejto databázy boli tiež prediskutované, aby vám pomohli naštartovať sa.

Celú implementáciu tohto tutoriálu nájdete v projekte Github.


$config[zx-auto] not found$config[zx-overlay] not found