HTTP2 and Spring Boot


HTTP2 is the successor of HTTP/1.1. It enables faster connections, multiplexing and server pushes via TCP. It’s not a completely new protocol, it’s more an evolutionary step to improve  HTTP/1.1 that has been in use since 1999. Since that year, many thing have changed: more users, more content, more cyber crime. So, it’s quite obvious that also security issues are in the focus of HTTP2.

In this blog I want to take a closer view on how HTTP2 can be used in a Spring Boot environment. As reference, I set up a project you can see on github:

HTTP2 – the security basics

The RFC7540 has a separate section concerning security. You can find several hints on why the standard recommends secured communication. HTTP2 works on TLS (Transport Layer Security). This on the  first glance is no news. But in fact there is something new, it’s TLS ALPN („TLS application-layer negotiation“). This has some impact on (not only) Spring Boot applications. See more details in the next section.

There might be people saying: „Okay, something new. Forget about TLS!“ Well, no. Let me explain: HTTP2 defines protocol identifiers offering two ways of communication:

  • „h2“ – means that the protocol uses HTTP/2 over TLS (TLS1.2)
  • „h2c“ – means that the protocol uses HTTP2 over cleartext TCP

So, HTTP2 definitely says that there CAN be a communication without TLS. Don’t forget the client side: most browsers only support secured HTTP2 communication. Of course, there are ways to avoid TLS, but at the moment it seems that browsers make „h2″/TLS mandatory.


Spring Boot and HTTP2

First of all, not every (servlet) container does support HTTP2 yet. Spring Boot’s standard container Tomcat starts support with Tomcat 9 (not yet released by 1 July 2016). Luckily, Jetty and Undertow already support HTTP2, both can be used by Spring Boot alternatively.

The second challenge concerns TLS ALPN. This protocol isn’t supported by Java JDK 7 and 8. Java JDK 9 is told to natively support TLS ALPN. For Java 7 and 8, there is already a workaround. Unfortunately this support has to be injected during Java VM start. You will find more information later in this blog.

In summary there are four steps to do:

  • replace tomcat by Undertow
  • configure Undertow
  • create a keystore with a server key pair (optionally signed to avoid well-known alerts in browsers)
  • during start-up inject ALPN

Since descriptions can be easily misleading, I built up a project that gives a complete overview:

So, these are the details…

Replace Tomcat by Undertow

That’s quite simple. This is done by excluding tomcat from spring-boot-starter-web package and adding the spring-boot-starter-undertow package instead. This example shows the gradle configuration (build.gradle). The same way also works in Maven POM:

