Changes

Cassandra -NoSQL database

6,752 bytes added, 21:51, 16 February 2019
Partíció mérete
* '''Keyspace''': ez felel meg egy adatbázisnak az RDBMS-ben, vagy egy Index-nek az Elasticsearch-ben
* '''Column families''': egy tábla az adatbázisban, amihez sorok tartoznak. Azonban az oszlopok fajtája soronként eltérhet.
* '''Partition''': Szerintem ez azon Azon sorok (illetve cellák) összessége, amiknek a particionáló kulcsuk értéke egyenlő (tehát ugyan azon a node-on lesznek). Ezt előre ki kell számolni, lásd lentebb.
* '''Clustering''': sorok sorba rendezése egy táblán belül (Semmi köze a Cassandra cluster-hez)
https://www.datastax.com/2012/01/getting-started-with-cassandra
<br>
==Materialized Views==
http://cassandra.apache.org/doc/4.0/cql/mvs.html<br>
A Materialized view az eredeti tálba egy részhalmazának, vagy az egész tálba egy olyan másolata, ahol más kulcsok alapján tesszük kereshetővé ugyan azt az adathalmazt. Ez akkor jó, ha van egy táblánk amit A és B oszlop szerint is keresni akarunk, ilyenkor csinálunk egy táblát, ahol A a kulcs, és egy Materialized view-t, ahol a B a kulcs. Ennek az a nagy előnye azzal szemben, mint ha erre két valódi táblát definiálnánk, hogy mikor az igazi táblába szúrunk be, akkor a materialized view-t is frissíteni fogja a Cassandra, nem nekünk kell manuálisan megcsinálni.
===Kulcsok az MW-ben===
Az MW-ban a kulcsokra nagyon komoly megkötés van:
* az alap tábla összes kulcsát tartalmaznia kell
Mire kell figyelni: <br>
* Avoid too large partitions
* Choose your partition key in a way that distributes the data correctly, avoiding cluster hotspots (the partition key chosen above like days of the week is not a good one as it leads to temporal hotspots)
<br>
<br>
=Architektúra=
 
==Alapfogalmak==
 
===Gossip (pletykák)===
 
 
 
===Snitches===
A snitch protokoll segítségével térképezi föl egy node, hogy milyen messze vannak tőle az általa ismert node-ok, hogy ha egy műveletben koordinátor node-ként vesz részt, meg tudja határozni hogy melyik node-okról olvasson (a legközelebbi) és melyik node-okra írjon.
 
 
 
 
===Lightweight Transactions (check-and-set)===
Cassandra-ban nem létezik a hagyományos értelembe vett tranzakció kezelés, csak az úgynevezett pehelysúlyú tranzakció (LWT) ami azt biztosítja, hogy egy olvasás és az azt követő írás egy tranzakcióban lesz ('''linearizable consistency'''). Az olvasással ellenőrizzük, hogy az adott adat szerepel e már az adatbázisban, és ha nem, akkor bírjuk. Ez LWT csak egy partíción belül működik és elég költséges művelet, mivel a végrehajtásához a Cassandra a Paxos nevű konszenzus algoritmust futtatja. A konszenzus kialakításához a partíciót tároló replikák többségének konszenzusra kell jutnia az adott tranzakciót illetően.
 
 
 
 
==Node-ok csoportosítása==
Cassandrában a node-okat két szinten csoportosíthatjuk: Rack és Data Cener.
 
* '''Rack''': A rack-ben olyan nodo-kat csoportosítunk, amik tényleg egy fizikai rack-ben vannak, tehát ezek vannak a "legközelebb" egymáshoz.
* '''Data Center''': Egy datacenter-ben azokat Rack-eket csoportosítjuk, amik fizikailag egy szerverfarmon vannak.
:[[File:ClipCapIt-181106-205504.PNG]]
Alapértelmezetten minden node-unk a '''RACK1'''-be fog tartozni, és a '''DC1''' datacenterbe.
 
===Seed Nodes===
Minden egyes node-nak amit hozzáadnunk a cluster-hez szüksége van egy referencia node-ra, amitől le tudja kérdezni a cluster topológiáját (élő és halott node-ok, távolság..). Ezeket hívják seed-node-nak.
 
