[Case Locomizer] So beschleunigen Sie die Berechnung einer Heatmap in zweieinhalb Jahren um das 20.000-fache

Dieser Artikel ist eine Fortsetzung der Case Locomizer-Reihe, siehe auch


Guten Tag.

FDC: TC, EMR, IDEA

Wissen Sie, was eine Obduktion ist? Dies ist eine Geschichte darüber, wie wir zu einem solchen Leben gekommen sind.

Ich bin mir nicht sicher, aber ich lese verdammt gerne Geschichten über den Prozess der Entwicklung von hochspezialisierter oder einfacher Software. Kollegen haben vielleicht eine interessante Idee, mit der sie arbeiten können, und es ist immer wieder neugierig zu verfolgen, was mit dem Programm vom Prototyp bis zum ausgereiften Produkt passiert ist, das in einem unbekannten Fachgebiet etwas Magie ausübt.

Darüber hinaus ist es unwahrscheinlich, dass jemand eine Ahnung davon bekommt, was es ist und warum und für welche Aufgaben es nützlich sein kann, wenn ich nur einen Link zu einem Repository mit einer solchen Software lege. Auch wenn ich drei Dutzend Seiten mit Anleitungen für den Einstieg aus dem Englischen übersetze. Trotzdem ist das Spark- Framework nicht nur ein weiteres Handwerk auf dem Winkel, es muss verstanden werden, dass die Autoren geraucht haben, warum es so geschrieben wurde und nicht anders.

Dieser Artikel ist eine historische Einführung in One Ring. Es gibt keinen Code und die Geschichte ist populärer als wissenschaftlich. Aber nur über die Entwicklung und über nichts anderes als zweieinhalb Jahre Entwicklung.

Beim letzten Mal habe ich ausführlich genug über die Schwierigkeiten beim Extrahieren von Daten aus anonymisierten Datensätzen in der Mittelspur gesprochen und am Ende nicht schwache Intrigen entdeckt. Lassen wir die Lösung ein letztes Mal, und wir werden heute über den langen und schwierigen Weg zur Perfektion unseres Hauptwerkzeugs sprechen:

  • Big Data ist groß
  • Unser Fall ist nicht Standard
  • Prototyp in C # und PostGIS
  • Erste Annäherung an Hadoop MapReduce
  • Das Aufkommen von CI und Spark
  • Dritte Annäherung bei GeoSpark
  • Japanische Analysten und Migration von Azure nach AWS
  • Asche Nazg Durbatuluk, Asche Nazg Gimbatul, Asche Nazg Trakatuluk, Ag Burzum Ishi Krimpatul !!
  • Optimierung und Geokatarsis mit Uber H3
  • Ganz in Weiß

Big Data ist groß


Bei Big Data geht es nicht um Größe.

In einem monatlichen Datensatz in der Region Greater London kann es Dutzende oder sogar Hunderte Millionen Datensätze geben, aber das ist nicht viel. Eine einzelne Iteration von Anfang bis Ende hängt von der Geschwindigkeit des linearen Lesens von der Platte ab. Wenn es sich bei dem Laufwerk um eine SSD handelt, dauert es einige Sekunden.

(Ich erinnere Sie daran, dass es sich bei dem fraglichen Datensatz um eine Reihe von CSV-Dateien mit einer Reihe von Feldern handelt, die für den Anbieter spezifisch sind. Die Gruppierung von Datensätzen mit den Koordinaten anonymer Benutzer in einer Datei erfolgt entlang der Grenzen des Verwaltungsgebiets des Landes, der Präfektur oder der Stadt. Die Dateien selbst werden auf erstellt Das ausgewählte Datum, täglich oder monatlich. Weitere Details sind im vorherigen Teil beschrieben. Führen Sie es diagonal aus, wenn nicht genügend Kontext vorhanden ist.)

Unser Prozess ist mehrstufig. Anfängliche Heuristiken zur Anreicherung von Rohdaten, die nur in einem einzigen Iterationsmodus ausgeführt werden, sind schnell, und Sie können sie zumindest in Python, zumindest in C ++ und sogar in PHP schreiben. Selbst auf einer schwachen Maschine ist die Verarbeitung schnell.

Befindet sich das Dataset irgendwo in der Cloud, ist es, sofern sich der Handler in derselben Cloud befindet, kein besonderes Problem, dorthin zu gelangen, zu gehen und das Ergebnis daneben zu speichern. Darüber hinaus ist die Datei in der Regel bereits vorhanden, da Datenanbieter das Archiv mit großer Freude selbst in Ihren Cloud-Speicher hochladen, wodurch Sie einen Download-Link erhalten. Es bleibt nur die Bereitstellung der virtuellen Maschine, und alle Bibliotheken für den Zugriff auf das Repository werden vom Hersteller sorgfältig darauf gespeichert. Alle Zugriffsschlüssel sind registriert. Nehmen Sie einfach die API in die Hand und verwenden Sie sie. Es wird auch schnell gehen.

Nun, mit den ersten Schritten ist alles klar. Sie nahmen die Datei, durchliefen sie mehrmals und setzten die verarbeitete Version zurück. Aber was passiert, wenn die nachfolgenden Schritte unseres Algorithmus einige etwas komplexere Berechnungen für jeden Datensatz erfordern?

Nehmen Sie so etwas wie die Bestimmung der Entfernung zwischen einem Koordinatenpaar. Es gibt eine extrem schnelle Haversine- Methode („Haversinuses“ gemäß der Version der Halle), die auf kurze Distanz eine akzeptable Genauigkeit liefert und es erlaubt, das WGS84- Geoid nicht zu nehmen, dessen Berechnung viel langsamer abläuft.

An sich kostet eine solche Berechnung nicht so viel, wenn sie einmalig ist. Und selbst wenn es Dutzende von Millionen von ihnen gibt, ist dies im Prinzip Unsinn.

Und jetzt nehmen wir den Fall von unserem patentierten Algorithmus, bei dem wir die Entfernung von jedem Signal zu jedem POI aus der ausgewählten Kategorie berechnen und diejenigen verwerfen müssen, die weiter als einen halben Kilometer entfernt sind (eine solche Entfernung, die leicht zu gehen ist).

In der Region Greater London fallen ungefähr eine Million Einrichtungen in die Ziel- POIs der Kategorie Stores and Outlets. Und wie gesagt, im monatlichen Datensatz kommen Dutzende von Hunderten von Millionen Datensätzen für ihn. Und so bekommen wir ...

1.000.000 POIs × N ,.000.000 Signale = N ,.000.000.000.000 Entfernungen.


Oh, komm schon. Verrückte Billionen von Entfernungsberechnungen und Vergleichen von Schwellenkonstanten.

Die klassische Situation mit dem kartesischen Produkt . Zwei nicht sehr leistungsstarke Sets ergeben leicht N × 10 12 Zwischenergebnisse, und dies ist nur ein Monat in einer Region! Aus einer solchen Menge wird bereits Qualität. Nicht nur die Größe des Zwischenergebnisses ist bereits ein ernstes Problem, da es nicht vollständig in den Speicher passt und sofort am Empfangsort verarbeitet werden muss, sondern auch der Rechenaufwand, der erforderlich ist, um es zu erhalten, nimmt zu viel Computerzeit in Anspruch. Und wenn für einen Datensatz unter Berücksichtigung aller Verzögerungen bei der Übertragung über das Netzwerk und anderer Gemeinkosten nur 100 Nanosekunden aufgewendet werden, sind Millionen von Sekunden Tage und Wochen von Berechnungen in einem Datenstrom.

Oder, wenn wir zum Beispiel ein Segment aus der allgemeinen Bevölkerung herauswerfen müssen, die Bedingung "die Interessen von Benutzern, die in einem bestimmten Gebiet leben, nicht berücksichtigen", dann müssen wir die device_id jedes Datensatzes des angereicherten Datensatzes der gesamten Region mit einem Satz vergleichen, in dem Hunderttausende Datensätze mit enthalten sind ausgeschlossen device_id Bewohner dieses Gebiets. Und dies sind in vielerlei Hinsicht Zeichenfolgenvergleiche, nicht so schnell wie für zwei Zoll. Wieder gibt es eine verrückte Anzahl von Nullen bei der Bewertung einer einfachen Operation, und wir haben sie für einen vollständigen Satz von Heuristiken für ein durchschnittliches Projekt mit einem Dutzend oder sogar mehr.
Big Data sind Daten, die aufgrund ihrer Größe aufgrund der Unangemessenheit oder Unpraktikabilität der direkten Verarbeitung spezielle algorithmische Techniken erfordern.

... auch wenn das Endergebnis der Berechnung in einem Bildschirm der Excel-Tabelle zusammenfällt.

