1 Webanwendungen mit Apache HBase entwickeln Roman Roelofsen Managing Director (aka Alpha Geek) Weigle Wilczek GmbH Twitter: romanroe JAX, 19. April 2012
1
Webanwendungen mit Apache HBase entwickeln
Roman RoelofsenManaging Director (aka Alpha Geek)
Weigle Wilczek GmbH
Twitter: romanroe
JAX, 19. April 2012
2
WeigleWilczek - Uber Uns
I IT-Beratung, Software-Entwicklung
I Typische AnwendungenI ”Web-Anwendungen”I LagerverwaltungI Kennzahlenanalyse, Dashboards
I Kein Facebook, Twitter, etc.
3
Typischer Stack
I JVM
I Jetty/Tomcat/WebSphere
I Guice/Spring
I Diverse Web-Frameworks
I Diverse RDBMS (MySQL, PostreSQL, ...)I Ein paar Exoten
I MongoDBI Prevayler
4
Probleme mit RDBMS
I Schlechte horizontale SkalierungI Option A) Partitionierung nach FunktionI Option B) Partitionierung nach PK, Hash, etc.
I Vertikale Skalierung ist teuer
5
Warum NoSQL?
I Buzzword! (== Spaß, meistens jedenfalls)I Partitionierungsfahigkeiten sind wirtschaftlich interessant
I Skaliert die Abfragezeit mit der Serveranzahl?I Kosten fur Redundanz konnen berechnet werdenI memcached, etc. wird uberflussig (weniger Administration)
I NoSQL-Architekturen passen gut zum Cloud-Trend
I Apache Hadoop wird (in den USA) sehr gehyped
I → Evaluierung
6
Warum NoSQL?
7
Warum NoSQL?
8
Warum NoSQL?
9
Auswahl NoSQL
I Sammelbegriff fur viele DatenbankenI Dokumenten-DatenbankI Graph-DatenbankI Key/Value DatenbankI Objekt-DatenbankI Google’s BigTable, Amazon’s DynamoI ...
10
Google’s BigTable
I Verteilte Key/Value Datenbank
I Basiert auf Google File System
I Fehlertolerant
I Skalierbar
I Lauft auf ”billiger” Hardware
I Erlaubt Parallelisierung (MapReduce & Co.)
11
Apache Hadoop Stack
I HadoopI Verteiltes Dateisystem (HDFS)
I HiveI Abfragen
I MapReduceI Implementierung fur das HDFS
I HBaseI BigTable-Implementierung
I MediaGuardian Innovation Awards: Innovator of the YearI Wikipedia: ”...Hervorgehoben wurde, dass Hadoop so
vielseitige und weitreichende Anwendungen ermoglicht, dass essich als Beginn einer neuen Datenrevolution erweisen konne.”
12
Exkurs: HBase vs. Cassandra
I HBase VorteileI KonsistenzI Read-Performance > Write-Performance
I Cassandra VorteileI Write-Performance > Read-Performance
I Wichtig fur Business-AnwendungenI KonsistenzI Read-PerformanceI Abfragen
13
HBase - Datendesign
I Key/Value DatenbankI java.util.Map
I Row
Map<ROW_ID, DATA>
I Column Family
Map<ROW_ID, Map<FAMILY_ID, DATA>>
I Column
Map<ROW_ID, Map<FAMILY_ID, Map<COLUMN_ID, DATA>>>
I Alle Werte sind byte[]
Map<byte[], Map<byte[], Map<byte[], byte[]>>>
14
HBase - Datendesign
I Key/Value DatenbankI java.util.Map
I Row
Map<ROW_ID, DATA>
I Column Family
Map<ROW_ID, Map<FAMILY_ID, DATA>>
I Column
Map<ROW_ID, Map<FAMILY_ID, Map<COLUMN_ID, DATA>>>
I Alle Werte sind byte[]
Map<byte[], Map<byte[], Map<byte[], byte[]>>>
15
HBase - Datendesign
I Key/Value DatenbankI java.util.Map
I Row
Map<ROW_ID, DATA>
I Column Family
Map<ROW_ID, Map<FAMILY_ID, DATA>>
I Column
Map<ROW_ID, Map<FAMILY_ID, Map<COLUMN_ID, DATA>>>
I Alle Werte sind byte[]
Map<byte[], Map<byte[], Map<byte[], byte[]>>>
16
HBase - Datendesign
I Key/Value DatenbankI java.util.Map
I Row
Map<ROW_ID, DATA>
I Column Family
Map<ROW_ID, Map<FAMILY_ID, DATA>>
I Column
Map<ROW_ID, Map<FAMILY_ID, Map<COLUMN_ID, DATA>>>
I Alle Werte sind byte[]
Map<byte[], Map<byte[], Map<byte[], byte[]>>>
17
HBase - Datendesign
18
HBase - Datendesign
I Zeilen IDs werden Byte-Lexikographisch sortiert
I Innerhalb einer Zeile werden Spalten Byte-Lexikographischsortiert
I Zeilen- und Spaltenabschnitte konnen leicht ”gescannt”werden
I Wichtige Eigenschaft, um Indizes zu simulieren bzw. Abfragenzu ermoglichen
19
HBase - Column Families
I Column Families werden in unterschiedlichen Dateiengespeichert
20
HBase - Horizontale Skalierung
I IDs werden in Regionen aufgeteilt
I Regionen werden auf Region Servers gespeichert
I Regionen und Region Server werden je nach Auslastung neuangeordnet
21
HBase - Versionierung
I Jede ”Zelle” wird mit einem Timestamp gespeichert undautomatisch versioniert
Map<byte[], Map<byte[], Map<byte[], Map<Long, byte[]>>>>
I Default: 3 Versionen
I Timestamp wird beim Schreiben vom Server gesetzt,
I kann aber vom Client uberschrieben werden
22
HBase - Versionierung
I Jede ”Zelle” wird mit einem Timestamp gespeichert undautomatisch versioniert
Map<byte[], Map<byte[], Map<byte[], Map<Long, byte[]>>>>
I Default: 3 Versionen
I Timestamp wird beim Schreiben vom Server gesetzt,
I kann aber vom Client uberschrieben werden
23
HBase - Versionierung
I Jede ”Zelle” wird mit einem Timestamp gespeichert undautomatisch versioniert
Map<byte[], Map<byte[], Map<byte[], Map<Long, byte[]>>>>
I Default: 3 Versionen
I Timestamp wird beim Schreiben vom Server gesetzt,
I kann aber vom Client uberschrieben werden
24
HBase API Grundlagen
25
HBase - Getting Started
I Standalone ModeI Zur Entwicklung kann HBase im Standalone Modus gestartet
werdenI HDFS wird dann nicht verwendetI HBase und ZooKeeper laufen in der selben JVM
I ShellI bin/hbase shell
hbase(main):001:0> status
1 servers, 0 dead, 0.0000 average load
26
Exkurs: Ubuntu 11.10 und HBase
I /etc/hosts
127.0.0.1 localhost
#127.0.1.1 servername
192.168.178.44 localhost
27
HBase API - Tabellen anlegen
I Tabellen mussen explizit angelegt werden
I Shell
create ’person’, ’cfamily1’
I Java
Configuration c = new Configuration();
HBaseAdmin admin = new HBaseAdmin(c);
HTableDescriptor person = new
HTableDescriptor("person");
person.addFamily(new HColumnDescriptor("cfamily1"));
admin.createTable(person);
28
HBase API - Tabellen anlegen
I Tabellen mussen explizit angelegt werden
I Shell
create ’person’, ’cfamily1’
I Java
Configuration c = new Configuration();
HBaseAdmin admin = new HBaseAdmin(c);
HTableDescriptor person = new
HTableDescriptor("person");
person.addFamily(new HColumnDescriptor("cfamily1"));
admin.createTable(person);
29
HBase API - Daten speichern
I Shell
put ’person’, ’id_1’, ’cfamily1:firstname’, ’John’
put ’person’, ’id_1’, ’cfamily1:lastname’, ’McCarthy’
I Java
Configuration c = new Configuration();
HTable person = new HTable(c, "person");
Put put = new Put(Bytes.toBytes("id_1"));
put.add(Bytes.toBytes("cfamily1"),
Bytes.toBytes("firstname"),
Bytes.toBytes("John"));
put.add(Bytes.toBytes("cfamily1"),
Bytes.toBytes("lastname"),
Bytes.toBytes("McCarthy"));
person.put(put);
30
HBase API - Daten speichern
I Shell
put ’person’, ’id_1’, ’cfamily1:firstname’, ’John’
put ’person’, ’id_1’, ’cfamily1:lastname’, ’McCarthy’
I Java
Configuration c = new Configuration();
HTable person = new HTable(c, "person");
Put put = new Put(Bytes.toBytes("id_1"));
put.add(Bytes.toBytes("cfamily1"),
Bytes.toBytes("firstname"),
Bytes.toBytes("John"));
put.add(Bytes.toBytes("cfamily1"),
Bytes.toBytes("lastname"),
Bytes.toBytes("McCarthy"));
person.put(put);
31
HBase API - Daten lesen
I Shell
hbase(main):001:0> scan ’person’
ROW COLUMN+CELL
id_1 column=cfamily1:firstname, timestamp=123,
value=John
id_1 column=cfamily1:lastname, timestamp=123,
value=McCarthy
1 row(s) in 0.1100 seconds
32
HBase API - Daten lesen
I Java
Configuration c = new Configuration();
HTable person = new HTable(c, "person");
Get get = new Get(Bytes.toBytes("id_1"));
get.addColumn(Bytes.toBytes("cfamily1"),
Bytes.toBytes("firstname"));
get.addColumn(Bytes.toBytes("cfamily1"),
Bytes.toBytes("lastname"));
Result result = person.get(get);
byte[] firstname =
result.getValue(Bytes.toBytes("cfamily1"),
Bytes.toBytes("firstname"));
..
33
HBase API - Daten loschen
I Shell
delete ’person’, ’id_1’, ’cfamily1:firstname’
I Java
Configuration c = new Configuration();
HTable person = new HTable(c, "person");
Delete delete = new Delete(Bytes.toBytes("id_1"));
delete.setTimestamp(1);
person.delete(delete);
34
HBase API - Daten loschen
I Shell
delete ’person’, ’id_1’, ’cfamily1:firstname’
I Java
Configuration c = new Configuration();
HTable person = new HTable(c, "person");
Delete delete = new Delete(Bytes.toBytes("id_1"));
delete.setTimestamp(1);
person.delete(delete);
35
HBase API - Scanner
I Java
Configuration c = new Configuration();
HTable person = new HTable(c, "person");
Scan s = new Scan(
Bytes.toBytes("id_1"),
Bytes.toBytes("id_5"));
s.addFamily(Bytes.toBytes("cfamily1"));
ResultScanner rs = person.getScanner(s);
// while....
Result r = rs.next();
r.getValue(Bytes.toBytes("cfamily1"),
Bytes.toBytes("firstname"));
...
36
HBase und Web-Anwendungen
37
Web-Anwendungen
I Mehrere Threads im ServerI API muss (sollte) Thread-safe sein
I Synchronisation beim Datenbankzugriff notwendigI Locking, Isolierung, ...
I Multi-User UmgebungI Gemeinsame Updates waren wunschenswert
I Viele Views auf die selben DatenI Anzahl Reads > Anzahl Writes
I Viele, kurzlebige Ausfuhrungen
38
Atomare Operationen, Isolierung
I Atomare Operationen sind:I Put, Get, Delete, ...I Keine Gruppierung moglich!I Jeder Aufruf ist direkt ein RPC-call
I Client-side BufferI table.setAutoFlush(false)I Put-Operationen werden im Client gepuffertI Explizit senden mit table.flushCommits()I Implizit basierend auf Heap-Verbrauch
39
Atomare Operationen, Isolierung
I Atomare Operationen sind:I Put, Get, Delete, ...I Keine Gruppierung moglich!I Jeder Aufruf ist direkt ein RPC-call
I Client-side BufferI table.setAutoFlush(false)I Put-Operationen werden im Client gepuffertI Explizit senden mit table.flushCommits()I Implizit basierend auf Heap-Verbrauch
40
Atomare Operationen, Isolierung
I Batch-Support
List<Row> ops = new LinkedList<Row>();
Put put1 = new Put(Bytes.toBytes("id1");
put1.add(Bytes.toBytes("cf"), Bytes.toBytes("col"),
Bytes.toBytes("abc"));
ops.add(put1);
Put put2 = new Put(Bytes.toBytes("id2");
put2.add(Bytes.toBytes("cf"), Bytes.toBytes("col"),
Bytes.toBytes("def"));
ops.add(put12;
Object[] result = new Object[ops.size()];
table.batch(ops, result);
41
Atomare Operationen, Isolierung
I Reihenfolge im Batch-Betrieb ist nicht explizit und wird vomClient optimiert
I Daher sollten keine Put- und Delete-Operationen fur dieselben Rows gemischt werde
I Egal wie und wann, andere Clients sehen Zwischenschritte
42
Compare and Swap
I HBase erlaubt atomare compare-and-swap Operationen
Put update = new Put(Bytes.toBytes("id1"));
update.add(
Bytes.toBytes("cfamily1"),
Bytes.toBytes("firstname"),
Bytes.toBytes("Max"));
boolean wasUpdated = person.checkAndPut(
Bytes.toBytes("id1"),
Bytes.toBytes("cfamily1"),
Bytes.toBytes("version"),
Bytes.toBytes("3"),
update);
I Ermoglicht Optimistic-Locking
43
Synchronisation uber Locks moglich
I Mehrere Operationen konnen nicht als eine, atomareOperation durchgefuhrt werden
I Andere Clients werden immer Zwischenschritte sehen
I Wenn Clients sich auf gemeinsame Locks einigen, istzumindest eine Synchronisation moglich
RowLock id1lock =
person.lockRow(Bytes.toBytes("id1"));
...
...
person.unlockRow(id1lock);
44
Synchronisation uber Locks moglich
I ProblemeI Welche Rows eignen sich als gemeinsamer Lock?I Gefahr von Deadlocks
45
Thread-Sicherheit der API
I HTableI Das Erstellen einer HTable-Instanz kann mehrere Sekunden
dauernI Instanzen sollten daher immer wiederverwendet werden
I Problem: Instanzen sind nicht Thread-safeI Losung: HTablePool
HTablePool pool = new HTablePool(c,
Integer.MAX_VALUE);
HTable table = pool.getTable("person");
...
table.close();
46
Thread-Sicherheit der API
I HTableI Das Erstellen einer HTable-Instanz kann mehrere Sekunden
dauernI Instanzen sollten daher immer wiederverwendet werdenI Problem: Instanzen sind nicht Thread-safe
I Losung: HTablePool
HTablePool pool = new HTablePool(c,
Integer.MAX_VALUE);
HTable table = pool.getTable("person");
...
table.close();
47
Thread-Sicherheit der API
I HTableI Das Erstellen einer HTable-Instanz kann mehrere Sekunden
dauernI Instanzen sollten daher immer wiederverwendet werdenI Problem: Instanzen sind nicht Thread-safeI Losung: HTablePool
HTablePool pool = new HTablePool(c,
Integer.MAX_VALUE);
HTable table = pool.getTable("person");
...
table.close();
48
”Transaction per View”
I RDBMS + Web-AnwendungI Fur jeden Request wird eine Transaction erstelltI RuntimeExceptions fuhren zu einem Rollback,I ... sonst Commit
I HBase + Web-AnwendungI Jeder Request braucht eigene HTable-InstanzenI Problem: Wir wissen vorher nicht, auf welche Tabellen
zugegriffen wirdI Losung: Proxy Objekt + Thread-local HTable-Zuordnung
49
”Transaction per View”
I RDBMS + Web-AnwendungI Fur jeden Request wird eine Transaction erstelltI RuntimeExceptions fuhren zu einem Rollback,I ... sonst Commit
I HBase + Web-AnwendungI Jeder Request braucht eigene HTable-Instanzen
I Problem: Wir wissen vorher nicht, auf welche Tabellenzugegriffen wird
I Losung: Proxy Objekt + Thread-local HTable-Zuordnung
50
”Transaction per View”
I RDBMS + Web-AnwendungI Fur jeden Request wird eine Transaction erstelltI RuntimeExceptions fuhren zu einem Rollback,I ... sonst Commit
I HBase + Web-AnwendungI Jeder Request braucht eigene HTable-InstanzenI Problem: Wir wissen vorher nicht, auf welche Tabellen
zugegriffen wird
I Losung: Proxy Objekt + Thread-local HTable-Zuordnung
51
”Transaction per View”
I RDBMS + Web-AnwendungI Fur jeden Request wird eine Transaction erstelltI RuntimeExceptions fuhren zu einem Rollback,I ... sonst Commit
I HBase + Web-AnwendungI Jeder Request braucht eigene HTable-InstanzenI Problem: Wir wissen vorher nicht, auf welche Tabellen
zugegriffen wirdI Losung: Proxy Objekt + Thread-local HTable-Zuordnung
52
Row IDs erzeugen
I ID Erzeugung ist nicht atomarI GET aktueller WertI +1I PUT neuer Wert
I Counter
HTable table = new HTable(conf, "IDs");
long counter1 = table.incrementColumnValue(
Bytes.toBytes("person_ids"),
Bytes.toBytes("family"),
Bytes.toBytes("next_id"),
1);
53
Row IDs erzeugen
I ID Erzeugung ist nicht atomarI GET aktueller WertI +1I PUT neuer Wert
I Counter
HTable table = new HTable(conf, "IDs");
long counter1 = table.incrementColumnValue(
Bytes.toBytes("person_ids"),
Bytes.toBytes("family"),
Bytes.toBytes("next_id"),
1);
54
Primary Keys
55
Secondary Keys
56
Secondary Keys - Backrefs
57
Secondary Keys und Scanner - Stadtenamen
I Java
Configuration c = new Configuration();
HTable person = new HTable(c, "personFK");
Scan s = new Scan(
Bytes.toBytes("FK:Stadt:A"),
Bytes.toBytes("FK:Stadt:L"));
ResultScanner rs = person.getScanner(s);
...
58
Secondary Keys und Scanner - Postleitzahlen
I Java
Configuration c = new Configuration();
HTable person = new HTable(c, "personFK");
Scan s = new Scan(
Bytes.toBytes("FK:PLZ:50667"),
Bytes.toBytes("FK:PLZ:51150"));
ResultScanner rs = person.getScanner(s);
...
59
Secondary Keys und Scanner - Alter?
Configuration c = new Configuration();
HTable person = new HTable(c, "personFK");
Scan s = new Scan(
Bytes.toBytes("FK:Alter:2"),
Bytes.toBytes("FK:Alter:51"));
ResultScanner rs = person.getScanner(s);
...
I Problem: IDs werden Byte-Lexikographisch sortiertI 2I 3I 51I 6I 7
60
Secondary Keys und Scanner - Alter?
Configuration c = new Configuration();
HTable person = new HTable(c, "personFK");
Scan s = new Scan(
Bytes.toBytes("FK:Alter:2"),
Bytes.toBytes("FK:Alter:51"));
ResultScanner rs = person.getScanner(s);
...
I Problem: IDs werden Byte-Lexikographisch sortiertI 2I 3I 51I 6I 7
61
Secondary Keys und Scanner - Alter?
I PaddingI 2 → 0002I 12 → 0012I 42 → 0042I 135 → 0135
I ProblemeI Maximale Zahlenlange muss bekannt sein
62
Secondary Keys und Scanner - Alter?
I PaddingI 2 → 0002I 12 → 0012I 42 → 0042I 135 → 0135
I ProblemeI Maximale Zahlenlange muss bekannt sein
63
Secondary Keys und Scanner - Alter?
I Wie ware eine andere Zahlendarstellung? (Additionssystem)I 2 → 2I 12 → 92I 42 → 99992I 135 → 99999999999995
I ProblemeI Sehr ineffizient bzgl. PlatzbedarfI Zahl ist nicht lesbar
64
Secondary Keys und Scanner - Alter?
I Wie ware eine andere Zahlendarstellung? (Additionssystem)I 2 → 2I 12 → 92I 42 → 99992I 135 → 99999999999995
I ProblemeI Sehr ineffizient bzgl. PlatzbedarfI Zahl ist nicht lesbar
65
Secondary Keys und Scanner - Alter?
I Losung: Additionssystem und Stellenwertsystem kombinierenI 2 → 1#2I 12 → 2#12I 42 → 2#42I 135 → 3#135
66
Fazit 1/6
I Entweder man macht es so, wie HBase es will, oder man lasstes bleiben!
I Bitte keine O/H(Base) Mapper!
67
Fazit 2/6
I Kostenargumente sind schwer dem Kunden zu verkaufen,wenn Oracle und Co. schon im Einsatz ist, Lizenzenvorhanden sind, etc.
68
Fazit 3/6
I HBase kann sehr gut mehrere Rechner nutzen
I Jedoch muss man diese Eigenschaft nutzen, um uberhaupterst eine gute Performance zu erhalten
69
Fazit 4/6
I HBase (& Co.): Imperativ
I SQL: Deklarativ
70
Fazit 5/6
I Es ist nicht leicht, im Team stabile Tests zu erstellen
I Man vermisst jdbc:h2:mem:testdb
71
Fazit 6/6
I Die Vorteile von HBase wurden durch Verzicht ermoglicht
72
Wir stellen ein!
I Wir suchen professionelle Java-Geeks!I Wir bieten eine gesunde Mischung aus
I ProgrammiererI BeraterI Kicker-Profi
I Bitte bei mir melden!I [email protected]
73
Vielen Dank fur Ihre Aufmerksamkeit!
Fragen?