dependencies {
  // ...
  compile("org.springframework.boot:spring-boot-starter-web") {
    exclude module: "spring-boot-starter-tomcat"
  // ...

Now there is some configuration necessary. This is done in the following section.


Configure Undertow

Undertow needs to be told to support HTTP2. This is done by an undertow option (HTTP2_ENABLE). In the reference project this is done by a programmatic configuration.

This is the  core of this step:

private UndertowBuilderCustomizer createCustomizerWithHttp2() {
   new UndertowBuilderCustomizer() {
       void customize(Undertow.Builder builder) {

Okay, assume Undertow is configured. But Spring Boot still wouldn’t work, because there is no key pair needed for TLS. So, let’s create the Keystore.


Create Keystore

Spring Boot can include most options and needed files by a resources directory. We also use that for the keystore. You should replace the keystore.jks file in the reference project.  More information about this key generation can be found in the reference’s The most important step is shown here.

ollihoo@ws:~/src/main/resources$ keytool -genkeypair -alias mycert -keyalg RSA -sigalg MD5withRSA -keystore my.jks -storepass secret -keypass secret -validity 9999

As a result, you should find a new keystore.jks file. Make sure, it is referenced in the with it’s storepass and keypass to grant access.

Does it run now? Well, yes. But without HTTP2. There is another step to do.


Inject ALPN standard in Java 8

AS already mentioned, HTTP2 uses a new protocol called TLS ALPN (TLS application-layer protocol negotiation). This is the protocol that is also defined in

server.ssl.protocol: TLSv1.2

The really last step is now to inject a library called org.mortbay.jetty.alpn:alpn-boot into the JVM. The procedure again is described in the

The correct alpn-boot library is defined by the JDK you are using. You can find out your version by typing java -version in the command line. Please download the library and make it easily reachable for your Application callup. With the command

java -Xbootclasspath/p:./alpn-boot-8.1.6.v20151105.jar -jar myapplication.jar

This command starts Spring Boot application. The option -Xbootclasspath/p: ensures the integration of TLS ALPN into the security layer. The reason for this complicated way is caused by the lack of an configurable API to the security layer in the Java standard library.

So, Spring Boot is configured to offer HTTP2 endpoints. Try it by starting the reference project.

By simply starting „http://localhost:8080/“ in Firefox, you should see an response header like this:

HTTP/2.0 200 OK

Content-Type: text/html;charset=UTF-8
Content-Length: 237
x-application-context: application:8443
Content-Language: de-
Date: Sun, 03 Jul 2016 09:04:40 GMT
X-Firefox-Spdy: h2

As you can see here, the protocol used is HTTP/2.0. Awesome. And you see the used protocol „h2“ that is listed in header field „X-Firefox-Spdy“. So this works well.

One last hint: Undertow offers fallback mechanisms that switch back to HTTP/1.1 when there is now ALPN available. There might be more things that cause that. To ensure that your application runs on HTTP2 care about tests.


HTTP2 is not yet an established standard in web containers. The lack of good support of TLS ALPN makes the delivery more complex than needed. But it’s possible. So, try the future.

There are questions? Let me know! Contact me by mail: oliver at


Development without continuously restarting


I love development. Add some code. Check that it works. Ehhhh….  Why doesn’t my change work???? …… ah, I forgot the restart….

You know that? I hate it!

This is my situation: I want add some Javascript code to my Spring Boot application. So, there is no change in the application back end, only in the front end.

Unfortunately I don’t get these frontend changes into my application container fast enough. An average restart took me ~7 secs. When doing small changes, it happens that I do 5 restarts within 5 minutes. With that approach I would have to wait 35 seconds for testing an implementation. That’s too much waste.

In this article I describe what I have done to solve this. Or better: I found a way to reduce the effort. Fortunately there are some mechanisms that support resource reloading. In summary I changed IDEA to use the gradle daemon instead of it’s recommended gradle wrapper implementation. So the reload only takes  ~700ms per iteration.

For more details: go on reading. Feedback is of course always appreciated.

The Setup

  • Spring Boot 1.3.3
  • Languages: Java, Groovy
  • Resources in /src/main/resources/static/*


Reload? Restart?

There is a difference between reloading and restarting.

Reloading means the replacement of artifacts in a running system, i.e. replacing method implementations, new classes, another resource that is put into the classpath, etc. Advantage of this is, that I can rapidly iterate through the my dev steps. Disadvantage is, that there can be incompatible artifacts in the container that cause strange exceptions.

A restart performs a shutdown on the the old container and starts a new one. The advantage is a completely fresh system with it’s last recent resources.  Disadvantage is the  break (several seconds), you have to accept.


The spring-boot-dev way

The official documentation recommends the spring-boot-devtools [SPDev]. The plugin can be used for several things. The main issue is a restart (!) when classes in the classpath change. This is done by a special class loader provided by the Spring Boot plugin.

Unfortunately this isn’t an useful approach:

  • it still is a restart, not a reload
  • it only works when classes are automatically replaced by IDEA IDE. It seems that this doesn’t happen. IDEA builds the project before it starts the Spring Boot application. But that’s too late for me
  • Resource files are not the concern of this plugin

So, this solution isn’t quite the right thing for me.


IntelliJ IDEA support

Unfortunately IDEA’s  support for Spring Boot seems to be a bit basic (in the Community edition :-)). These are my findings:

  • IDEA puts its resource data directly under build/resources.
  • [JB2016] describes a HotSwap mechanism for class methods; it’s not used  when resources change
  • There is not automatic deployment  by IDEA into this directory

So, also IDEA doesn’t offer a good way to enable faster iteration through Javascript code in a Spring Boot environment.


Gradle support

When you use gradle, there is a target called processResources. It’s faster  (4.5 secs) than restarting the whole spring project (~7 secs). There is another trick to make it even faster. Gradle offers a daemon mode [GRDLD]. With some configuration, it starts the first time and then keeps in background as a daemon process.

So, this is the workaround. After having done my changes I manually have to trigger this processRessource job. It’s not as good as I expected but it reduces the processing time to  a few milliseconds (700ms). That’s okay for me as for the first approach.

This is what you I have done…

… with gradle

  • install latest gradle version
  • read the instructions in [GRDLD]; it sets the daemon mode to on and keeps gradle running

… with IDEA

  • go to IDEA and Edit your Gradle settings (File -> Settings -> Build, Execution, Deployment)
  • switch OFF „Use default gradle wrapper“
  • ensure that your installed gradle version is found
  • (optional) go to the Gradle menu and right-mouse-click on job „processResources“. In the context menu select „Assign Shortcut…“, create one, i.e. Ctrl-Shift-#

I hope I was able to explain my approach. If there are better ways to solve that (besides from splitting this project into nodeJS project for the frontend and  Spring Boot for the backend 😉 ), let me know.


phantomjs on raspberry


Okay, what I am doing here? I want to use phantomjs as part of an application that spiders my servers for tests. This testing environment is thought to be executed on raspberry instances. Why? It’s cheap, it’s silent, it’s perfect for me.

So, how do I deploy it? The easiest way is to use docker. Why? It enables me to simply to docker-compose for my instances and to start phantomjs-docker as standalone container with an interface to run it via selenium (see phantomjs –webdriver=<PORT>).

First tries were very promising.  I simply built up a phnatomjs-docker version. It’s amazing: docker can automatically build your image via Github Dockerfiles. See for more information.

Well, there is one problem: raspberry is not a x86 architecture, it bases on ARM. Luckily there are lots of linux packages, that run on this platform. This also suits to docker that has been built by Hypriot. This is where you will find more information:   Hypriot – getting started.

So, there is only one question left. Is there an ARM-based, raspberry-compatible docker package for raspberry? Well, yeaooooo…. No:

Okay, so I should try to build it for myself…

Build phantomjs on raspberry in a docker container

I don’t want to re-invent the wheel. There are lots of instructions in the web. This one is the most promising: how to compile phantomjs on the raspberry pi 2.

Okay, some remarks to the article. Of course, you should use the most current branch. Ariya Hidayat, the founder of phantomjs, works on branches for every new version. When I tried out (26.3.2016), I used 2.0 which threw – very late – an error and stopped. This bug is solved, fortunately. So, my finding: it makes sense to use the most recent branch.

Another remark: the instructions recommend to use a hdd or ssd to build. Do that!! It really speeds up compiling. The second thing is: use this f***ing –jobs 2 parameter. Without it, it gets a nightmare, because it builds and builds and… stops, because of full RAM.

To avoid more problems, I also decided to build phantomjs in a docker container. The image I used was hypriot/rpi-node:latest. Just start it with this command:

docker run -ti hypriot/rpi-node:latest /bin/bash

And voilà: 6 hours later, the build was perfectly done.

It works! What’s next?

I really tried to save the development state into a docker container. I doesn’t make sense. The building job produces changes of more than 1.5 GB. That’s nothing, you would like to get into a repository.

So, there is an alternative. In fact, the only thing we want to get, is this phantomjs binary. So copy it, and simply put it in this hypriot/rpi-node:latest container via a normal Dockerfile. This is it. You’ll find the project with this binary here:

Ah and of course, you also can download it from

Any questions, comments? Don’t hesitate to write me.

Docker mit Spring Boot: wie baue ich ein Image?



Ich möchte ein funktionsähiges docker-Image bauen, das die Applikation beherbergt und dessen Container mit einem neo4j-Container kommunizieren kann.

application-wo-docker docker-application


Ich arbeite auf einem Linux-System (Ubuntu 14.0 LTS), auf dem docker 1.9.1 läuft. Um die Platformvoraussetzungen zu erfüllen, lade ich die entsprechenden Images:

docker pull neo4j
docker pull java:8

Damit wäre die Pakete, die ich brauche, schon mal auf meinem Rechner.

Einrichtung von Java

Das Java-8-Image ist die Basis für das Applikations-Image. Daher möchte ich dieses in meiner privaten Registry speichern.

docker pull java:8

Mit dem Tag ‚java8‘ kann ich dann später die Applikation aufsetzen.

Einrichtung von neo4j

Neo4j bietet einen Container an, der die eigentlichen Nutzdaten außerhalb des Containers speichert. Um dieses Feature zu nutzen, mache ich die folgenden Schritte (als root, mit kurzem Wechsel zu einem neuen Nutzer namens neo4j):

useradd -U -m neo4j
sudo su - neo4j
mkdir geohierarchy

Den Container starte ich als root mit diesem Kommando:

docker run --detach --restart=always --name=neo4j --publish=7474:7474 --volume=/home/neo4j/geohierarchy:/data neo4j

Erklärungen: der Prozess soll im Hintergrund gestartet werden (–detach) und auch dann neu starten, wenn er gestoppt wurde (–restart=…). Die Einführung des Namens ist notwendig, um später aus der Applikation heraus die Datenbank ansprechen zu können. Im Verzeichnis /home/neo4j/geohierarchy wird die eigentliche Datenbank abgelegt. Diese wird in gemounteten Verzeichnis gespeichert und bleibt auch dann erhalten, wenn der Container gelöscht wird.

Der einfachste Test der Funktionalität: im Browser http://localhost:7474 eingeben und ggf. einrichten (z.B. Passwort setzen).


docker gibt einiges an Infrastruktur vor, das jetzt in Zusammenarbeit mit Spring Boot in Einklang gebracht werden muss. Das ist der Überblick:

  • neo4j erhält einen eigenen Container mit einem eindeutigen Namen: ’neo4j‘
  • der Spring-App-Container erhält den Namen ‚web‘
  • ‚web‘ wird mit dem Container ’neo4j‘ verlinkt (siehe 3.), dadurch kann die Server-Adresse automatisiert abgefragt werden (siehe 4.)
  • ‚web‘ benötigt außerdem noch eine Konfiguration für die Credentials. Dies wird über ein separat gemountetes Konfigurations-Directory erledigt (siehe unten).

Dockerfile aufsetzen

Hier zunächst der Link zum Dockerfile. Dies sind die zentralen Elemente, auf die ich mich beziehe:

FROM java:8 
ADD geographicalHierarchy-0.0.1-SNAPSHOT.jar app.jar 
RUN bash -c 'touch /app.jar' 
RUN mkdir /config 
VOLUME /config 
ENTRYPOINT ["java","-jar","/app.jar", "--neo4j.serverUrl=http://${NEO4J_PORT_7474_TCP_ADDR}:7474"]

Zeile 1: das Basispaket ist java:8 (siehe oben)

Zeile 2: die Applikation wird in app.jar umbenannt und später im ENTRYPOINT gestartet.

Zeile 4 und 5 legen ein Directory config/ an, das später automatisch von Spring Boot zur Konfiguration der App eingelesen wird. Dieses Directory kann von außen gemountet werden, um Konfigurationen einzuspielen (siehe 1).

Zeile 6 ist der Befehl, der ausgeführt wird, wenn der Container gestart wird. Man sieht, dass hier auch ein Parameter namens neo4j.serverUrl gesetzt wird. Die dort befindliche Variable NEO4J_PORT_7474_TCP_ADDR wird später vom Neo4j-Container bereitgestellt, wenn dieser mit der App verbunden wird. Hier erfolgt zunächst nur die Annahme, dass dies der Fall ist.

docker image bauen

Zum Bauen des Containers sollte eigentlich das Target buildDocker genügen. Im Hintergrund passiert folgendes: das Application-Jar und das docker-File wird in ein Verzeichnis kopiert (build/docker). Dort kann man dann den folgenden Befehl ausgeführen:

docker build -t localhost:5000/ollihoo/geohierarchy:0.5 .

Das Label 0.5 sollte entsprechend gesetzt werden. Als Ergebnis steht nun  ein docker-Image namens   localhost:5000/ollihoo/geohierarchy:0.5 zur Verfügung.


Konfiguration zur Verfügung stellen

Wie im Readme des Projektes beschrieben, sollte nun ein Konfigurationsverzeichnis mit einer angelegt werden. Diese Konfiguration ist dazu gedacht, die Credentials setzen zu können. Im Beispiel wird dieses Verzeichnis hier abgelegt: /opt/geoHierarchyConfig


Den Container zum Laufen bringen

Nun muss nur noch alles verbunden werden:

docker run -p 8080:8080 --link=neo4j -d --volume=/opt/geoHierarchyConfig:/config localhost:5000/ollihoo/geohierarchy:0.5


Durch -p wird der Port 8080 nach außen verfügbar gemacht. –link=neo4j zeigt an, dass der Container neo4j verfügbar sein muss. Ist dies der Fall, exponiert er Variablen, die von der App benutzt werden (siehe oben). -d lässt der Prozess im Hintergrund laufen. —volume sorgt dafür, dass die Konfiguration zur Verfügung steht.

Das war’s. Die Applikation sollte laufen.


Was ist noch unklar? Was fehlt? Was ist falsch oder kann besser gemacht werden? Schreibt mir 🙂



  1. Spring Boot: external configuration
  2. github: GeohierarchyGraph project
  3. docker – network
  4. docker hub: neo4j




  • URL:
  • Document-Datenbank
  • Code-Basis: C/C++
  • Interaktive Shell auf Javascript-Basis
  • ArangoDB query language (AQL)
  • erste produktive Version: 1.0 im Frühjahr 2012
  • Apache 2.0 license. Use free for non-commercial and commercial us

Recht junges Projekt aus Deutschland, das eine Dokument-DB aufgesetzt hat. Diese DB wird durch Aufsätze angereichert und kann u.a. auch als Graphen-Datenbank genutzt werden. Positiv herausgestellt sei, dass die Software an sich keinerlei Kosten verursacht, da sie unter einer Apache Lizenz freigegeben wurde. Skeptisch bin ich ein wenig beim Einsatz als Graphen-Datenbank, da diese stark an die darunterliegende Dokumentenstruktur gebunden ist.

Wer steht hinter ArangoDB

  • die ArangoDB GmbH, gegründet 2014, Firmensitz in Köln
  • die Firma verdient Geld über Support, Training und Consulting-Dienstleistungen


  • Basiert auf Document Mapsjeweils für Knoten und Kanten (Vertices and Edges)
  • Abbildung von Graphen durch eine Bibliothek (org/arangodb/general-graph)


  • Dokumenten-DB
  • Aufsätze und Bibliotheken auf Basis von V8
  • Unterstützung von Graphen- und Key-/Value-Pair- und Dokument-Systemen
  • interaktive Shell
  • Authentication: basic (username, password)
  • Unterstützung von ACID
  • lauffähig auf 32- und 64-bit-Systemen von Linux, Windows und MacOS
  • Schnittstellen: REST/json, AQL, http://localhost:8529/_admin, Gremlin/Blueprints
  • Clusterfähig über Foxx und einer Coordinator-Applikation
  • Basis-Treiber für diverse Systeme

Meine Kriterien

1. handelt es sich um eine native Graphen-Datenbank oder um einen Aufsatz?

Die Verwaltung von Graphen ist ein Zusatzmodul.

2. Welche Treiber und Schnittstellen gibt es?Wie gestaltet sich die Sprache, in der die Datenbank bedient wird?

Es gibt Treiber für Java, Javascript, Perl, Python, Scala, PHP, Clojure und andere ( Keine direkte Unterstützung durch Spring Data, es gibt jedoch auch einen Treiber für Blueprints/Gremlin, für die es bereits Spring-Data-Treiber gibt (dies habe ich nicht getestet!).

Die interne Sprache ist AQL (ArangoDB query language), die auch für Graphen-Funktionen genutzt wird (Original Doc: AQL – GraphFunctions)

3. Kosten?

ArangoDB is published under the Apache 2.0 license. This means essentially that you can use it free for non-commercial and commercial use. Kommerzieller Support wird durch die Firma angeboten (siehe oben).

4. Unterstützung durch die Community

Das Projekt ist auf github einsehbar ( Es gibt anscheinend 7 Entwickler, die regelmäßig am Code arbeiten. Es scheint auch Beiträge aus der Community zu geben (43), die aber immer nur kurz am Code beteiligt sind.

5. Lizenz

Apache 2.0 license für den gesamten Code.

Spannende Seiten zu diesem Thema

DB-Engines – ArangoDB


The stage is yours…

Alles hier stellt nur meine Meinung dar, ich bin gespannt auf Hinweise, Feedback und sonstiges. Traut Euch, schreibt mir 😉

Neo4j in Produktion

In diesem Beitrag möchte ich mir einmal die produktive Seite der Graphen-Datenbanken neo4j anschauen. Die Software hat mich technisch begeistert. Auf der Betriebs- und Kostenseite kommen nun Aspekte zum Vorschein, die aus dem Favoriten schnell ein Problem machen. Lest selbst und sagt mir Eure Meinung!

Das Szenario

Ich nehme als Beispiel-Applikation dieses Projekt: github-Reader. Der github-Reader nutzt eine neo4j, um ein Abbildung des Know-Hows innerhalb eines Unternehmens erstellen zu können. Technisch gesehen liest die Applikation Daten über die github-API aus und speichert diese in der Datenbank.

Technisch gesehen reicht für den aktuellen Stand der Entwicklung eine Standalone-Datenbank zusammen mit der Applikation (hier Spring Boot). Interessant wird es nun, wenn die Applikation nicht nur ein, sondern hunderte oder tausende Unternehmen abbilden soll. Für ein derartiges Szenario dürfte es recht eng werden in der Standalone-Datenbank. Ein Cluster wäre nett, um die Last verteilen und die Schreiboperation auf einem Knoten zu kapseln. Und natürlich wären regelmäßige Backups recht praktisch.

neo4j – Lösungsansatz

Die beiden Features lassen sich durch neo4j technisch lösen. Aber nun greift das Geschäftsmodell der neo4j: die genannten Features sind nicht mehr Teil der freien Community-Edition, die kommerziellen Lösungen werden nun relevant.

Wie schaut es nun aus bei der Enterprise-Edition (Vergleich der Editionen der neo4j)? Nun, auch neo4j möchte Geld verdienen. Zurecht! Leider wird das wichtigste Thema nicht auf den Seiten angesprochen: was kostet mich der Einsatz der Enterprise Edition?

Doch zunächst die guten Aspekte: für Startups mit weniger als 20 Mitarbeitern ist die Enterprise Edition frei. Darüber gibt es anscheinend spezielle Preismodelle (neo4j startup program). Alles andere kann offenbar mit dem Vertrieb in persönlicher Absprache geklärt werden.

Vorsicht: Migration

Die oben beschriebene Vorgehensweise enthält einige Aspekte, die Entwickler und Produktverantwortliche auf jeden Fall beachten sollten:

  • die Community Edition ist ein Evaluierungsprodukt; der Produktiveinsatz scheint mir nur in Spezialfällen möglich; dies entspricht auch der Empfehlung von neo4j
  • das Preisgefüge ist Verhandlungssache; über Themen des produktiven Einsatzes sollte daher früh nachgedacht werden (Skalierung, Backup, etc.)
  • für den Fall, dass eine frühe Absprache nicht stattfand, können im Falle einer notwendigen Migration ungeplante Kosten und Zeiten entstehen:
    • bei einer Migration zur Enterprise Edition muss der Fokus auf eine schnelle Einigung gelegt werden. Mehrere Verhandlungsrunden dürften sich dann als problematisch für das Produkt erweisen
    • bei einer Migration zu einer Alternativlösung muss die Applikation umgebaut werden; auch hier können ggf. Lizenzkosten entstehen
    • der Aufbau der Infrastruktur  an sich benötigt Zeit


Ich denke, dass neo4j durchaus einiges an Mehrwert bringen dürfte. Eine Kosten-/Nutzen-Rechnung lohnt sich also in jedem Fall. Was ist aber, wenn neo4j nicht in Frage kommt? Welche Alternativen gibt es noch so auf dem Markt? Hier ist eine Liste von anderen Graphen-Datenbanken (kein Anspruch auf Vollständigkeit):

  • Orient-DB (
  • Sparksee (
  • ArangoDB (ArangoDB)
  • Cayley (

Welche davon passt, hängt von ganz persönlichen Kriterien ab. Ich habe für mich die folgenden Aspekte aufgestellt, die ich prüfen möchte:

  • handelt es sich um eine native Graphen-Datenbank oder um einen Aufsatz?
  • Welche Treiber und Schnittstellen gibt es?
  • Wie gestaltet sich die Sprache, in der die Datenbank bedient wird?
  • Kosten?
  • Unterstützung durch die Community
  • Lizenz

Genau diesen Check werde ich in den nächsten Runden hier veröffentlichen. Wenn Ihr schon Erfahrungen gesammelt habt, lasst es mich gerne wissen.

Hierarchische Daten in einer Graph-Datenbank


Im Beitrag „Hierarchische Daten in einer relationalen Datenbank“ habe ich geprüft, ob ich hierarchische Daten in eine relationale Datenbank zu klopfen. Das hat nur bedingt gut geklappt.

Dieses Mal probiere ich es mit Graphen-Datenbank.  Hier ist zunächst das Modell:


Ich baue ein hierarchisches Modell auf Basis der politischen Ebenen des Landes Bayern auf. Ich möchte mit der Graphen-Datenbank dieselben Fragen klären, die ich im relationalen Modell klären wollte:

  • Ausgabe aller Bezirke von Bayern (RDBMS: leicht)
  • Alle Gemeinden des Landkreises Traunstein (RDBMS: leicht)
  • Gibt es eine politische Einheit mit dem Namen „Tutzing“? Wenn ja, wo in der Hierarchie befindet sich diese? (RDBMS: schwer)


Für Tests habe ich eine Datenbank aufgebaut, mit der man die oben genannten Fragen live klären kann:

Das Projekt hat im README eine Anleitung, wie man die Neo4j-Datenbank aufsetzt.

Aufbau der Datenbank

Die Datenbank nutzt Knoten, die die Hierarchie abbilden: (:Country), (:State), (:District), (:RuralDistrict), (:City), (:Community). Die zugehörigen Zuordnungen zur nächsthöhren Ebene werden durch :BELONGS_TO-Beziehungen abgebildet.

Zur Abfrage der Datenbank wird die Sprache Cypher benutzt, die in der Konsole des neo4j-Browsers angeboten wird.


Mit dieser Sprache lassen sich nun leicht die oben genannten Fragen beantworten:

Ausgabe aller Bezirke von Bayern

MATCH (d:Districts)-[:BELONGS_TO]->(s:State{name:"Bayern"}) return

Als Ergebnis werden wie erwartet alle sieben Regierungsbezirke zurückgegeben.

Alle Gemeinden des Landkreises Traunstein

MATCH (c:Community)-[:BELONGS_TO]->(:RuralDistrict{name:"Traunstein"}) RETURN

Es sollten 37 Gemeinden zurückgegeben werden.

Gibt es eine politische Einheit mit dem Namen „Tutzing“?

MATCH (n)-[:BELONGS_TO*1..]->(:State{name:"Bayern"}) 
WHERE = "Tutzing"

Als Ergebnis sollte ein Community-Knoten zurück gegeben werden. Das heisst, es gibt eine Gemeinde namens „Tutzing“.

Wo in der Hierarchie befindet sich „Tutzing“?

Die Frage wurde bereits beantwortet (Tutzing ist eine Gemeinde), ich werde die Frage daher dahingehend erweitern, wie die einzelnen Hierarchie-Stufen aussehen:

MATCH path=(n)-[:BELONGS_TO*1..]->(:State{name:"Bayern"}) 
WHERE = "Tutzing"
RETURN extract(node in nodes(path) |

Als Ergebnis wird ein Array zurück gegeben, das die einzelnen Hierarchie-Ebenen zurückgibt: [Tutzing, Starnberg, Oberbayern, Bayern]

Ergebnis und Ausblick

Im Vergleich zu einer relationalen Datenbank lassen sich die oben genannten Fragestellungen ohne größere Probleme lösen. Die expressive Sprache Cypher erlaubt eine eingängige Beschreibung der Knoten/Relationship-Muster und ermöglicht so, auch komplexere Fragen gut abbilden zu können.

Hier zunächst ein Vergleich der Elemente zwischen den beiden Modellen:

Element Relationales Modell Graphen-Modell
Entity/Dateneinheit Eine Zeile der Tabelle Knoten (z.B. (…) )
Typ der Dateneinheit Definiert durch Tabellen-Name Definiert durch die Label der Knoten (z.B. :State)
Eigenschaften der Entity Eine Tabellen-Zelle Eine property des Knotens (z.B. {name:“Bayern“})
Beziehung Kombination von Eigen- und Fremdschlüsseln, referenziert in Tabellen-Zellen Ein eigenes Relationship-element mit eigenem Label (z.B. -[:BELONGS_TO]-> )

Wie die Tabelle zeigt, arbeitet das Relationen-Modell sehr Tabellen-zentriert. Demgegenüber bietet das Graphen-Modell sehr viel mehr Modellierungs-Möglichkeiten an, die so komplexere Sachverhalte greifbarer machen.

Ausblick: in Hinblick auf den Abbildungsreichtum der Graphen-Datenbank stellt sich die Frage, warum relationale Datenbanken ein so zentrale Bedeutung bei zahlreichen Anwendungen haben. Eine Antwort ist, dass letztere sehr flexibel eingesetzt werden können und schnell und leicht zu erlernen sind.

Dies führt auch dazu, dass Entwickler sich daran gewöhnt haben, relational zu denken. Modelle werden lieber „platt geklopft“ als sie „anders“ abzubilden. Für viele Fälle ist das auch sicherlich der kostengünstigere Weg.

Dennoch lohnt sich gerade bei Graphen-Datenbanken der Blick über den Tellerrand. Vielleicht schaffen wir es ja, dieses Modell ebenso flexibel einzusetzen wie das der relationalen Datenbank.


Hierarchische Daten in einer relationalen Datenbank


Egal, ob wir Excel oder MySQL oder Oracle DB nutzen, das Prinzip dieser Tools ist dasselbe: wir reduzieren ein Objekt auf seine wesentlichen Eigenschaften, setzen diese Objekte zueinander in Beziehung und beantworten dann bestimmte Fragestellungen auf dieses Konstrukt.

Technisch gesehen sind Objekte („entities“) in einer Tabelle zusammen gefasst, die Eigenschaften sind die Spalten dieser Tabelle.  Die Beziehungen („relationships“) werden über Referenzen als weitere Spalte abgebildet. Bei komplexeren Beziehungen werden auch weitere Hilfstabellen aufgebaut. Um die Referenzierung zu erleichtern, werden oftmals eindeutige Schlüssel („primary key“) definiert, die wiederum als Fremdschlüssel („foreign key“) genutzt werden. Wer weitere Grundlagen benötigt, möge sich die Links zu den Relationalen Datenbanken (siehe unten) anschauen.

Aufbauend auf diesem Modell soll nun eine Hierarchie abgebildet werden. Als Beispiel dient mir hier die politische Aufteilung des  Bundeslandes Bayern.

Ich beschreibe zuerst, wie ich das Modell aufbaue und diskutiere dann Vor- und Nachteile bei der Implementierung bestimmter Fragestellungen. Ich möchte zeigen, dass relationale Datenbanken nur sehr bedingt für hierarchische Systeme geeignet sind. Dies und mögliche Alternativen diskutiere ich zum Schluss dieses Blogs.

Das Modell

Hier ist ein Beispiel: die folgende Tabelle zeigt eine Tabelle mit Bundesländern und deren Bezirken. Die Beziehung („relation“) habe ich dadurch eingebaut, dass die Spalte „Teil_von“ aussagt, zu welchem Bundesland die entsprechenden Bezirke gehören. Die Verknüpfung habe ich durch einen eindeutigen Identifier (ID) hergestellt.

ID Name Typ Teil_von
1 Bayern Bundesland
2 Oberbayern Bezirk 1
3 Niederbayern Bezirk 1
4 Oberpfalz Bezirk 1
5 Oberfranken Bezirk 1
6 Unterfranken Bezirk 1
7 Mittelfranken Bezirk 1
8 Schwaben Bezirk 1

Das Beispiel zeigt bereits das Problem dieser Art der relationalen Abbildung: irgendwie passen diese Elemente nicht so recht zusammen. In der Regel werden daher mehrere Tabellen angelegt, die diese Darstellung vereinfachen (z.B. Bundeslaender, Bezirke). Diesen Vorgang nennt man „Normalisierung“.

Was passiert nun, wenn wir eine weitere Ebene einziehen wollen? Im Beispiel wären dies Kreisstädte und Landkreise. Dafür könnte man zwei weitere Tabellen angelegt werden.

Dieses Spiel lässt sich beliebig oft wiederholen, indem man zum Beispiel noch Stadtteile und Gemeinden abbildet.

An dieser Stelle haben wir nun eine Hierarchie von politischen Gebieten, das wie folgt aussieht:


Zur Erläuterung: Kreisstädte haben entweder keinen Landkreis oder sind der Verwaltungssitz eines Landkreises.


Die Sinnhaftigkeit dieses Modells möchte ich nun dahingehend prüfen, ob es möglich ist, bestimmte Fragestellungen zu beantworten. Das Ziel sollte sein, möglichst einfache Algorithmen definieren zu können. Die Wartbarkeit oder Anpassungsfähigkeit sollte dabei nicht eingeschränkt werden. Im einfachsten Fall sollte die Datenbank die Fragestellung mit Bordmitteln abbilden und somit beantworten können.

Vorteile des relationen Modells

  • eindeutige Tabellen, denen wir je nach Anwendungszweck auch noch weitere Eigenschaften zuordnen können
  • einfache Beziehungen: jeder spezifischere Element (z.B. eine Gemeinde) ist genau einem allgemeinerem Element (z.B. genau einem Landkreis) zugeordnet
  • Abfragen wie „Alle Bezirke von Bayern“ oder „Alle Gemeinden im Landkreis Traunstein“ sind ohne großen Aufwand möglich

Nachteile des relationalen Modells

Komplexität entsteht in diesem Modell, wenn man über die Hierarchie suchen muss. Hier sind ein paar Beispiele:

  • die Suche eines beliebigen Elements der Hierarchie nach seinem Namen (z.B. „Tutzing“).
  • Die Hierarchie dieses Elements soll ausgegeben werden können.

Im konkreten Fall könnte man am einfachsten jede einzelne Tabelle abfragen. Dies wären sechs Abfragen.

Über den Fundort des Ergebnisses, wäre es dann wiederum möglich, die Hierarchie abzubilden. Dies bedeutet bei Ergebnissen auf Gemeinde- oder Stadtteil-Ebene eine Verknüpfung mit drei weiteren Tabellen.

Dies sind nun die Nachteile:

  • mehrere Requests, die programmatisch ausgewertet werden müssen
  • Aufbau von (teuren) Verknüpfungen über mehrere Tabellen
  • kontext-abhängige Abfragen

Für das o.g. Beispiel „Tutzing“ wäre die Hierarchie wie folgt: „Gemeinde Tutzing“, „Landkreis Starnberg“, „Bezirk Oberbayern“, „Bundesland Bayern“

Ergebnis und Ausblick

  • die Abbildung von hierarchischen Strukturen in Tabellen ist möglich
  • einfache Anfragen auf einer oder zwei Ebenen sind einfach zu realisieren
  • Suchen auf einer Ebene und damit auf einer Tabelle sind einfach
  • Suchen entlang der Hierarchie sind komplex und schwer abzubilden
  • die Abbildung komplexerer Fragestellungen stößt schnell an die Grenzen der Sprachmittel von relationalen Datenbanken

Zur Abbildung von hierarchischen Datenstrukturen ist das relationale Datenmodell somit nur bedingt geeignet.

Ausblick: eine gute Alternative bieten hier Graphen-Datenbanken. Es lohnt sich ein Blick auf Datenbanken wie Neo4j oder OrientDB.