Sie können versuchen, den "naiven" Handler anhand der Anzahl der verfügbaren virtuellen Prozessoren auf der Maschine, auf der die Berechnung ausgeführt wird, zu parallelisieren. Sie können den Datensatz in Teile teilen und die Berechnung der Strasssteine ​​auf einem Dutzend virtueller Maschinen in der Cloud ausführen. All dies führt jedoch nicht zu einem qualitativ hervorragenden Ergebnis. Die Skalierung "in der Breite" führt ab einer bestimmten Breite zu abnehmenden Erträgen . Und das Problem der Synchronisierung und Partitionierung wird mit Sicherheit auftauchen, und die Verwaltung einer ganzen Flotte virtueller Maschinen wird Zeit und Geld kosten. Es ist teuer, sie die ganze Zeit eingeschaltet zu halten, und das Starten und Stoppen bei Bedarf ist arbeitsintensiv.

Daher werden für Big Data spezielle Softwaresysteme aus dem Hadoop-Ökosystem verwendet, die bereits über Skalensteuerungen verfügen, sowie spezielle Algorithmen, die es dem Mammut ermöglichen, in kleinen Portionen zu essen, ohne die astronomische Menge von Zwischendaten zu verschlucken, und die das Leben eines Big-Data-Entwicklers erheblich vereinfachen. Sie können Hadoop jedoch nicht einfach verwenden. Zuerst müssen Sie einen Plan erstellen.

Besonders wenn ...

Unser Fall ist nicht Standard


Wenn Sie sich fragen, wie die an der Analyse großer Datenmengen beteiligten Stellen ihre Prozesse aufbauen, stellt sich heraus, dass in der Weltpraxis zwei Hauptansätze verwendet werden.

Ansatz Nummer 1. Data Lake


Für Daten, die sich im Laufe der Zeit ansammeln und für immer relevant bleiben, ist eine spezielle Art der Speicherung vorgesehen, der sogenannte " Data Lake ".

Die Architektur solcher Repositories ist für den schnellen Direktzugriff optimiert. Viele der gesammelten Datensätze werden in ein spezielles Format übersetzt, mit dem Sie schnell mehrere Kriterien und Segmente nach Spaltensätzen auswählen können. Im Gegensatz zu herkömmlichen relationalen und dokumentenorientierten Datenbanken wird die Spaltenspeicherung in Data Lakes verwendet. Normalerweise sind sie endgültig, das heißt, das Format der Container mit den Daten ist so, dass sich nach dem Füllen und Indizieren der Daten im selben Datensatz nie mehr ändert. Zum Beispiel Parkettdateien, die nicht modifiziert werden müssen.

Danach drängen sich eine Menge Datensatanisten oder Datenanalysten, und in Spezialsoftware („Laptops“ wie Jupyter) werden Statistiken, Indikatoren usw. gesammelt. online. Diese Statistiken werden irgendwo nach außen aus dem See geladen oder einfach in Form derselben endgültigen Dateien für die nachfolgende Aggregation addiert.

Ansatz Nummer 2. Daten-Streaming


Für Daten, die in Echtzeit eingehen und schnell verarbeitet werden müssen (dh Daten-Streaming), sind Datenbusse oder Nachrichtenwarteschlangen vorgesehen.

In einer Infrastruktur mit einem Datenbus gibt es an einem Ende Generatoren und am anderen Ende Verbraucher, und die Datenströme selbst setzen sich aus Ereignissen zusammen.

Generatoren werden generiert, und Verbraucher analysieren Ereignisse in Echtzeit oder nahezu in Echtzeit, wobei sie einige Endergebnisse ansammeln, die wiederum Ereignisse generieren können, die die nächste Gruppe von Aggregatoren über denselben Bus verbraucht, und so weiter, bis das Endergebnis erhalten wird. im Endergebnisspeicher gefaltet.

Es wird von Apache Kafka und schnellem Speicher wie Aerospike angetrieben.

Unser Fall


Unser Fall passt jedoch nicht in diese beiden Ansätze.

Erstens macht es für uns keinen Sinn, einen Datensee zu führen, da der Datensatz selten länger als ein Jahr dauert (Benutzerspuren für 2016 im Jahr 2019 werden von niemandem mehr benötigt) und jedes Mal, wenn Kunden einen völlig unvorhersehbaren Teil aller gesammelten Daten benötigen. Aufgrund der Tatsache, dass für jedes Bevölkerungssegment und jede Kategorie eine eigene Vorlage erstellt wird, sind wir weiterhin gezwungen, nur das erforderliche Teil zu nehmen, und das Zusammenführen zu einem gemeinsamen See macht wenig Sinn. Es ist einfacher, jeden Monatsdatensatz in seiner ursprünglichen Form zu halten - CSV-Dateien in einem eigenen Verzeichnis. Der Pfad zur Datei wird abgerufen ... / provider / country / region / subregion / year / month / dataset-Dateien, und eine Teilmenge wird einfach über die Dateinamenmaske ausgewählt, z. B. ... / Tamoco / UK / Greater_London / * / 2019 / {6, 7,8} / *. Csv.