Minden egyes data-center-ben legalább két seed-node-ot kell létrehozni. A nem seed-node -knak a seed-nodeokat a cassandra.yaml fájlban kell statikusan beállítani. Alapértelmezetten csak a localhost van hozzáadva a listához:
<pre>
- seeds: "127.0.0.1"
</pre>
=Telepítés=
...
=CQL (Cassandra Query Language)alapok=
https://www.datastax.com/2012/01/getting-started-with-cassandra
<br>
 =Kulcsok és indexek kezeléselekérdezésekben=
==Kulcs használati alapelvek==
* Ugyan azzal a primary kulccsal (partition és clustering kulcs együttvéve) egy INSERT-et többször is ki lehet adni, elsőre beszúrja, másodikra frissíti a sort, tehát átírja a nem kulcs mezőket a sorban az új értékre.
Alap esetben sem a particionáló kulcsokra, sem az indexekre nem használhatjuk a <, <=, >, >= operációkat, csak az =, IN. {{warning|Az IN ('k1', 'k2',..) . {{warning|Az IN használata erősen ellenjavallott performancia okokból, de ha már használjuk, akkor az IN értékkészletét alacsonyan kell tartani. }}
(A másodlagos indexre megengedett a <,> operáció, ha a SASI implementációt használjuk)
 
Fontos, hogy ha a Clustering kulcsra írunk bármilyen megkötést (restriction-t) ami nem a '=', vagyis egyike a >,>=,<, <=, IN, CONTAINS megkötéseknek, akkor az összes sorban korábbi Clustering kulcsot is szerepeltetni kell, és azokban csak a '=' megkötés használható, vagy a <,>.. megkötése csak a sor legvégén használhatóak, a legutolsó clustering kulcsban, amit még beteszünk a WHERE -be (mert hogy a Clustering kulcsok megadása nem kötelező)
 
 
Ha tartományra kérdezünk le, akkor a kisebb mint és nagyobb mint (vagy fordítva) mindig ugyan arra a Clustering kulcsra kell legyen felírva és csak az utolsó kulcsra.
 
:[[File:ClipCapIt-181015-221508.PNG]]
Helyes, pert benne van a particionáló kulcs, és mivel a col3 szerepel a WHERE-ben, a col2 is ott van, ahol csak az '=' restriction-t használtuk. Mivel a col3 az utolsó olyan Clustering kulcs, amit szerepel a WEHRE-ben, ezért ott használhattuk a '>' megkötést.
SELECT * FROM adam.test1 WHERE col1 = 'k1' AND col2 = 'Z' AND col3 > 'A'
 
:[[File:ClipCapIt-181015-221508.PNG]]
Helyes, mert a tartományra lekérdezés a végén van, és a nagyobb mint-kisebb mint operációkban ugyan az az oszlop (Clustering kulcs) szerepel, ráadásul a legvégén, különben nem lenne helyes.
SELECT * FROM adam.test1 WHERE col1 = 'k1' AND col2 = 'Z' AND col3 > 'A' AND col3 < 'Z'
A tartomány több Clustering kulcsra is vonatkozhat, még akkor is ha aszimmetrikus, tehát ez is helyes:
SELECT * FROM adam.test1 WHERE col1 = 'k1' AND (col2 , col3) > ('A','A') AND col2 < 'Z'
 
Helytelen, mert a col2-re nem az '=' operátort használjuk, pedig megadtuk a col2 utáni soron következő kulcsot is a col3-at. Mindig csak a legutolsó kulcs-ra használhatunk az '='-től eltérő operációt.
SELECT * FROM adam.test1 WHERE col1 = 'k1' AND col2 > 'Z' AND col3 > 'A'
 
 
 
===IN megkötés===
Az IN megkötést mind Particionáló kulcsra mind Clustering kulcsra lehet alkalmazni a sorrendre való tekintet nélkül (ezt a 2.2-ben vezették be, azelőtt ezt is csak az utolsó kulcs-ra lehetett rárakni).
 
 
IN az utolsó előtti Clustering kulcsra:
SELECT * FROM adam.test1 WHERE col1 = 'k1' AND col2 IN ('A', 'B') AND col3 = 'Z'
 
 
IN a particionáló kulcson:
SELECT * FROM adam.test1 WHERE col1 IN ('k1', 'k2') AND col2='A' AND col3 < 'Z';
 
 
Több oszlopos IN lekérdezés :
SELECT * FROM adam.test1 WHERE col1 = 'k1' AND (col2 , col3) IN (('A','A'), ('B','Z'));
 
===CONTAINS és CONTAINS KEY megkötések===
 
Láthatjuk, hogy az alaptáblában a TourId particionáló kulcs volt, az MW-ben viszont már csak Clustering key, ezzel teljesítettük a megkötést, hogy az alap tábla összes kulcsának szerepelnie kell az MW kulcsai között.
 
 
=Tervezés=
 
==Partíció mérete==
Egy partíció, vagyis azon cellák összessége, aminek ugyan az a particionáló kulcs csoportja, nem lehet nagyobb mint '''2 milliárd cella / partíció'''. Az egy partícióba eső cellák számát így lehet kiszámolni:
 
N<sub>v</sub> = N<sub>r</sub>*( N<sub>c</sub> − N<sub>pk</sub> − N<sub>s</sub>) + N<sub>s</sub>
 
* N<sub>v</sub> = össz cella szám, ezt akarjuk kiszámolni, ez nem lehet több mint 2 milliárd.
* N<sub>r</sub> = az összes sorok száma a partícióban
* N<sub>pk</sub> = primary kulcs oszlopok száma
* N<sub>c</sub> = az oszlopok száma a partícióban
* N<sub>s</sub> = statikus oszlopok
 
 
Mikor a tábla szerkezetet kitaláljuk, fontos, hogy előre megbecsüljük, hogy mi lesz az '''N<sub>v</sub>'''. Fontos hogy a legrosszabb esetet számoljuk ki beleszámolva a jövőbeli elképzelt növekedést is. Ha ez átlépné az 1 milliárdot, akkor be kell vezessünk újabb particionáló kulcsokat is.
 
Minél szélesebb egy tábla annál egyszerűbb ezt a korlátot elérni még relatíve kevés adattal is.
 
 
=Java kliens=
 
 
 
<source lang="java">
public class CassandraConnector {
 
private static CassandraConnector instance;
 
private Cluster cluster = null;
 
private Session session = null;
 
private MappingManager manager = null;
 
private CassandraConnector() {
initConnection();
}
 
public static CassandraConnector getInstance() {
 
if (instance == null) {
 
synchronized (CassandraConnector.class) {
if (instance == null) {
// if instance is null, initialize
instance = new CassandraConnector();
}
}
}
 
return instance;
}
 
private void initConnection() {
 
String host = System.getProperty("cassandra.default.host", "localhost");
String port = System.getProperty("cassandra.default.port", "9042");
String keyspace = System.getProperty("cassandra.default.keyspace");
String username = System.getProperty("cassandra.user", "user");
String password = System.getProperty("cassandra.pwd", "pass");
boolean withSSL = Boolean.parseBoolean(System.getProperty("cassandra.needSSL", "false"));
 
connect(nodes, withSSL, username, password, keyspace);
}
 
private void connect(String host, String port, boolean withSSL, String username, String password, String keyspace) {
 
try {
 
Cluster.Builder b = Cluster.builder().withSocketOptions((new SocketOptions()).setReadTimeoutMillis(1800000))
.withQueryOptions((new QueryOptions()).setFetchSize(100000));
 
 
builder.addContactPoint(host).withPort(port);
 
if (withSSL) {
b.withSSL();
}
 
if (username != null && username.trim().length() > 0 && withSSL) {
b.withCredentials(username, password);
}
 
session = cluster.connect();
 
manager = new MappingManager(session);
 
} catch (Exception e) {
System.out.println(e);
throw e;
}
}
 
 
public Session getSession() {
 
return session;
}
 
public MappingManager getManager() {
 
return manager;
}
 
 
 
@Override
public void finalize() {
try {
session.close();
cluster.close();
} catch (Exception e) {
// ...
}
}
}
</source>