{"id":6909,"date":"2020-08-01T13:19:48","date_gmt":"2020-08-01T11:19:48","guid":{"rendered":"https:\/\/u-labs.de\/portal\/?p=6909"},"modified":"2020-08-01T13:20:40","modified_gmt":"2020-08-01T11:20:40","slug":"couchdb-datensaetze-zu-postgre-migrieren-nosql-zu-sql","status":"publish","type":"post","link":"https:\/\/u-labs.de\/portal\/couchdb-datensaetze-zu-postgre-migrieren-nosql-zu-sql\/","title":{"rendered":"CouchDB Datens\u00e4tze zu Postgres migrieren (NoSQL zu SQL)"},"content":{"rendered":"<p>Folgender Artikel stellt ein kleines Skript vor, welches kleine bis mittelgro\u00dfe CouchDB NoSQL-Datenbanken zu einer PostgreSQL-Datenbank migriert. <\/p>\n<h2 class=\"wp-block-heading\">Motivation: Kompatibilit\u00e4t und Ressourcenverbrauch<\/h2>\n<p>Ich entschied mich f\u00fcr ein kleines Projekt zur Speicherung von Sensordaten f\u00fcr CouchDB. Prim\u00e4r ging es darum, CouchDB kennen zu lernen. Dar\u00fcber hinaus erschien mir die Datenbank schlang genug f\u00fcr einen Raspberry Pi. Wobei das auf andere Datenbanken auch zutrifft, im Nachhinein betrachtet sogar noch besser. Die Entscheidung beruhte aber nicht prim\u00e4r auf technischen Grundlagen. Vielmehr dachte ich mir: F\u00fcr ein kleines Bastelprojekt bietet es sich doch gerade zu an, mal etwas neues auszuprobieren. Schauen wir mal, wie das damit funktioniert. Schlie\u00dflich geht es nicht nur ums Ergebnis, sondern auch um den Weg.<\/p>\n<p>Im Zuge des Projektes habe ich die Grundlagen von CouchDB kennen gelernt. Musste aber auch feststellen, dass diese Datenbank f\u00fcr mein Vorhaben aus zwei Gr\u00fcnden nicht optimal geeignet ist. <\/p>\n<h3 class=\"wp-block-heading\">Kompatibilit\u00e4t mit Grafana<\/h3>\n<p>Anf\u00e4nglich habe ich die Auswertungen der Daten noch komplett h\u00e4ndisch durchgef\u00fchrt. Wichtig war somit nur, serverseitig z.B. mit Python und  einer Bibliothek auf die Daten zugreifen zu k\u00f6nnen. Das ist mit so ziemlich jeder g\u00e4ngigen Datenbank und Programmiersprache kein Problem. Das habe ich mit einem anderen RPI-Projekt \u00e4hnlich gehandhabt. Der Vorteil ist maximale Flexibilit\u00e4t, da alles angepasst werden kann. Bei anderen Projekten kam zudem noch eine Verwaltungsoberfl\u00e4che dazu. Somit integrierte sich die Auswertung in zus\u00e4tzliche Funktionalit\u00e4ten.<\/p>\n<p>Als ich mir Grafana etwas n\u00e4her anschaute, stellte ich die Entscheidung f\u00fcr dieses Projekt infrage. Schlie\u00dflich bot Grafana s\u00e4mtliche M\u00f6glichkeiten der Visualisierung, sogar noch weitere dar\u00fcber hinaus. Da keine zus\u00e4tzlichen Funktionen geplant sind, schien es mir eine gute Idee, hierf\u00fcr Grafana auszuprobieren. Schlie\u00dflich gilt das Tool in Kombination mit Prometheus als Standard-OS Werkzeug f\u00fcrs Monitorring. Es steht daher auch schon l\u00e4nger auf meiner Liste an auszuprobierenden interessanten Projekten. Mit meinen Sensoren hatte ich einen idealen Anwendungsfall, der mit ein paar einfachen Diagrammen \u00fcberschaubar zum Einstieg geeignet scheint.<\/p>\n<p>Allerdings gab es einen Haken: Grafana unterst\u00fctzt viele Datenquellen, jedoch bisher nicht MongoDB. SQL-Datenbanken wie MySQL oder Postgres k\u00f6nnen dagegen direkt angesteuert werden. Der einfachste Weg schien mir daher, auf Postgres zu wechseln. So hatte ich zwar wieder eine SQL-Datenbank. Aber eine aufstrebende, auf die ich unabh\u00e4ngig davon auch gerne mal einen Blick werfen wollte.<\/p>\n<h3 class=\"wp-block-heading\">Performance<\/h3>\n<p>Hinsichtlich der Datenbankabfragen w\u00fcrde sich grunds\u00e4tzlich kein nennenswerter Vorteil bei NoSQL-Datenbanken geben. Dazu ist die Datenbankstruktur nicht komplex genug. F\u00fcr ein produktives System w\u00e4re das im Rahmen der technisch begr\u00fcndeten Entscheidung ein Hauptargument, grunds\u00e4tzlich auf NoSQL statt SQL zu setzen.<\/p>\n<p>Da ich sowohl mit CouchDB als auch Postgre gearbeitet habe, konnte ich mir ein erstes Bild vom Ressourcenverbrauch der Datenbanken selbst machen. Auf einem Raspberry Pi mit begrenzten Ressourcen ist das ein durchaus wichtiges Kriterium. Zumal es sich um ein \u00e4lteres Modell 3 handelte. Bei den neueren 4ern mit 2 GB, 4 GB oder sogar 8 GB Arbeitsspeicher kann das wieder etwas in den Hintergrund r\u00fccken. <\/p>\n<p>Im Dauerbetrieb ist mir aufgefallen, dass CouchDB einen deutlich h\u00f6heren Arbeitsspeicherverbrauch aufweist als Postgres. Obwohl Postgres zu diesem Zeitpunkt etwas mehr unter Last stand. Beide Datenbanken wurden per Cronjob st\u00fcndlich mit einem Datensatz bef\u00fcllt. Postgres war zus\u00e4tzlich lesenden Anfragen durch Grafana ausgesetzt &#8211; mangels direkter Einbindungsm\u00f6glichkeit fanden diese bei CouchDB nicht statt.<\/p>\n<p>Der Verbrauch von CouchDB war hierbei nahezu um den <strong>Faktor 8<\/strong> h\u00f6her, wie man an folgendem Beispiel sieht:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"\" data-line=\"\">postgres_1      0.04%               7.34MiB \/ 926.1MiB    0.79%               1.97MB \/ 1.82MB     0B \/ 0B             11\ncouchdb_1       3.03%               55.04MiB \/ 256MiB     21.50%              4.45MB \/ 3.65MB     0B \/ 0B             62\n<\/code><\/pre>\n<p>Hier darf man sich nicht von den Limits irritieren lassen. F\u00fcr Postgres ist aktuell noch keines gesetzt, CouchDB besitzt dagegen eine Grenze von maximal 256 MB. Relevant ist daher der effektive Verbrauch von <strong>7,3 MB<\/strong> bei Postgres und satten <strong>55 MB<\/strong> durch CouchDB.<\/p>\n<p>Fairer weise muss man dazu sagen, dass selbst CouchDB nur knapp 6% des zur Verf\u00fcgung stehenden Arbeitsspeichers nutzt. In diesem Falle bleibt somit genug Luft nach oben. F\u00fcr mich war das nicht der entscheidende Grund, da es prim\u00e4r um die Anbindung an Grafana ging. Dennoch fand ich den Unterschied interessant. Auf einem Desktop-PC oder Server ist das zwar irrelevant, dort wird man es nicht mal merken. F\u00fcr einen \u00e4lteren RPI mit weniger RAM sieht es dagegen schon anders aus. Hier m\u00f6chte man die Differenz vielleicht lieber f\u00fcr andere Anwendungen nutzen, oder zumindest als Puffer frei halten.<\/p>\n<h2 class=\"wp-block-heading\">CouchDB zu Postgres mit Python3 migrieren<\/h2>\n<p>Da ich an dem Projekt nebenbei arbeite und mit dem Sammeln der Sensordaten begonnen hatte, befanden sich in der CouchDB bereits einige Messwerte. Darunter auch von k\u00fchleren Tagen, die sich bei den aktuellen Temperaturen nicht so schnell wieder sammeln lassen. Zwar keine essenziellen Daten, da ich diese auch sp\u00e4ter mit etwas Zeit erneut sammeln k\u00f6nnte. Aber da ich schon beide Datenbanken aus Python heraus ansteuerbar sind und es nur um wenige hundert Datens\u00e4tze geht, kann ein kleines Migrationsskript nicht viel Aufwand sein.<\/p>\n<h3 class=\"wp-block-heading\">Erstellen einer CouchDB-View<\/h3>\n<p>Um die Daten abzufragen, wird in CouchDB eine View ben\u00f6tigt. S\u00e4mtliche Details der Abfrage sind darin festgelegt. Sie ist am ehesten mit einer SQL-Prozedur vergleichbar. Allerdings mit einem Unterschied: Bei SQL hat man die Wahl, ob man die vollst\u00e4ndige Abfrage clientseitig erstellen und \u00fcbermitteln m\u00f6chte &#8211; oder auf eine Prozedur zur\u00fcckgreifen. Letzteres macht vor allem bei komplexen oder h\u00e4ufig genutzten Abfragen Sinn. Bei komplexen Abfragen entsteht weniger Datenverkehr zum SQL-Server. H\u00e4ufig verwendete k\u00f6nnen auf Seiten des Servers zentral bereitgestellt werden.<\/p>\n<p>Jedenfalls gibt es bei CouchDB keine Wahl. Auch f\u00fcr eine einfache <strong>SELECT * FROM xyz<\/strong> Abfrage muss daher eine extra View erstellt werden. Hier reicht eine einfache Map-Funktion, die das gesamte Dokument zur\u00fcckliefert:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-javascript\" data-line=\"\">function(doc) { emit(doc.id, doc); }<\/code><\/pre>\n<p>Wer nicht extra zur Datenbank m\u00f6chte, kann die View auch in Python anlegen:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\" data-line=\"\">_doc = {\n   &quot;_id&quot;: &quot;_design\/testing&quot;,\n   &quot;views&quot;: {\n       &quot;names&quot;: {\n           &quot;map&quot;: &quot;function(doc) { emit(doc.id, doc); }&quot;\n       }\n   }\n}\n\ndoc = db.save(_doc)<\/code><\/pre>\n<h3 class=\"wp-block-heading\">Datenbanktreiber f\u00fcr CouchDB und Postgres installieren<\/h3>\n<p>F\u00fcr CouchDB nutzen wir pycouchdb:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-bash\" data-line=\"\">pip3 install pycouchdb<\/code><\/pre>\n<p>Im Falle von Postgres stellt psycopg2 eine aktiv weiterentwickelte Bibliothek bereit. Sie ben\u00f6tigt zwei Pakete als Abh\u00e4ngigkeiten. Unter Debian\/Ubuntu k\u00f6nnen diese wie folgt installiert werden:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-bash\" data-line=\"\">sudo apt install python3-dev libpq-dev<\/code><\/pre>\n<p>Anschlie\u00dfend folgt auch hier das dazugeh\u00f6rige Pip-Paket:<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-bash\" data-line=\"\">pip3 install psycopg2<\/code><\/pre>\n<h3 class=\"wp-block-heading\">Python-Skript f\u00fcr die Migration<\/h3>\n<p>Sind alle Abh\u00e4ngigkeiten installiert, k\u00f6nnen wir mit dem Kern der Migration beginnen. Der Aufbau des Skriptes ist einfach: Es l\u00e4dt alle Datens\u00e4tze aus der alten CouchDB. Anschlie\u00dfend wird f\u00fcr jeden Datensatz eine SQL-Abfrage erzeugt, womit wir das JSON-Dokument auf die neue Tabelle mappen.<\/p>\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-python\" data-line=\"\">import pycouchdb\nimport psycopg2\nfrom datetime import datetime\nfrom typing import NamedTuple\nfrom collections import OrderedDict\nfrom chirp_modbus import SoilMoistureSensor\n\nserver = pycouchdb.Server(&quot;http:\/\/127.0.0.1:5984\/&quot;)\n\ndbName = &quot;sensors&quot;\ndb = server.database(dbName)\n\n# Zuvor erstellte Ansicht\nres = list(db.query(&quot;testing\/names&quot;))\n\nconn = psycopg2.connect(host=&quot;127.0.0.1&quot;,database=&quot;pms&quot;, user=&quot;postgres&quot;, password=&quot;xxx&quot;)\ncur = conn.cursor()\n\nfor doc in res:\n  row = doc[&#039;value&#039;]\n  print(row)\n\n  sql = &quot;&quot;&quot;\n    insert into sensors(soilMoisture, temp, dateTime)\n    values(%s, %s, %s)\n  &quot;&quot;&quot;\n  cur.execute(sql, (row[&#039;soilMoisture&#039;], row[&#039;temp&#039;], row[&#039;dateTime&#039;]))\n  conn.commit()\n\ncur.close()\nconn.close()\n<\/code><\/pre>\n<p>Die Tabellen\/Spalten sind nat\u00fcrlich auf den jeweiligen Einzelfall anzupassen. Dar\u00fcber hinaus ist diese recht einfache Vorgehensweise nicht f\u00fcr gro\u00dfe Datens\u00e4tze geeignet: Sie l\u00e4dt alle Datens\u00e4tze in den RAM. Wer viele bzw. sehr gro\u00dfe Dokumente migrieren m\u00f6chte, sollte je nach zur Verf\u00fcgung stehender Hardware ggf. lieber mit einem Puffer arbeiten. Statt der gesamten Datenbank wird so Schritt-f\u00fcr-Schritt immer nur eine bestimmte Anzahl an Daten geladen und migriert, bis alles \u00fcbertragen wurde. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Folgender Artikel stellt ein kleines Skript vor, welches kleine bis mittelgro\u00dfe CouchDB NoSQL-Datenbanken zu einer PostgreSQL-Datenbank migriert. Motivation: Kompatibilit\u00e4t und Ressourcenverbrauch Ich entschied mich f\u00fcr ein kleines Projekt zur Speicherung von Sensordaten f\u00fcr CouchDB. Prim\u00e4r ging es darum, CouchDB kennen zu lernen. Dar\u00fcber hinaus erschien mir die Datenbank schlang genug f\u00fcr einen Raspberry Pi. Wobei &#8230;<\/p>\n","protected":false},"author":5,"featured_media":6911,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[74],"tags":[770,823,824,639],"class_list":["post-6909","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-linux","tag-couchdb","tag-postgres","tag-postgresql","tag-raspberry-pi"],"_links":{"self":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/6909","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/comments?post=6909"}],"version-history":[{"count":2,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/6909\/revisions"}],"predecessor-version":[{"id":6912,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/posts\/6909\/revisions\/6912"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/media\/6911"}],"wp:attachment":[{"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/media?parent=6909"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/categories?post=6909"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/u-labs.de\/portal\/wp-json\/wp\/v2\/tags?post=6909"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}