Zweitens ist die Art der Datensätze diskret und kein Streaming. Natürlich könnte man einige Indikatoren direkt beim Hochladen in den Netzwerkspeicher berechnen, aber die fertigen Wärmekarten für die Moskauer Region und die Nachbarregion der Moskauer Region stimmen nicht mit der fertigen Wärmekarte der kombinierten Region Moskau und Region überein ( aufgrund der Tatsache, dass zu viele in der Region leben und in Moskau arbeiten, und wir immer noch nicht im Voraus wissen, welche Region wir brauchen werden. Vielleicht weder Moskau noch die Region Moskau, sondern nur eine Stadt 17. Es ist sehr teuer, Heuristiken zu fahren und Indikatoren für alle Datensätze zu berechnen.

Daher müssen wir schnell eine Teilmenge der akkumulierten Datensätze auswählen, schnell eine für die Stromversorgung geeignete Computerfarm bereitstellen, schnell einen eindeutigen, aber standardisierten Berechnungsprozess ausführen, das Ergebnis ausspucken und ... vielleicht nie wieder zu einer Teilmenge oder einer Farm dieser Größe zurückkehren , nicht an die Vorlage. Und wir können absolut keinen gut abgestimmten Leistungscluster auf unserer eigenen Hardware halten, der die Anforderungen aller unserer Projekte vom kleinsten bis zum schwierigsten abdeckt, da sie zu unterschiedlich sind.

Ich denke nicht, dass wir so einzigartig sind. In Gesprächen mit Kollegen taucht regelmäßig die Notwendigkeit auf, ähnliche Burst- Fälle zu instrumentieren, aber hier baut jeder den Prozess auf seine eigene Weise auf. In der Regel werden Lösungen für nicht standardisierte Fälle von den seitlichen Ansätzen Nr. 1 oder Nr. 2 an den vorhandenen Förderer angehängt. unser prozess besteht ausschließlich aus privaten projekten, wir haben alle aufgaben wie "burst".

Nun gut. Für zwei Jahre und einen Cent konnten wir ein Tool-Kit entwickeln, um meine Arbeit so weit wie möglich zu automatisieren, und genau dies werde ich im dritten Teil meiner Geschichte für den allgemeinen Gebrauch vorstellen. Sprechen wir in der Zwischenzeit über die Evolution und all die Fehler und Probleme, die wir korrigieren und lösen, die wir durch Erfahrung zu einem nachhaltigen Prozess gebracht haben.

Prototyp in C # und PostGIS


Alles begann vor ein paar Jahren. Zwei sehr kluge Typen namens Alexei Polyakov und Alexei Polyakov - lachen Sie nicht, sie sind eigentlich Namensvetter, aber aus verschiedenen Teilen der Welt - Biologe und Vermarkter - beschlossen, die Methode aus der Dissertation über das kollektive Verhalten von Zellpopulationen in Zellkulturen anzuwenden, die experimentell an Mäusen getestet wurde , Werbung und Marketing.

Es hat bei Menschen funktioniert.

Und dann kam das Locomizer-Projekt zustande. Ich sage "Projekt", weil es wie ein Startup mit einer GmbH ist , Verträge abzuschließen, aber nicht ganz. Unsere Teammitglieder sind über die ganze Welt verstreut, arbeiten an verschiedenen Orten und in verschiedenen Büros als Freiberufler oder Outsourcer (und nicht alle in Vollzeit) und wir verwenden unsere Algorithmen für sehr unterschiedliche Kunden mit unterschiedlichen Interaktionsmodellen, wenn wir Aufträge erhalten oder finden. Es gibt Abonnements, aber mehr private einmalige Aufgaben.

Aber das ist gerade jetzt. Und vor ein paar Jahren war alles noch chaotischer. Wer die erste Software-Implementierung zur Berechnung der Geschwindigkeit geschrieben hat, weiß ich generell nicht. (Wenn Sie diese unbekannten Helden plötzlich kennen, grüßen Sie sie.) Am Ende meines letzten Artikels über die Karriere eines Programmierers in einer bestimmten Stadt schrieb ich wörtlich Folgendes: „Ich bin gekommen, um mit dem Ort zu sprechen, an dem ich jetzt arbeite, und PM hat von Anfang an darauf hingewiesen Das Projekt ist höllisch. Nichts Auch hier, GIS, basieren nur Berechnungen auf MapReduce (und ich möchte es auf Spark), Karten auf ArcGIS, und all dies dreht sich in Wolken, die sich niemand ausdenken kann. Meiner Meinung nach eine großartige Option! “- in diesem Moment war es bereits so, und ich kann die allererste Stufe der Codeentwicklung eines Projekts nur aus den Memoiren von mitra_kun wiederherstellen , die selbst ein Jahr zuvor für das Projekt erschienen sind.

Die rudimentären Heuristiken für die Verarbeitung von Rohdatensätzen wurden in PHP, Python und C ++ geschrieben, und die Hauptberechnung der Geschwindigkeit für die Heatmap wurde von einem Programm in C # durchgeführt.

Das ganze Projekt in C #

Sie hat so gearbeitet:

  1. Zuerst lesen wir den String direkt aus der Dataset-Datei in das Array.
  2. Führen Sie es für sie aus, bauen Sie eine Hash-Tabelle auf polzakz.
  3. Die POI-Basis ist eine Literal-Tabelle in einer PostgreSQL-Datenbank mit PostGIS-Feldern vom Typ GEOMETRY. Um den Abstand zwischen jedem Benutzersignal und jedem POI zu berechnen, wird die Funktion ST_DISTANCE durch einen kleinen Speicher gezogen und das Ergebnis für jeden Benutzer in eine Hash-Tabelle mit einem Schlüssel eingefügt.
  4. Dann foreachen wir auf dem Tisch mit der Akkumulation des Ergebnisses der Zinsbewertung für jeden Schlüssel im Array.
  5. Gruppieren Sie noch einmal für jede Kategorie.
  6. Nach dem Ende der Berechnung, die nur ein paar Stunden bis eine Woche dauert, wird das Ergebnis zur CSV-Datei hinzugefügt ...
  7. ... und dann noch manuell verarbeitet, auf der Karte eingeblendet und in ArcGIS visualisiert.

Es ist klar, dass das maximal verarbeitete Volumen durch den auf dem Computer verfügbaren Speicher begrenzt ist und die Geschwindigkeit einzelner Abfragen an die Datenbank einen bestimmten Alarm auslöst.

Erste Annäherung an Hadoop MapReduce


Am lokalen Prototyp wurde etwas berechnet, die Angemessenheit der angewandten Methoden zur Erstellung von Datensätzen und Gebäude-Heatmaps getestet und die Frage aufgeworfen, wie die Arbeit in Betrieb genommen werden soll. Nun, es ist wichtig, den Sonnenuntergang nicht manuell zu bewältigen, sondern die Fähigkeiten einer Plattform zu nutzen, die vorzugsweise von Industriewalen geschrieben wird, und zumindest auf das Minimum zu skalieren.

Wie gesagt, die Standardplattform für die Verarbeitung großer Datenmengen ist das Hadoop-Ökosystem. Eine große Anzahl heterogener Bibliotheken, darunter ein verteiltes Dateisystem, Planer für die Parallelisierung von Aufgaben, relativ bequeme Abstraktionen über Kartenreduzierung, Engines für die Ausführung von Abfragen und sogar eine große Menge an Material für die Datenanalyse. Und all diese Software-Infrastruktur ist in Form von integrierten Paketen in den Clouds von verschiedenen Anbietern verfügbar und wird automatisiert, aber dazu später mehr.

Ok Google, suche nach Hadoop. Meine Vorgänger nahmen den Prototyp und schrieben die Hauptberechnung von C # auf Java um, wobei sie buchstäblich alle foreach durch den entsprechenden Hadup-Mapper und -Reducer ersetzten und alle Schritte zur Aufbereitung und Anreicherung von Datensätzen in separaten Dienstprogrammen in Skriptsprachen unternahmen, um sich mit dem Aufkommen von different schneller zu entwickeln Kundenalgorithmen begannen sich aktiv zu entwickeln. Separat haben wir im Frühjahr mit dem Schreiben eines Backends für die Web-Benutzeroberfläche begonnen (nicht die beste Lösung, wenn keine Java-Entwicklungserfahrung vorhanden ist, es sollte besser in PHP geschrieben werden), mit einem Frontend für Node.js mit Kartenintegration von ArcGIS.

Ein kleiner Teil eines Java-Projekts

Sie haben für diesen Fall den "großen Cluster" von Hadoop auf fünf virtuellen Maschinen in Microsoft Azure ausgelöst. Warum Azure? Erstens gibt es für Startups in den ersten Jahren einen großen Rabatt. Zweitens wurde ArcGIS Desktop für Windows zur Visualisierung von Karten bereits in dieser Cloud bereitgestellt.

Der Hadoop-Cluster wurde manuell bereitgestellt und nicht über den entsprechenden Azure HDInsight-Dienst, der schwer zu konfigurieren war.Auf jedem der Cluster-Rechner haben sie Postgre + PostGIS ausgelöst (eine eher zweifelhafte Entscheidung, da MR und die Basis beginnen, um den Prozessor zu konkurrieren), um keine Entfernungen zu einem separaten Server anzustreben. Wir haben ein kleines Skript erstellt, das Repliken der POI-Datenbank auf die Knoten des Clusters verteilt.

Das Projekt war noch ein Prototyp, nur etwas weiter fortgeschritten. PostGIS wurde immer noch verwendet, weil Geofencing auftauchte und die Jungs noch nicht wussten, wie es mit minimalem Arbeitsaufwand implementiert werden könnte. Es fühlte sich an, als sei alles furchtbar langsam, und die Anzahl der Schritte, die manuell ausgeführt werden mussten, überstieg ein Dutzendeinhalb.

In diesem Moment interessierte mich der Vorschlag einer in unserer kleinen, aber sehr IT-orientierten Stadt wenig bekannten Firma (in Ischewsk gibt es mehr als sieben Dutzend Büros mit Entwicklungsmitarbeitern, in denen etwa dreitausend Programmierer arbeiten), ein Büro mit dem absolut generischen Namen „Russische Informationstechnologien“ plötzlich, ohne Grund, brauchte es Senior Java Developer mit umfassender Erfahrung in der Bereitstellung und Automatisierung und hörte zumindest vom Ohr über Big Data und die Clouds. Nun, als ich etwas über Wolken und Big Data hörte.

Was alles andere angeht, ich habe mehr als genug Erfahrung :( Deshalb habe ich als erstes gesagt, dass ich den Code und den Status der Prozesse laut und oft in den besten Traditionen von Artemy Lebedev gesehen habe. Ich werde es nicht wiederholen.

Nun, wenn der Code und die Prozesse von verständlicher Qualität sind, dann haben sie definitiv einen Optimierungsplatz. Für den Anfang können Sie Anforderungen mindestens einzeln an PostGIS senden, jedoch stapelweise mit jeweils ca. 5000 Punkten. Datenbanken sind in der Regel gut für die Auflösung kartesischer Produkte optimiert. Es ist erledigt, der Speicher mit dem Aufruf ST_DISTANCE wurde so umgeschrieben, dass sofort ein großes Array für ein Paket von Punkten zurückgegeben wurde, und die Berechnung wurde sofort von Grund auf um das 40-fache beschleunigt, da nun nicht mehr so ​​oft eine Verbindung zur Datenbank und zu so vielen Indizes hergestellt werden musste auf Geometrie in der Tabelle mit POI begann mit großem Sinn zu arbeiten.

Richtig, ein böser esoterischer Fehler hat sich direkt in die Berechnung eingeschlichen, da der Prototyp nicht vollständig korrekt von C # nach Java portiert wurde. Die Jungs haben den Punkt einer wichtigen Variablen verpasst, und die formale TK auf dem Prototyp hat sie überhaupt nicht erreicht, irgendwo auf dem Weg verloren. Dann haben wir alle Algorithmen aus fragmentarischen Beschreibungen wiederhergestellt, aber das war schon sehr viel später. Dieser Fehler hat das Berechnungsergebnis insgesamt jedoch nicht beeinträchtigt, sondern lediglich den Kontrast der Wärmekarte verringert.

Mit MapReduce erhalten Sie jedoch nicht viel Leistung, da der Mapper Daten aus HDFS liest und zurückschreibt und der nächste Reduzierer in der Kette dasselbe tut und so weiter, bis alle Schritte abgeschlossen sind. Es ist auch sehr unpraktisch, einen mehrstufigen Prozess zu verwalten, insbesondere wenn der Algorithmus aufgrund von Einstellungen Verzweigungen aufweist. Der gesamte Algorithmus ist ein Hardcode, und wenn Sie die Schritte irgendwie neu anordnen möchten, müssen Sie sie mit Ihrem eigenen Launcher in separate Module verschieben und eine Art Logik nach außen packen.

Das Abrufen von PostGIS aus der Berechnung heraus, selbst wenn Sie die Datenbank auf jedem Knoten des Clusters duplizieren, ist immer noch eine sehr schmerzhafte Idee.

Das Aufkommen von CI und Spark


- Automatisiere es! - mein zweites großes Punkt rofessionalny Interesse nach enterprayznogo n rogrammirovaniya auf einer Kröte ... Und nein. Zweitens - es ist n itstsa, n asta und n udingi, dann lassen Sie es eine dritte sein - ist n Stop - n - Prozesse und deren Automatisierung. (I wie shef- n Ovar wie alles an haben , p . Hashtag # n echenki.)

Handarbeit birgt zu viele Gefahren. Menschen sind unzuverlässig und machen häufig Fehler, auch wenn sie das Gleiche tun. Daher ist es viel effizienter, den gesamten Projektfluss zu formalisieren und ein Skript zu schreiben, das nicht fehlschlägt, wenn das Dienstprogramm zum Kopieren des Datensatzes aus dem Langzeitspeicher in den Online-Speicher aufgerufen wird, und die Reihenfolge der Schritte nicht zu vertauschen. als weiter den Rechen laufen.

Rake Walking war nur das schwerwiegendste Problem, das zuerst gelöst werden musste. Zuerst habe ich mich in einer separaten kleinen virtuellen teamCity bereitgestelltRichten Sie die Assembly mit dem Durchlauf aller Tests so ein, dass das überprüfte Artefakt immer zur Hand ist und nicht manuell auf den Cluster geworfen werden muss. Der zweite Schritt bestand darin, einen Wrapper zu schreiben, um eine MR-Task mit dem angegebenen Datensatz und Parametersatz im Cluster direkt aus demselben TC zu starten. Dabei wurden die ursprünglichen Datensätze automatisch in den Cluster kopiert und die Ergebnisse der Berechnung im Ergebnisspeicher gespeichert.

Und der dritte Schritt, der viel Zeit in Anspruch nahm, bestand darin, die Bereitstellung des Clusters selbst zu automatisieren, seine Parameter zu optimieren und die Berechnung für ein in Azure Blob Storage integriertes Dataset zu starten. Plötzlich gab es Projekte, bei denen ein statischer Cluster von fünf virtuellen Maschinen übersehen wurde und / oder deren Datensätze nicht mit einem Speicherauszug alter Dateien auf HDFS gemischt werden sollten.

Azure HDInsight ist eigentlichHortonworks HDP (Earth Rest in Peace), und einige seiner Einstellungen werden in der API vorgenommen, und einige können nur über Ambari registriert werden. Das Bereitstellen eines Clusters kann je nach Auslastung der Cloud bis zu einer Stunde dauern, und der Optimierungszyklus, dh das Überprüfen der Auswirkungen einer Reihe von Einstellungen auf die Leistung unseres Codes, kann einen ganzen Tag dauern. Die lokale Version von HDP Sandbox in der virtuellen Maschine benötigt 11 GB RAM und stellt eine enorme Belastung für das Festplattensubsystem dar. Daher ist auch das lokale Debuggen äußerst unangenehm und die Einstellungen unterscheiden sich geringfügig von der Cloud-Version. Ich habe viel Zeit für Experimente aufgewendet, aber zumindest habe ich herausgefunden, wie das alles funktioniert und was zu tun ist, wenn die Berechnung beim nächsten OOM plötzlich in der Mitte hängt, weil es auch ziemlich unangenehm ist, die Protokolle manuell zu analysieren.

Während ich mich mit HDP beschäftigte, begann ein anderer Programmierer, die unterschiedlichen Phasen der Vorbereitung von Datensätzen auf Apache Spark zu vereinheitlichen. Spark löste das Problem des ständigen Schreibens / Lesens von Zwischendaten, die zwischen den Schritten einer Berechnung auftreten, und wurde im Allgemeinen unter Berücksichtigung aller schlechten Stellen von MR entwickelt und kann dies um ein Vielfaches sofort tun. Und Spark's fauler RDD ist eine sehr praktische Sache.

Gleichzeitig erstellte ich ein Skript für Azure-Vorlagen in PowerShell, um den Edge-Knoten für PostGIS zu konfigurieren - eine separate Thick-Nose-Instanz im Cluster mit einer Reihe von Kernen und Arbeitsspeicher, um Anforderungen zu beschleunigen dann in HDFS auf dem Cluster geladen.

So lernte die Skriptbindung, die ursprünglich davon ausgegangen war, dass sie sowohl interaktiv als auch im Batch-Modus auf TC als separater Build funktionieren würde, nach und nach, eine beliebige Kombination von Schritten auf MR, Spark und anderen Softwarepaketen auszuführen, die wir nicht aus der HDInsight-Suite, sondern verwendeten noch mit rudimentärer Parametrisierung. Das Übertragen der Build-Parameter auf ein benachbartes Repository mit einer Reihe von INI-Dateien (für jede Plattformkomponente und für jeden Prozessschritt) und das Verwalten von Prozessvorlagen in den Zweigen dieses Repositorys erwies sich jedoch als eine so praktische Praxis, dass wir sie immer noch verwenden.

Schon Fortschritte. Mit der Automatisierung einer manuellen Routine wurde die Vorbereitungszeit für die Berechnung um das Vierfache reduziert, ganz zu schweigen von menschlichen Fehlern, die viel geringer wurden. Es ist aber noch nicht der Zeitpunkt der Berechnung.

Dritte Annäherung bei GeoSpark


Es dauerte ungefähr sechs Monate. Zu diesem Zeitpunkt hatte sich allmählich eine Reihe von debuggten und getesteten Heuristiken angesammelt, bereits mit separaten Anwendungen auf Spark und ohne Skripte, und es wurden einige typische Prozessvorlagen entwickelt. Jetzt war es notwendig, sie zu optimieren.

Der zweite Programmierer, der weder in einem Team noch in einem Unternehmen Erfahrung hatte, handelte mit seinen Modulen ganz unkompliziert - nachdem er die Übertragung einer Heuristik an Spark abgeschlossen hatte, kopierte er einfach das gesamte Projekt und begann, den alten Algorithmus durch den neuen zu ersetzen. Infolgedessen stellten acht solcher parallelen Module, von denen jedes einen ähnlichen, aber leicht unterschiedlichen Parametersatz, eine ausgezeichnete Aufrufsemantik und eine Menge doppelter Dienstcodes aufwies, ein weiteres Problem dar. Je mehr Code vorhanden ist, desto mehr Zeit wird für die Unterstützung aufgewendet, insbesondere, wenn die Entwicklung in dieser Zeit nicht aufhört. Durch das ständige Kopieren und Einfügen sammelten sich unbenutzte Parameter und andere Abfälle an.

Nachdem ich mit dem Brennproblem der Automatisierung fertig war und mich mit der Konfiguration von Clustern befasst hatte, konnte ich jetzt bereits die Datenaufbereitungsmodule und die Heuristik aufnehmen. Zunächst habe ich den gesamten sich wiederholenden Code in ein separates Commons-Projekt übernommen, das als Git-Submodul eingesteckt wurde , und in den Berechnungsmodulen wurde es um ein Vielfaches weniger chaotisch. Ich habe eine Vorlage für eine typische Heuristik zusammengestellt, aus der bereits ein neues Projekt hervorgegangen ist, ohne dass Code-Teile ersetzt werden müssen und ohne unnötigen Schmutz in der Geschichte der Festschreibungen. Die Entwicklung begann schneller zu werden.

Das nächste große Problem, das besiegt werden musste, war die Logik der Berechnung der kartesischen Produkt × POI-Signale.

Nur die Stapelverarbeitung überträgt sie in die Datenbank, verringert jedoch nicht die Anzahl der Vorgänge, selbst wenn die Datenbank Indizes und die Abfrageoptimierung effektiv verwendet. Es wäre logisch, den Abstand für diejenigen Paare nicht zu berücksichtigen, bei denen er offensichtlich den von uns benötigten Schwellenwert überschreitet. Aber wie kann man Paare mit einem Abstand verwerfen, der größer als der Schwellenwert ist, ohne diesen Abstand zu berechnen?

Antwort: Partitionieren Sie sowohl Signale als auch POIs in einem geometrischen Raster.

Darüber hinaus besteht die Heatmap bereits aus einem Gitter von Polygonen. Und wenn Sie die Zellengröße dieses Gitters richtig auswählen, können wir uns für jeden POI des ausgewählten Polygons durchaus darauf beschränken, die Abstände zu den Signalen zu berechnen, die in dasselbe Polygon fallen, seine Nachbarzellen und das ist alles. Der Rest kann weggeworfen werden, er wird sicherlich außerhalb der relevanten Grenzen liegen.

Spark hat bereits ein fertiges Tool für die Arbeit mit Gittern - GeoSpark . Der zweite Programmierer fing an, es zu verwenden, und die vorläufige Operation "Ziehen des Datensatzes auf das Raster" erschien. Aber es wurde nicht viel besser, ein ernstes Problem wurde durch ein anderes ernstes Problem ersetzt.

Dies war nun das Problem der "Long Tail" -Nutzer, bei denen die Anzahl der Signale in Millionenhöhe liegt. Es gibt nicht viele von ihnen, aber wenn sie sich in der Innenstadt ansammeln, wo der POI hoch ist, und sie sich dort ansammeln, wie es das Glück wollte, dann wird es immer noch eine geben, egal wie Sie sich in der Geometrie unterteilen (zumindest Voronoi , zumindest Quadtree ) Polygone, bei denen die Anzahl der Vergleiche einen angemessenen Betrag überschreitet. Sie müssen aber auch benachbarte Polygone überprüfen, bei denen die Dichte so hoch ist.

Und wenn 99% der Partitionen mit schwach gesättigten Polygonen schnell funktionieren, hängt 1% der Spark-Workstations mit Zellen hoher Dichte weiterhin am Sieg, frisst das Gedächtnis wie bewusstlos und verdirbt alle Himbeeren. Spark versucht, alles im Auge zu behalten. Wenn die Größe der Partitionen in RDD stark schwankt, läuft die gesamte Optimierung des Speicherverbrauchs den Bach runter, da dies für die größten Partitionen erforderlich ist.

Es stellte sich heraus, dass 99% der Berechnung Hunderte Male mit geometrischer Partitionierung beschleunigt wurden und 1% des langen Schwanzes die gesamte Optimierung auf fast nichts reduzierte.

Im Allgemeinen hat der Übergang zu GeoSpark zu einer Verfünffachung geführt, allerdings nur bei der Größe von Executoren, die sehr speichereffizient waren - und dementsprechend auch bei Clustern mit teuren virtuellen Maschinen. Kurz gesagt, die geometrische Unterteilung für Geodaten mit hoher Dichte erwies sich als Sackgasse.

Und dann war da noch das Glück in der Person des analytischen Schreibtisches einer der größten japanischen Telekommunikationsunternehmen. Ein kleines Tochterunternehmen, das auf Geolokalisierungsdaten basiert, die vom Hauptunternehmen gesammelt wurden.

Japanische Analysten und Migration von Azure nach AWS


Die Japaner haben eine interessante Mentalität. Sie selbst haben es nicht eilig, aber wenn ihnen nur ein Gaijin in den Finger beißt, werden beide Hände abgeschnitten. Geben Sie niemals die genauen Daten für Japaner an! Und wenn Sie anrufen, dann nehmen Sie mindestens die dreifache Menge. Es wird ungeheuer lang und schwierig sein, die Aufgaben zu koordinieren, und nicht nur die berühmte japanische Akribie wird stören, sondern auch der Unterschied im Denken. Möglicherweise bleibt einfach keine Zeit mehr, um die endgültige Version des TOR zu implementieren.

Das Projekt zur Integration mit der "Tochter" der japanischen Telekom hätte unser Projekt beinahe zum Erliegen gebracht. Die Aussichten waren großartig, ein exklusiver Datenanbieter für den verrückten japanischen Werbemarkt zu werden, und das Geschäft ist ein bisschen ... äh, ich kann ohne Kommentar auskommen.

Erstens kein Azure. Nur AWS, nur Hardcore.

Zweitens sollte die Front an ihre Bedürfnisse angepasst werden, die sich im Laufe des Projekts ständig änderten. Marketingfachleute aus diesem Büro wollten ständig etwas, das sie selbst nicht genau kannten und das sie nicht wirklich artikulieren konnten, und es musste zehnmal pro Phase wiederholt werden, um die Berechnungslogik für die nächsten neuen Indikatoren im Handumdrehen zu ändern.

Ich entschuldige mich für die Qualität, einen Screenshot aus dem Fehlerbericht gibt es nicht mehr

Irgendwann bin ich ein bisschen durchgedreht und habe eine Reihe von „elementaren Operationen“ durchgeführt - ungefähr 15 primitive Aktionen auf RDD mit dem Aufrufen grundlegender Methoden wie Joins, Mapping, Ablegen von Standardwerten, Summieren von Spaltenwerten - und anderen derart kleinen Operationen - zu schnell Ändern Sie die Logik der Berechnungskette, als ob es sich um eine Reihe von SQL-Anweisungen handeln würde.

(In unserem Fall ist reguläres Spark-SQL nicht anwendbar, da weder eine strenge Typisierung noch eine strenge Reihe von Feldern vorhanden sind. Sie können dem Datensatz jederzeit beliebig viele zusätzliche Felder hinzufügen, die sich während des Prozessablaufs ändern Es ist zu schwierig, Metadaten unter sich ständig ändernden Bedingungen zu verschreiben.)

Die übergeordnete Aufgabe bestand darin, eine beliebige Region Japans auszuwählen und eine Heatmap für einen beliebigen Zeitraum unter Verwendung einer beliebigen Gruppe von Kategorien mit einer Vielzahl von Indikatoren für die Deponie zu erstellen. Welche Art von Indikatoren, wie man sie zählt - der Kunde selbst hat das nicht wirklich verstanden.

Der (dh kleine) Testdatensatz mit Benutzersignalen für 2016-2017, an dem wir die Technologie ausarbeiten mussten, umfasst 5 Terabyte Daten, 14.000.000.000 Datensätze. Allein in Tokio gibt es mehrere Millionen POIs und in der Region Hokkaido 1.600.000 Zellen.

Und die Karten für alle zweitausend Kategorien für jede der 47 japanischen Perfektionen sollten als "on the fly" betrachtet werden, da sie als Cloud-Service verkauft werden sollten.

Eine großartige Aufgabe, um das Gehirn zu brechen. Irgendwo drei oder vier Größenordnungen höher als unsere damaligen Fähigkeiten in Bezug auf "Berechnungsgeschwindigkeit" und "Datenvolumen".

Nachdem wir traurig geworden waren, beschlossen wir dennoch, für jede Region (den Shinto-Göttern sei Dank, die Japaner mussten die Regionen nicht vereinen) und für einen Monat eine Vorberechnung vorzunehmen, damit die Heatmap nach zuvor erstellten Scores erstellt wurde. Nicht in Echtzeit, sondern ein paar Minuten oder zehn Minuten (für das Zentrum von Tokio). Die Vorberechnung dauerte mehrere Monate mit Clustern von 25 der leistungsstärksten virtuellen Maschinen, die in der AWS-Region Tokio verfügbar sind.

Um jedoch in AWS ausgeführt zu werden, mussten Sie zuerst die Automatisierung unter der AWS-API neu schreiben. Und verschiedene Clouds bieten zwar nach außen hin ähnliche Dienste an, sind jedoch intern völlig unterschiedlich. Es ist gut, dass zu diesem Zeitpunkt PowerShell bereits die Release Candidate-Version 6 erreicht hat und Azur-Bindungsskripte für die Bereitstellung des Clusters und die Ausführung der Berechnung unter Linux TeamCity portiert und kühn ausgeführt werden können (da die Bereitstellung von Servern unter Windows in AWS eine Idee ist ) Genauer gesagt, portieren Sie nicht, sondern öffnen Sie ein vorhandenes Skript auf einem Monitor und schreiben Sie die parallele Implementierung für eine andere Cloud auf dem zweiten.

Außerdem ist AWS viel älter und daher archaischer als Azure. Außerdem ist die Architektur viel älter, und die Konfiguration der niedrigeren Infrastrukturebene erfordert viel mehr manuelle Arbeit. Und die lokale Auktion für den Verkauf von Computer-Ressourcen bereitet Kopfzerbrechen, wenn Sie möglicherweise nicht die richtige Größe für Autos zum gewünschten Preis haben und der Kunde kein Budget für die vollständige Preisberechnung zuweist.

Aber das Hadoop-Ökosystem selbst in der Amazonas-Inkarnation - EMR - kommt Vanille näher und die Arbeit damit ist einfacher als mit HDInsight. Nun, zumindest mit etwas stellte sich heraus, dass es einfacher war.

Aber nicht mit s3. Hier kam Ärger raus, wo sie nicht gewartet haben. S3 hat undokumentierte Grenzen. Zum Beispiel kann es in einem Bucket nicht mehr als ~ 11.000.000 Objekte geben, weil sie irgendwo in den Tiefen der API die Schlüssel in lexikografischer Reihenfolge für jede (jede!) Anfrage sortieren und der dafür zugewiesene Puffer einfach keine Sortierung zulässt mehr Zeilen, besonders wenn sie lang sind. Um die Berechnung zu beschleunigen, haben wir am Ende keine Partitionen zusammengeführt, und irgendwann stießen wir auf dieses Limit, wonach der Prozess einfach gestoppt wurde.

Nach Ansicht des Verstandes muss das Zusammenführen durchgeführt werden, und es gibt sogar ein Tool - das Dienstprogramm s3-dist-cp, dessen Verwendung jedoch separate Kopfschmerzen verursacht. Die Raubtiere für Außerirdische haben das Dienstprogramm mit Sicherheit geschrieben, es verhält sich so intuitiv. Und es hat einen schwerwiegenden Fehler: Unter der zusammengeführten Datei benötigen Sie so viel Speicherplatz auf HDFS, wie alle Originaldateien benötigen. Das Zusammenführen von Zehntausenden von Partitionsdateien mit einer Größe von Hunderten von Bytes bis Zehntausenden von Megabytes, verteilt auf einen Cluster von 25 Computern, wird sehr lange dauern.

Bereits mit einer Million Objekten im Bucket beginnt S3, still und leise Anfragen an ihn zu richten. Und unter möglichen Konsistenzbedingungen ist dies im Allgemeinen eine Katastrophe - Funken, ohne auf das nächste Pult zu warten, kann die vereinbarte Anzahl von Malen fallen. Es gibt eine Lösung: Verwenden Sie das EMRFS-eigene Amazon-Add-On, das jedoch zusätzlich zu DynamoDB funktioniert. Dies ist eine sehr teure Sache. Und mit eigenen Grenzen für die Anzahl der Anfragen pro Sekunde.

Kurz gesagt, wir haben uns aus Zeitgründen entschlossen, auf das statische Schema zurückzugreifen - einen permanenten Cluster auf Instanzen mit einer relativ geringen Größe bereitzustellen (wenn auch teuer, aber billiger als DynamoDB), alle Terabyte des ursprünglichen und berechneten Datensatzes in HDFS zusammenzuführen und die Karten lokal zu lesen.

Der nächste Wendepunkt war jedoch die japanische Forderung, vom erzeugten hexagonalen Gitter auf Japan Mesh umzusteigen - die Standardmethode für die geografische Unterteilung mit rechteckigen Zellen, die nur von den Koordinaten des Punkts abhängen. Eine sehr gute Sache, da Sie damit den rechenintensiven Schritt „Signale auf das Gitter ziehen“ aufgeben können.

Der Nachteil ist, dass das Japan Mesh-Netz nur für Japan und die Inselgebiete gilt, von denen es historisch behauptet, dass sie es sind, nicht jedoch für den Rest der Welt. Aber zumindest für die Japaner wurde es möglich, den langsamen GeoSpark aufzugeben und die Signale ohne Bezug auf die äußere Geometrie gleichmäßig zu verteilen. Und mit dem Weggang des "langen Schwanzes" beschleunigte sich die Rechnung sofort noch einmal um 10 Uhr.

Es ist bedauerlich, dass dies geschah, nachdem wir alle die Sechsecke herausgefunden hatten und viel Geld und Zeit umsonst ausgegeben hatten. Ein Cluster mit Terabytes vorbereiteter Datensätze musste einfach weggeworfen werden.

Und auf jeden Fall baten die Japaner noch mitten in der Arbeit darum, die gesamte Infrastruktur von einem AWS-Konto auf ein anderes zu übertragen. Und als ob Sie sich nicht um die ganze Arbeit kümmern, die Sie am Setup erledigt haben. Nun, ich konnte zum Zeitpunkt des Übergangs ein Skript für die CloudFormation-Vorlage erstellen, sodass die Migration mehr oder weniger reibungslos verlief.

Als letzte Kirsche auf dem Kuchen entschieden die Japaner schließlich, dass die Front nicht aufgibt, und sie zogen die Berechnungen auf Wunsch ihrer Kunden manuell durch. Wir danken ihnen für die Algorithmen (zum ersten Mal haben wir sie alle detailliert dokumentiert - und einige gefunden Fehler) und vorerst. Nun ... viel Glück und bis später.

Brrr Ich erinnere mich mit Entsetzen und Schaudern an dieses Projekt.

Asche Nazg Durbatuluk, Asche Nazg Gimbatul, Asche Nazg Trakatuluk, Ag Burzum Ishi Krimpatul !!


Neben der Dokumentation aller Algorithmen gab es auch allgemeine Verbesserungen.

Wir haben einen Studenten in Java Junior kennengelernt und er hat eine Reihe von geografischen Bibliotheken studiert, wodurch er es endlich geschafft hat, die richtige auszuwählen und aus der PostGIS-Umgebung zu werfen.

Frühere Versuche blieben aufgrund mangelnder Genauigkeit erfolglos. Im Umkreis von drei Kilometern machen die Haversins bereits einen merklichen Fehler für uns, und die meisten Bibliotheken, die wir von Anfang an zu entnehmen versuchten, waren in den Breitengraden nördlich von St. Petersburg mies, wodurch Löcher oder doppelte Überlappungen im Raster auftraten. Und wir Finnen sind Stammkunden, daher ist es wichtig, dass alles in ihren Breitengraden korrekt funktioniert.

Bis wir herausfanden, dass wir eine Bibliothek mit einem normalen Geoid benötigen (vorzugsweise das gleiche wie in PostGIS, WGS84), stimmten die Ergebnisse nicht mit den erwarteten Ergebnissen überein. Nach dem Wechsel zu GeographicLib wurde der Engpass in Form von Postgre-Verbindungen beseitigt und die letzte Stufe der Berechnung der Geschwindigkeit um das 40-fache beschleunigt. Golovnyak ging mit der zusätzlichen Konfiguration einer separaten RDS-Instanz unter der Basis und dem Hochladen eines Dumps mit POI davon aus, der zu den üblichen Datensätzen in S3 verschoben wurde. Vereinigung!

Zur gleichen Zeit hat derselbe Schüler genau den Fehler entdeckt und behoben, der dazu führte, dass die Karten blasser aussahen, als sie tatsächlich waren. Wenn es eine zeitlich unbegrenzte Aufgabe gibt, beneide ich die Schüler.

Ein weiterer wichtiger Punkt. Einmal, zum x-ten Mal, habe ich mir Bindungsskripte angesehen, die ein Spark-Modul nach dem anderen aufrufen, aber mit was für einem Teufel schließen wir sie kurz?

Warum sollten Zwischenergebnisse jedes Mal in S3 oder HDFS gespeichert werden, wenn die endgültige RDD des vorherigen Moduls einfach zum Eingang des nächsten Moduls in der Kette umgeleitet werden kann? Gesagt, getan, MetaRunner wurde in ein paar Stunden geschrieben. Das Vorhandensein von Commons half dabei sehr, da die Module zu diesem Zeitpunkt ziemlich standardisiert waren, zumal die Parameter der einzelnen Module bereits in derselben task.ini lagen und die Schlüsselpräfixe ihren Namen entsprachen.
Ihre Aufmerksamkeit wird mit einem Blockdiagramm einer Karte (der letzte Schritt vor der Ausgabe an die Front, aber nicht die endgültige Version), geschrieben über elementare Operationen, dargestellt:

Flussdiagramm des Heatmap-Vorbereitungsprozesses

Wenn Sie 24 zwischenzeitliche Aufrufe von HDFS loswerden, wird diese Berechnung speziell um das 50-fache beschleunigt.

Was ist jedoch, wenn Sie der Prozessvorlage eine variable Unterstützung hinzufügen, damit Sie die Datei "tasks.ini" nicht jedes Mal neu generieren müssen, wenn Sie die Parameter im Eigenschaftenspeicher ändern?

- Ash Nazg! Schrie ich. Die Kollegen sahen sich ratlos an. Ein Typ hat wegen dieser Japaner ein Dach, aber na ja, das passiert.
"Ash nazg ... burzum-ishi krimpatul", knurrte ich (es hat nicht sehr gut geklappt) und ging zu PM, um die Zusammenführung aller 15 (die Anzahl der Heuristiken und Hilfsprogramme nahm allmählich zu) der Berechnungsmodule zu einem einzigen Speicher zu besprechen.

Wenn wir die Module miteinander kurzschließen, dann schuften wir nicht mehr mit der Auskleidung aller einzelnen JARs im Klassenpfad des Funkens herum und lassen das gesamte Paket der patentierten Locomizer-Logik (und unserer Hilfsoperationen) zu einem fetten JAR zusammenbauen. Gleichzeitig und lokal kann jetzt ohne Cluster ausgeführt werden. Und was wichtig ist, die Logik zum Parsen von tasks.ini kann von PowerShell-Bindungen in Java-Code übertragen werden, wo die Variablensubstitution viel einfacher ist.

Kollegen, die über den Vorschlag nachdenken, das Projekt "Der Ring der Allmacht" zu nennen - ein Ring -, aber ein wenig gesundes Pathos wird niemals schaden.

Nachdem ich den Moment der nächsten Runde der endlosen Koordination von TK an der Front genutzt hatte, sammelte ich alle Module auf einem Haufen. Maven ist ein erweitertes Tool zum Auflösen von Abhängigkeiten in einem Projekt mit mehreren Modulen. Daher war es möglich, die letzten Teile des duplizierten Codes zu bereinigen, Versionen aller Bibliotheken zu vereinheitlichen und Erstellungsoptionen für lokale und Cloud-Umgebungen zu erstellen. Darüber hinaus bleibt jedes Modul in einem eigenen Teilprojekt, und der Autor kann ganz unabhängig daran arbeiten, ohne den Rest zu stören.

Übrigens betrachte ich einen solchen Ansatz mit der Kristallisation von Abstraktionen und der Konstruktion einer Architektur aus einer bestehenden Menge homogener Entitäten mehr als den Versuch, eine abstrakte Ebene im Voraus zu entwerfen und in bestimmte Aufgaben umzusetzen. Ohne etablierte Praktiken und Nutzungsmuster ist es nutzlos, eine Architektur zu entwerfen - alle Fälle können nicht im Voraus vorhergesehen werden, und die Optionen für das Verhalten der Benutzer des Systems können sich radikal von den Ideen des Designers unterscheiden.

Mit der einheitlichen Logik der Verarbeitung der Parameter konnte ein eindeutiges einheitliches Objektmodell für die Modulkonfiguration erstellt und die Gültigkeit und Konsistenz der Konfigurationen der Module untereinander innerhalb desselben Prozesses normal überprüft werden.Dies ist besonders wichtig bei Datensätzen im CSV-Format - die Kontrolle der Anzahl und Reihenfolge der Felder in jedem RDD-Datensatz sowie die Richtigkeit der Übertragung des Datensatzes selbst von der Ausgabe eines Moduls zur Eingabe mehrerer nachfolgender liegt vollständig auf der aufrufenden Seite. Und wenn es einen Kontrollpunkt gibt, kann das schon gut gemacht werden.
Warum gehen wir nicht höher und arbeiten mit RDD und nicht mit Datenrahmen? Aus dem gleichen Grund, dass wir kein Spark-SQL verwenden. Darüber hinaus ist die Implementierung von Spark die letzte, letzte Stufe des Codes, der mit Whitepaper beginnt, vollständig in Python debuggt und erst dann in wenigen Schritten auf die produktivste Version optimiert wird. Und je näher an den Primitiven der Basisbibliothek, desto schneller wird der Code normalerweise ausgeführt.

... wenn die Hände des Entwicklers aus seinen Schultern wachsen und sein Kopf hell ist. Theoretisch.

Es stellt sich heraus, dass es unter unseren Bedingungen viel einfacher ist, die Zeile der ursprünglichen CSV-Datei in Form eines kompakten nativen Hadoup-Texts (unter der Haube handelt es sich nur um ein Array von Bytes) zu verschieben und nur die Spalten zu beschreiben, die der aktuelle Vorgang kennt, und zwar nur für diesen. Entsprechend den Ergebnissen von Experimenten ergeben Datenrahmen einen größeren Overhead für den Speicherverbrauch als die Notwendigkeit, CSV am Eingang jeder Operation zu analysieren und am Ausgang wieder in Text zu komprimieren. Gut und doch - es ist wichtig, dass wir die Möglichkeit behalten, zwischengeschaltete RDDs nach jedem Schritt manuell zu partitionieren, da sich neue Datasets aus dem Speicher mit ihnen vermischen können (dies ist im Diagramm deutlich sichtbar), sodass Sie immer noch eine Ebene tiefer gehen müssen, egal, wie Sie auf der Ebene bleiben möchten Logik-Whitepaper.

Aber im "Low-Level" -Code in Java gibt es auch Pluspunkte. Wenn Sie beispielsweise die Betriebsparameter (sowie die erwarteten und generierten RDDs) in den Metadaten beschreiben, können Sie automatisch sowohl Dokumentation als auch ein Konfigurationsbeispiel dafür generieren und diese nicht mehr manuell schreiben. Und die Docks werden nach jedem Build immer relevant sein.

Die Konfigurationsdatei tasks.ini selbst wurde aus einem heterogenen Satz von Parametern für jedes Modul sofort zu einem Programm in einer Art deklarativer Programmiersprache. Nicht sehr schön, aber intern logisch und relativ lesbar. Das Aufrüsten auf echtes DSL mit eigener Syntax ist kein Problem, aber ich habe es nicht als unnötig angesehen. Wenig später fügte er JSON jedoch mit einem visuellen Editor eine Ansicht für die zukünftige Front hinzu.

Ein kurzgeschlossener Prozess erhielt durchschnittlich drei- bis fünfmal schneller als eine Kette von Einzelaufrufen zu Spark-Jobs.

Nicht hundert Mal, denn jetzt können im Rahmen desselben Spark-Jobs Schritte von Aufgaben unterschiedlicher Komplexität und Datensättigung gemischt werden. Infolgedessen hat die Feinabstimmung der Clusterparameter für jeden der Teile eines mehrstufigen Prozesses keine praktische Bedeutung mehr. Allmählich und für eine solche Option wurden einige allgemeine Muster gefunden, die es ermöglichten, Voreinstellungen für die Clustergröße nur auf der Grundlage der Größe des ursprünglichen Datensatzes und der Gesamtzahl der Schritte in der Verarbeitungsprozessvorlage auszuwählen.

Um diese Phase zusammenzufassen: Am Ende unserer Arbeit mit den Japanern hatten wir bereits einige Tools entwickelt:

  • , ,
  • , , DSL ,
  • , — ,
  • AWS, .

Aber was nicht geklappt hat, war die Front. Die alte Web-Benutzeroberfläche von Locomizer ist hoffnungslos veraltet. Wir haben es nie geschafft, die neuen Japaner in einen vernünftigen Zustand zu versetzen, bevor sie ihn vollständig aufgegeben haben. Ja, und der Backend-Code dieser Benutzeroberfläche selbst, der in einer dunklen Oktobernacht mit meinem linken hinteren Fuß geschrieben wurde, konnte ich allein wegen des großen Volumens nicht bis zum Ende kämmen.

Optimierung und Geokatarsis mit Uber H3


Nach dem Ausatmen kehrten wir zu privaten Projekten zurück. Die Stimmung nach den Japanern war ehrlich gesagt, das ganze Team war sehr mittelmäßig.

Aber ich habe mich endlich der Notwendigkeit entledigt, ein Back-up an der Front mit seinen Bogomersssky, holm, holm, spring zu unterhalten. (Dies ist meine persönliche Meinung. EE gefällt mir nicht ein bisschen weniger, da es weniger Autogie und implizite Standardeinstellungen hat. Es spielt also keine Rolle, was für ein schrecklicher Mist es ist, RESTs zu schreiben.)

Es gab eine Zeit, in jedes Modul mit einer Sucht zu schauen.
— , . , , , . - . — . , — .
Nicht, dass ich mir den Code meiner Kollegen unaufmerksam angesehen hätte. Nur jeder ist mit der ihm übertragenen Aufgabe beschäftigt, und während sie von ihm mit dem gewünschten Ergebnis ausgeführt wird, darf der Entwickler bei seiner Arbeit nicht gestört werden. Wenn der Algorithmus korrekt funktioniert und dies durch Tests bestätigt wird, ist alles in Ordnung. Je nach Arbeitsgeschwindigkeit wird - es ist akzeptabel oder anders - die Entscheidung vom Ministerpräsidenten getroffen.

Ich greife erst ein, wenn ich ein hohes Risiko für weitere Unterstützung bei der Entscheidung des Entwicklers bei der Implementierung einer neuen Aufgabe erkenne. Und die alten und hässlichen Module, die unter Zar Gorokh von jemandem geschrieben wurden, der das Projekt schon lange verlassen hat, aber für das Geschäft notwendig ist, werden auf einem brauchbaren Niveau gehalten, so wie es ist, und egal, wie sehr es nach ihnen riecht. Es klingt zynisch, aber ich bin ein Pragmatiker, kein Idealist. Das Ergebnis der Arbeit ist mir wichtiger als die Schönheit des Codes.

Aber manchmal ist es notwendig, die technischen Schulden zu begleichen, damit er das Projekt nicht unter seinem eigenen Gewicht begräbt.

Spark ist eine Bibliothek auf sehr hohem Niveau. Es ermöglicht Ihnen, Operationen an RDD auf viele verschiedene Arten durchzuführen, die zum gleichen Ergebnis führen, und jede Methode kann mehrere Teile mit einigen ausgezeichneten Optionen enthalten. Sie müssen die Beschreibung der einzelnen Elemente sorgfältig lesen und im Zweifelsfall in die Quelle klettern, um zu verstehen, welches in welchem ​​Fall optimal ist. Das Ergebnis ist das gleiche, aber der Unterschied in der Geschwindigkeit seiner Berechnung kann mehrere Male sein. Wenn die Logik einer Heuristik in Spark hundert Codezeilen entfaltet, müssen Sie besonders vorsichtig sein, um die am besten geeigneten Methoden zum Transformieren der Daten zu verwenden.

Hochsprachen - sie sind so, dass Sie abstrakt denken.

Gleichzeitig sollte sich der Entwickler des niedrigen Niveaus bewusst sein, egal wie sehr er in hohe Abstraktionen aufsteigt. Beispielsweise wird jedes Lambda, das an die .map () -Methode übergeben wird, in der der Speicher für ein fett gedrucktes Objekt zugeordnet ist, für jeden Datensatz erneut aufgerufen und das gleiche Objekt neu zugeordnet, und keine der vorhandenen JVMs mag fett gedruckte wiederholte Zuordnungen.

Und wenn Sie über die Unterstützung von Code nachdenken, wäre es schön, Teile des Algorithmus zu haben, die durch interne Logik verbunden sind, aber gleichzeitig für einige Parameterwerte vollständig vom Rest des Codes isoliert sind, insbesondere wenn sich diese Teile am Anfang oder Ende des Algorithmus befinden. Sie können in der Regel in einem separaten Arbeitsgang entnommen werden, gleichzeitig werden Tests mit vollständiger Abdeckung aller Fälle kürzer.

Früher war es verfrüht, sich mit Optimierung zu befassen, aber jetzt ist es soweit, und für ein paar Monate bin ich mit meinem Kopf in den Darm von Computermodulen eingetaucht, deren Profiling-Code über zwei Jahre von meinen Kollegen geschrieben wurde.

Als ich dort getaucht bin, hatte One Ring 29 Operationen (einige Module enthalten mehr als eine). Als es herauskam - 43, und jedes schneller als das Original, von ein paar Prozent bis zehnmal. Wertvoller ist jedoch, dass die Vorgänge, die zuvor mit Daten zu Partitionen mit 10.000 Elementen unterdrückt wurden, jetzt problemlos auf Teilen in einer Million Datensätzen verarbeitet werden können. An einigen Stellen musste ich auf die Flexibilität und Lesbarkeit des Codes verzichten, an einigen Stellen kostete es einen einfachen Ersatz von .map () durch .mapPartition (), aber der Code stürzte nicht mehr ab.

Es gab nur einen Engpass - Geofencing in einer beliebigen Region. Es war immer noch eine seltsame Hybridlösung mit einem externen Netz. Es war möglich, Japan Mesh für Japan zu verwenden, aber für den Rest der Welt war es erforderlich, nach einer geeigneten Variante eines dynamischen Gitters zu suchen, die nur von den Koordinaten des Punkts abhängt und bequem zu verwenden ist.

Eine solche Option wurde gefunden - Uber H3 .

Soweit ich weiß, ist der sechseckige Baum mit dem Namen H3 verschlüsselt - und dies ist ein geografisches Raster mit großartigen Funktionen. Es ist über den gesamten Koordinatenbereich stabil, unglaublich schnell (der native Code wird genannt), gibt Zellen mit einheitlicher Größe ohne Lücken auf dem gesamten Land und ermöglicht es Ihnen, eine Reihe verschiedener Optionen zum Abdecken von Polygonen, Punkten und Pfaden zu erstellen. Außerdem hat eine hexagonale Gitterzelle eine minimale Anzahl von Nachbarn, und die nächste Ebene umfasst sieben Zellen der vorherigen, die genau über der Mitte der zugrunde liegenden Zelle liegen. Dies ist praktisch, wenn Sie Karten zusammenfassen.

Mit dem Übergang zu H3 scheint sich das Puzzle vollständig entwickelt zu haben.

Wenn wir uns mit dem vergleichen, was es zu Beginn vor 2,5 Jahren war, dann kamen wir von den Wochen, die für eine unglückliche Wärmekarte auf einem Datensatz verbracht wurden, zu ein paar Millionen Signalen zu den Minuten, die für Dutzende Karten mit Datensätzen ausgegeben wurden, deren Größe Der Datenanalyst schenkt nicht viel Aufmerksamkeit (Sie müssen meckern, wenn er die Voreinstellung für die Clustergröße zu hoch einstellt, wenn das Schreiben des Ergebnisses in S3 länger dauert als die Berechnung selbst). Und er schaut sich TC nicht mehr selbst an, sondern verstopft nur die Parametermatrix irgendwo zu Hause und zieht die erforderliche Anzahl an erforderlichen Builds mit der Python.

Fügen Sie eine neue Operation hinzu - Sie müssen nur die Operationsklasse korrekt implementieren (Sie können auch die Scala verwenden, wenn Sie möchten), sie mit Metadaten einschließen, sie in Ihre Konfiguration aufnehmen und One Ring wird dann herausfinden, ob Sie die neue Heuristik oder die Verarbeitung in der Kette korrekt aufrufen.

Nun, alles funktioniert sowohl lokal als auch in AWS. Es wird auch in einer anderen Cloud sein, wenn es S3 unterstützt, und Spark kann dort über Livy abgerufen werden - und wir haben alle anderen externen Abhängigkeiten beseitigt.

Ganz in Weiß


- Gandalf?!

Wir haben aber immer noch keine Front, um flexible Prozesse in Gang zu setzen. Und die Vorlagen solcher Prozesse selbst müssen auf altmodische Weise geschrieben werden - von Hand in VSCode, aber ich wollte eine Maus in einem Editor sein, der Visio ähnelt. So etwas in der Art: Ich habe im Rahmen von One Ring sogar einen kleinen REST-Service erstellt, der alles enthält, was Sie zum Schreiben eines solchen Editors benötigen. Das letzte Mal, als ich an der Front gearbeitet habe, war das vor etwa 10 Jahren und nicht in den Kursen der aktuellen Trends. Es ist nicht für JSF, dass ich es niete, es wird nicht einmal Retro sein, sondern schon eine Art Nekro. Es wäre schön, daraus ein statisches SPA für etwas Modernes zu machen. Nur ich habe keine Ahnung was. Mein egoistisches persönliches Interesse, One Ring- Code zu enthüllen

Mocap-Oberfläche zum Bearbeiten eines Prozesses



(Ich beende das Repository mit Inhalten, aber Sie können es jetzt ansehen.) Ich hoffe, es ist klar. Und wenn es jemanden gibt, der mutig genug ist, diese Aufgabe anzugehen , schreibe ich eine vernünftige technische Aufgabe mit Spezifikationen.
Im Allgemeinen möchten wir, das Data Engineer-Team, das fertige Tool jedoch nicht in unserem Schrank aufbewahren. Wir sind sicher: Es wird nicht nur uns nützen. Und das nicht nur für die Bedürfnisse von GIS, sondern generell für jede Burst-Verarbeitung von Datensätzen mit parametrierbaren Verarbeitungsschritten.
Im letzten Artikel (oder in einigen Artikeln dauert etwas zu lange) werde ich Ihnen erklären, wie Sie One Ring für Ihre Forschungsaufgaben erstellen, ausführen, erweitern und verwenden.

* Der One Ring OSS-Quellcode enthält keine proprietären heuristischen Locomizer-Algorithmen. Das Repository wird jedoch Schnittstellen und Beschreibungen enthalten, anhand derer die freien Implementierungen dieser Heuristiken mithilfe der Reinraummethode neu erstellt werden können, ohne dass ich dazu aufgefordert werde, den Code einzugeben.

Danksagung


... an Kollegen Gregory pomadchin für sachliche Kommentare zum Thema und sshikov für eine unabhängige Bewertung der Lesbarkeit des Textes sowie an Anton dartov Zadorozhny für ein unerwartetes Feedback zum vorherigen Artikel in der Serie.

Source: https://habr.com/ru/post/de485988/


All Articles