Kapitel 4. Speicherung von Daten

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Wo du die Daten für deine Anwendung speicherst, ist ein entscheidender Teil deiner Datenanalyse-Infrastruktur. Das kann von einer trivialen Angelegenheit, bei der du einfach die nativen Speichersysteme von GA4 verwendest, bis hin zu komplexen Datenströmen reichen, bei denen du mehrere Datenquellen wie GA4, deine CRM-Datenbank, andere Kostendaten aus digitalen Marketingkanälen und mehr einbeziehst. Hier dominiert BigQuery als Analysedatenbank der Wahl in GCP, denn sie wurde entwickelt, um genau die Probleme zu lösen, die sich bei der Arbeit mit Daten aus der Analyseperspektive ergeben, und genau deshalb bietet GA4 sie als Exportoption an. Generell besteht die Philosophie darin, alle Daten an einem Ort zusammenzuführen, an dem du problemlos Analyseabfragen durchführen und sie denjenigen Personen oder Anwendungen zur Verfügung stellen kannst, die sie auf sicherheitsbewusste, aber demokratische Weise benötigen.

In diesem Kapitel gehe ich auf die verschiedenen Entscheidungen und Strategien ein, die ich im Umgang mit Systemen zur Speicherung von Daten zu berücksichtigen gelernt habe. Ich möchte, dass du von meinen Fehlern profitierst, damit du sie vermeiden und dir eine solide Grundlage für deine Anwendungsfälle schaffen kannst.

Dieses Kapitel ist das Bindeglied zwischen der Datenerhebung und der Datenmodellierung in deinen Datenanalyseprojekten. Deine GA4-Daten sollten nach den in Kapitel 3 dargelegten Grundsätzen einfließen, und du wirst sie mit den in diesem Kapitel beschriebenen Werkzeugen und Techniken bearbeiten, um die in den Kapiteln 5 und 6 beschriebenen Methoden zu nutzen, wobei du dich an den Anwendungsfällen orientierst, die du in Kapitel 2 definiert hast.

Wir beginnen mit einigen allgemeinen Grundsätzen, die du bei der Wahl deiner Lösung für die Speicherung von Daten berücksichtigen solltest, und gehen dann einige der beliebtesten Optionen auf GCP durch, die ich täglich benutze.

Datengrundsätze

In diesem Abschnitt geht es um einige allgemeine Richtlinien, die dir bei allen Optionen der Datenspeicherung helfen sollen, die du verwendest. Wir sprechen darüber, wie du deine Daten aufräumst und auf einem hohen Standard hältst, wie du deine Datensätze so gestaltest, dass sie zu den verschiedenen Rollen passen, die dein Unternehmen benötigt, und worauf du bei der Verknüpfung von Datensätzen achten solltest.

Aufgeräumte Daten

Tidy Data ist ein Konzept, das mir von in der R-Community vorgestellt wurde. Es ist eine so gute Idee, dass alle, die mit Daten arbeiten, davon profitieren können, wenn sie seine Prinzipien befolgen. Tidy Data ist eine Beschreibung, wie du deine Daten speichern solltest, damit sie für nachgelagerte Datenflüsse möglichst nützlich sind. Es gibt dir feste Parameter vor, wie du deine Daten speichern solltest, damit du eine gemeinsame Grundlage für alle deine Datenprojekte hast.

Das Konzept der aufgeräumten Daten wurde von Hadley Wickham, Data Scientist bei RStudio und Erfinder des Konzepts für das "tidyverse", entwickelt. Siehe das Buch R for Data Science: Import, Tidy, Transform, Visualize, and Model Data von Garrett Grolemund und Hadley Wickham (O'Reilly), um eine gute Grundlage für die Anwendung der Prinzipien zu erhalten, oder besuche die tidyverse Website.

Obwohl Tidy Data zuerst in der Data-Science-Community von R populär wurde, empfehle ich dir, auch wenn du R nicht verwendest, es als ein erstes Ziel bei deiner Datenverarbeitung zu betrachten. Dies wird in einem Zitat aus dem Buch von Wickham und Grolemund zusammengefasst:

Glückliche Familien sind alle gleich; jede unglückliche Familie ist auf ihre eigene Art unglücklich.

Leo Tolstoi

Ordentliche Datensätze sind alle gleich, aber jeder chaotische Datensatz ist auf seine eigene Weise chaotisch.

Hadley Wickham

Der Grundgedanke ist, dass es einen Weg gibt, deine Rohdaten in einen universellen Standard umzuwandeln, der für die spätere Datenanalyse nützlich ist, und wenn du das auf deine Daten anwendest, musst du das Rad nicht jedes Mal neu erfinden, wenn du deine Daten verarbeiten willst.

Es gibt drei Regeln, die, wenn du sie befolgst, dafür sorgen, dass dein Datensatz aufgeräumt ist:

  1. Jede Variable muss eine eigene Spalte haben.

  2. Jede Beobachtung muss eine eigene Zeile haben.

  3. Jeder Wert muss eine eigene Zelle haben.

Du kannst diese Regeln in Abbildung 4-1 sehen.

Following three rules makes a dataset tidy: variables are in columns, observations are in rows, and values are in cells. From *R for Data Science* by Hadley Wickham and Garret Grolemund
Abbildung 4-1. Drei Regeln sorgen für Ordnung in einem Datensatz: Variablen sind in Spalten, Beobachtungen in Zeilen und Werte in Zellen (ausR for Data Science von Wickham und Grolemund)

Da die Datenbereinigung in der Regel der zeitaufwändigste Teil eines Projekts ist, setzt dies viel geistige Kapazität frei, um an den spezifischen Problemen für deinen Anwendungsfall zu arbeiten, ohne dass du dir jedes Mal Gedanken über den Zustand deiner Daten machen musst. Ich empfehle, dass du nach dem Import deiner Rohdaten alle Anstrengungen unternimmst, um aufgeräumte Datenversionen dieser Daten zu erstellen, die du den nachgelagerten Anwendungsfällen zur Verfügung stellst. Der Standard für aufgeräumte Daten hilft dir, indem er dir die Arbeit abnimmt, jedes Mal darüber nachzudenken, wie deine Daten geformt sein sollten, und ermöglicht deinen nachgelagerten Datenanwendungen eine Standardisierung, da sie davon ausgehen können, dass die Daten immer in einer bestimmten Form vorliegen.

Das ist die Theorie, also schauen wir uns im nächsten Abschnitt an, wie das in der Praxis funktioniert.

Beispiel für das Aufräumen von GA4-Daten

Es folgt ein Beispiel für einen Arbeitsablauf, bei dem wir mit unaufgeräumten GA4-Daten beginnen und diese bereinigen, damit sie für die weitere Analyse bereit sind. Wir verwenden R für das Aufräumen, aber die gleichen Prinzipien können für jede Sprache oder jedes Tool wie Excel gelten.

Beginnen wir mit einigen GA4-Daten. Beispiel 4-1 zeigt ein R-Skript, das einige customEvent-Daten aus meinem Blog exportiert. Diese Daten enthalten die Kategorie, in die ich jeden Blogbeitrag eingeordnet habe, z. B. "Google Analytics" oder "BigQuery". Diese benutzerdefinierten Daten sind in einem customEvent namens category verfügbar.

Beispiel 4-1. R-Skript zum Extrahieren der benutzerdefinierten Dimension category aus der GA4 Data API
library(googleAnalyticsR)

# authenticate with a user with access
ga_auth()

# if you have forgotten your propertyID
ga4s <- ga_account_list("ga4")

# my blog propertyId - change for your own
gaid <- 206670707

# import your custom fields
meta <- ga_meta("data", propertyId = gaid)

# date range of when the field was implemented to today
date_range <- c("2021-07-01", as.character(Sys.Date()))

# filter out any data that doesn't have a category
invalid_category <-
 ga_data_filter(!"customEvent:category" == c("(not set)","null"))

# API call to see trend of custom field: article_read
article_reads <- ga_data(gaid,
    metrics = "eventCount",
    date_range = date_range,
    dimensions = c("date", "customEvent:category"),
    orderBys = ga_data_order(+date),
    dim_filters = invalid_category,
    limit = -1)

Der obere Teil des Inhalts von article_reads ist in Tabelle 4-1 dargestellt.

Du kannst sehen, dass die Qualität der Datenerfassung Auswirkungen auf die nachgelagerte Datenverarbeitung hat: Die Artikelkategorie hätte zum Beispiel in eigene Ereignisse aufgeteilt werden können, um die Daten sauberer zu machen. Es handelt sich nicht um "ordentliche" Daten. Wir müssen die Daten bereinigen, um sie für die Modellierung geeignet zu machen - das ist ganz normal. Das zeigt auch, wie eine saubere Datenerfassung die nachgelagerte Arbeit reduzieren kann.

Tabelle 4-1. Über die Daten-API extrahierte GA4-Daten über googleAnalyticsR
Datum customEvent:category eventCount

2021-07-01

GOOGLE-TAG-MANAGER - CLOUD-FUNCTIONS

13

2021-07-01

GOOGLE-TAG-MANAGER - GOOGLE-ANALYTICS

12

2021-07-01

R - GOOGLE-APP-ENGINE - DOCKER - GOOGLE-ANALYTICS - GOOGLE-COMPUTE-ENGINE - RSTUDIO-SERVER

9

2021-07-01

R - CLOUD-RUN - GOOGLE-TAG-MANAGER - BIG-QUERY

8

2021-07-01

R - DOCKER - CLOUD-RUN

8

2021-07-01

GOOGLE-TAG-MANAGER - DOCKER - CLOUD-RUN

7

2021-07-01

R - GOOGLE-ANALYTICS - SUCHKONSOLE

7

2021-07-01

R - DOCKER - RSTUDIO-SERVER - GOOGLE-COMPUTE-ENGINE

6

2021-07-01

DOCKER - R

5

2021-07-01

R - FIREBASE - GOOGLE-AUTH - CLOUD-FUNCTIONS - PYTHON

5

2021-07-01

R - GOOGLE-AUTH - BIG-QUERY - GOOGLE-ANALYTICS - GOOGLE-CLOUD-STORAGE - GOOGLE-COMPUTE-ENGINE - GOOG

4

2021-07-01

GOOGLE-CLOUD-STORAGE - PYTHON - GOOGLE-ANALYTICS - CLOUD-FUNCTIONS

3

2021-07-01

R - GOOGLE-ANALYTICS

3

2021-07-01

BIG-QUERY - PYTHON - GOOGLE-ANALYTICS - CLOUD-FUNCTIONS

2

2021-07-01

DOCKER - R - GOOGLE-COMPUTE-ENGINE - CLOUD-RUN

2

2021-07-01

R - GOOGLE-AUTH

2

2021-07-01

Docker - R

2

2021-07-02

R - CLOUD-RUN - GOOGLE-TAG-MANAGER - BIG-QUERY

9

2021-07-02

DOCKER - R

8

2021-07-02

GOOGLE-TAG-MANAGER - DOCKER - CLOUD-RUN

8

2021-07-02

GOOGLE-TAG-MANAGER - GOOGLE-ANALYTICS

8

2021-07-02

R - DOCKER - CLOUD-RUN

6

2021-07-02

R - GOOGLE-APP-ENGINE - DOCKER - GOOGLE-ANALYTICS - GOOGLE-COMPUTE-ENGINE - RSTUDIO-SERVER

6

Wie in "Aufgeräumte Daten" beschrieben , liegen diese Daten noch nicht in einer aufgeräumten Form vor, die für die Analyse geeignet ist. Deshalb werden wir einige der tidyverse Bibliotheken von R nutzen, um sie zu bereinigen, nämlich tidyr und dplyr.

Als Erstes benennen wir die Spaltennamen um und trennen die Kategoriezeichenfolgen, damit wir eine pro Spalte haben. Außerdem müssen wir alles klein schreiben. In Beispiel 4-2 erfährst du, wie du das mit Hilfe von tidyverse und dem data.frame article_reads aus Tabelle 4-1 machen kannst.

Beispiel 4-2. Aufräumen der article_reads Rohdaten mit tidy und dplyr, so dass sie ähnlich wie in Tabelle 4-2 aussehen
library(tidyr)
library(dplyr)

clean_cats <- article_reads |>
    # rename data columns
    rename(category = "customEvent:category",
        reads = "eventCount") |>
    # lowercase all category values
    mutate(category = tolower(category)) |>
    # separate the single category column into six
    separate(category,
         into = paste0("category_",1:6),
         sep = "[^[:alnum:]-]+",
         fill = "right", extra = "drop")

Die Daten sehen jetzt wie in Tabelle 4-2 aus. Allerdings sind wir mit dem ordentlichenFormat noch nicht ganz am Ziel.

Tabelle 4-2. Das Ergebnis der Datenbereinigung Tabelle 4-1
Datum Kategorie_1 Kategorie_2 Kategorie_3 Kategorie_4 Kategorie_5 Kategorie_6 liest

2021-07-01

google-tag-manager

cloud-functions

NA

NA

NA

NA

13

2021-07-01

google-tag-manager

google-analytics

NA

NA

NA

NA

12

2021-07-01

r

google-app-engine

Docker

google-analytics

google-compute-engine

rstudio-server

9

2021-07-01

r

Cloud-Run

google-tag-manager

big-query

NA

NA

8

2021-07-01

r

Docker

Cloud-Run

NA

NA

NA

8

2021-07-01

google-tag-manager

Docker

Cloud-Run

NA

NA

NA

7

Wir möchten die Daten aggregieren, sodass jede Zeile eine einzelne Beobachtung darstellt: die Anzahl der Lesungen pro Kategorie und Tag. Dazu schwenken wir die Daten in ein "langes" Format und nicht in das "breite" Format, das wir jetzt haben. Sobald die Daten in diesem längeren Format vorliegen, werden sie über die Spalten Datum und Kategorie aggregiert (ähnlich wie bei SQL GROUP BY), um die Summe der Lesungen pro Kategorie zu erhalten. Siehe Beispiel 4-3.

Beispiel 4-3. Umwandlung der breiten Daten in lange und Aggregation nach Datum/Kategorie
library(dplyr)
library(tidyr)

agg_cats <- clean_cats |>
    # turn wide data into long
    pivot_longer(
        cols = starts_with("category_"),
        values_to = "categories",
        values_drop_na = TRUE
    ) |>
    # group over dimensions we wish to aggregate over
    group_by(date, categories) |>
    # create a category_reads metric: the sum of reads
    summarize(category_reads = sum(reads), .groups = "drop_last") |>
    # order by date and reads, descending
    arrange(date, desc(category_reads))
Hinweis

Die R-Beispiele in diesem Buch setzen R v4.1 voraus, das den Pipe-Operator |> enthält. In R-Versionen vor 4.1 wird der Pipe-Operator aus einem eigenen Paket, magrittr, importiert und sieht aus wie %>%. Für diese Beispiele können sie getrost ausgetauscht werden.

Sobald die Daten aufgeräumt sind, sollten wir eine Tabelle ähnlich der Tabelle 4-3 sehen. Das ist ein aufgeräumter Datensatz, mit dem jeder Datenwissenschaftler oder Analytiker gerne arbeiten sollte und der Ausgangspunkt für die Phase der Modellexploration ist.

Tabelle 4-3. Aufgeräumte Daten aus den article_reads Rohdaten
Datum Kategorien Kategorie_Lesungen

2021-07-01

r

66

2021-07-01

google-tag-manager

42

2021-07-01

Docker

41

2021-07-01

google-analytics

41

2021-07-01

Cloud-Run

25

2021-07-01

cloud-functions

23

Die Beispiele in Büchern scheinen immer idealisiert zu sein, und ich habe das Gefühl, dass sie selten die Arbeit widerspiegeln, die du regelmäßig machen musst, wahrscheinlich viele Iterationen von Experimenten, Fehlerbehebung und Regex. Obwohl es sich um ein Mining-Beispiel handelt, habe ich trotzdem ein paar Anläufe gebraucht, um genau das zu bekommen, was ich in den vorangegangenen Beispielen gesucht habe. Es ist jedoch einfacher zu handhaben, wenn du das Prinzip der aufgeräumten Daten im Hinterkopf behältst - es gibt dir ein Ziel vor, das dich wahrscheinlich davor bewahrt, es später noch einmal machen zu müssen.

Nachdem ich Rohdaten gesammelt habe, ist mein erster Schritt zu sehen, wie ich sie in ordentliche Daten wie in unserem Beispiel verwandeln kann. Aber auch wenn die Daten aufgeräumt sind, musst du dir Gedanken über die Rolle dieser Daten machen, was wir im nächsten Abschnitt behandeln.

Datensätze für verschiedene Rollen

Die eingehenden Rohdaten sind selten in einem Zustand, in dem für die Produktion oder sogar für interne Endnutzer verwendet werden sollte. Wenn die Zahl der Nutzerinnen und Nutzer steigt, gibt es mehr Gründe, ordentliche Datensätze für diese Zwecke vorzubereiten, aber du solltest eine "Quelle der Wahrheit" behalten, so dass du jederzeit zurückverfolgen kannst, wie die abgeleiteten Datensätze entstanden sind.

Hier solltest du über deine Data Governance nachdenken, d.h. darüber, wer und was auf die verschiedenen Datentypen zugreift.

Hier werden ein paar verschiedene Rollen vorgeschlagen:

Rohdaten

Es ist eine gute Idee, deine Rohdatenströme zusammen und unangetastet zu lassen, damit du immer die Möglichkeit hast, sie wiederherzustellen, wenn etwas schief läuft. Im Fall von GA4 ist das der BigQuery-Datenexport. Es ist generell nicht ratsam, diese Daten durch Hinzufügen oder Entfernen von Daten zu verändern, es sei denn, du hast rechtliche Verpflichtungen, wie z. B. die Löschung von personenbezogenen Daten. Es wird auch nicht empfohlen, diesen Datensatz den Endnutzern zur Verfügung zu stellen, es sei denn, sie haben einen Bedarf, da die Rohdatensätze in der Regel ziemlich schwer zu bearbeiten sind. Der GA4-Export hat zum Beispiel eine verschachtelte Struktur, die für jeden, der noch keine Erfahrung mit BigQuery SQL hat, schwer zu erlernen ist. Das ist bedauerlich, denn für manche Leute ist das ihre erste Erfahrung mit Data Engineering, und sie denken, dass es viel schwieriger ist, als wenn sie nur mit den typischen flachen Datensätzen arbeiten würden. Stattdessen werden deine ersten Workflows in der Regel diese Rohdaten nehmen und sie aufräumen, filtern und aggregieren, so dass sie viel handlicher sind.

Aufgeräumte Daten

Das sind Daten, die in einem ersten Durchgang für den Konsum fit gemacht wurden. Hier kannst du schlechte Datenpunkte entfernen, die Namenskonventionen vereinheitlichen, Datensatz-Joins durchführen, wenn das hilfreich ist, Aggregationstabellen erstellen und die Daten leichter nutzbar machen. Wenn du auf der Suche nach einem guten Datensatz bist, der als "Quelle der Wahrheit" dienen soll, dann sind die aufgeräumten Datensätze der ursprünglichen Rohdatenquelle vorzuziehen. Die Pflege dieses Datensatzes ist eine fortlaufende Aufgabe, die höchstwahrscheinlich von den Dateningenieuren übernommen wird, die ihn erstellt haben. Nachgelagerte Datennutzer sollten nur Lesezugriff haben und können helfen, indem sie nützliche Tabellen vorschlagen, die aufgenommen werden sollten.

Geschäftsfälle

Zu den vielen Aggregationen, die du auf aus den aufgeräumten Daten erstellen kannst, gehören auch die typischen geschäftlichen Anwendungsfälle, die die Quelle für viele deiner nachgelagerten Anwendungen sein werden. Ein Beispiel wäre die Zusammenführung deiner Kostendaten aus deinen Medienkanälen und deinen GA4-Webstream-Daten mit deinen Konversionsdaten in deinem CRM. Dies ist ein häufig gewünschter Datensatz, der den gesamten "geschlossenen Kreislauf" der Marketingeffektivitätsdaten enthält (Kosten, Aktion und Konversion). Andere Geschäftsfälle konzentrieren sich eher auf den Vertrieb oder die Produktentwicklung. Wenn du über genügend Daten verfügst, könntest du den entsprechenden Abteilungen bei Bedarf Datensätze zur Verfügung stellen, die dann die Datenquelle für die täglichen Ad-hoc-Abfragen der Endnutzer sind. Der Endnutzer wird wahrscheinlich entweder mit begrenzten SQL-Kenntnissen oder über ein Datenvisualisierungstool wie Looker, Data Studio oder Tableau auf seine Daten zugreifen. Wenn diese relevanten Datensätze allen in deinem Unternehmen zur Verfügung stehen, ist das ein gutes Zeichen dafür, dass du tatsächlich ein "datengesteuertes Unternehmen" bist (ein Satz, den meiner Meinung nach etwa 90 % aller CEOs anstreben, aber vielleicht nur 10 % tatsächlich umsetzen).

Test Spielplatz

Außerdem brauchst du oft einen Scratch Pad, um neue Integrationen, Joins und Entwicklungen auszuprobieren. Wenn du einen eigenen Datensatz mit einem Verfallsdatum von 90 Tagen hast, kannst du sicher sein, dass deine Mitarbeiter mit deinen Datensätzen arbeiten können, ohne dass du verirrte Testdaten von Nutzern aufspüren oder Produktionssysteme beschädigen musst.

Datenanwendungen

Jede Datenanwendung, die du in der Produktion einsetzt, ist höchstwahrscheinlich eine Ableitung aller zuvor genannten Datensatzrollen. Wenn du sicherstellst, dass du eineigenes Dataset für deine geschäftskritischen Anwendungsfälle hast, kannst du immer genau wissen, welche Daten verwendet werden, und vermeiden, dass andere Anwendungsfälle deine Daten beeinträchtigen.

Diese Rollen sind in einer groben Reihenfolge der Datenflüsse angeordnet. Normalerweise werden Ansichten oder Zeitplanungsprogramme so eingerichtet, dass sie Daten verarbeiten und in ihre jeweiligen Abhängigkeiten kopieren, und du kannst sie zur Verwaltung in verschiedenen GCP-Projekten haben.

Hinweis

Ein großer Vorteil bei der Verwendung von Datensätzen wie als BigQuery-Exporte von GA4 ist die Verknüpfung dieser Daten mit deinen anderen Daten, wie in "Verknüpfung von Datensätzen" beschrieben .

Wir haben untersucht, was dazu beiträgt, dass die Arbeit mit deinen Datensätzen für deine Nutzerinnen und Nutzer zum Vergnügen wird. Wenn du den Traum von aufgeräumten, rollendefinierten Datensätzen verwirklichst, die Daten über alle Abteilungen hinweg so miteinander verknüpfen, dass die Nutzerinnen und Nutzer mit einem Knopfdruck (oder einer SQL-Abfrage) alles haben, was sie brauchen, bist du vielen Unternehmen bereits voraus. Nehmen wir als Beispiel Google, das viele als Inbegriff eines datengesteuerten Unternehmens betrachten. In Laks Buch " Data Science on the Google Cloud Platform" erzählt er, dass 80 % der Google-Mitarbeiter wöchentlich mit Daten arbeiten:

Bei Google zum Beispiel nutzen fast 80 % der Beschäftigten jeden Monat Dremel (Dremel ist das interne Gegenstück zu BigQuery von Google Cloud). Manche nutzen Daten auf anspruchsvollere Art und Weise als andere, aber jeder hat regelmäßig mit Daten zu tun, um seine Entscheidungen zu treffen. Wenn du jemandem eine Frage stellst, erhältst du wahrscheinlich einen Link zu einer BigQuery-Ansicht der Abfrage und nicht die eigentliche Antwort: "Führe diese Abfrage jedes Mal aus, wenn du die aktuellste Antwort wissen willst", so der Gedanke. BigQuery hat sich in diesem Szenario von einem einfachen Datenbankersatz zu einer Self-Service-Lösung für die Datenanalyse entwickelt.

Das Zitat spiegelt wider, worauf viele Unternehmen hinarbeiten und was sie sich für ihre eigenen Beschäftigten wünschen, und das hätte große Auswirkungen auf das Geschäft, wenn es vollständig umgesetzt würde.

Im nächsten Abschnitt betrachten wir das im Zitat erwähnte Tool, das dies für Google ermöglicht hat: BigQuery.

BigQuery

Es ist so etwas wie eine Binsenweisheit, dass alle deine Anforderungen an die Datenanalyse gelöst werden, wenn du einfach BigQuery verwendest. Es hatte einen großen Einfluss auf meine Karriere und hat das Data Engineering von einer frustrierenden Übung, bei der man viel Zeit für dieInfrastruktur und das Laden von Daten aufwenden muss, in die Lage versetzt, sich mehr auf die Wertschöpfung aus den Daten zu konzentrieren.

Über BigQuery haben wir bereits in "BigQuery" im Abschnitt über die Dateneingabe gesprochen, und zwar im Hinblick auf die GA4 BigQuery Exporte ("GA4 mit BigQuery verknüpfen") und den Import von Cloud Storage Dateien aus Cloud Storage zur Verwendung mit CRM Exporten ("Ereignisgesteuerte Speicherung"). In diesem Abschnitt geht es darum, wie du deine Daten organisierst und mit ihnen arbeitest, nachdem sie in BigQuery gespeichert wurden.

Wann sollte BigQuery verwendet werden?

Es ist vielleicht einfacher zu skizzieren, wenn BigQuery nicht verwendet, da es so etwas wie ein Allheilmittel für Digital-Analytics-Aufgaben auf GCP ist. BigQuery hat die folgenden Funktionen, die du auch für eine Analytics-Datenbank brauchst:

  • Günstige oder kostenlose Speicherung, damit du all deine Daten einspeichern kannst, ohne dir Gedanken über die Kosten zu machen.

  • Unbegrenzte Skalierbarkeit, sodass du dir keine Gedanken über die Erstellung neuer Instanzen von Servern machen musst, die du später zusammenbinden kannst, wenn du Daten im Petabyte-Bereich hinzufügst.

  • Flexible Kostenstrukturen: Die übliche Wahl ist eine, die nur bei zunehmender Nutzung (über Abfragen) skaliert, anstatt jeden Monat versunkene Kosten für die Server zu zahlen, oder du kannst Slots für versunkene Kosten reservieren, um Abfragekosten zu sparen.

  • Integrationen mit dem Rest deiner GCP-Suite, um deine Daten durch maschinelles Lernen oder auf andere Weise zu verbessern.

  • Datenbankinterne Berechnungen, die gängige SQL-Funktionen wie ZÄHLEN, MITTELWERT und SUMMEN umfassen, bis hin zu Aufgaben des maschinellen Lernens wie Clustering und Prognosen, sodass du die Daten nicht erst exportieren, modellieren und dann wieder einfügen musst.

  • Massiv skalierbare Fensterfunktionen, die eine herkömmliche Datenbank zum Absturz bringen würden.

  • Schnelle Ergebnisse (Minuten im Vergleich zu Stunden in herkömmlichen Datenbanken), selbst wenn du Milliarden von Zeilen durchsuchst.

  • Eine flexible Datenstruktur, die es dir ermöglicht, mit vielen einzelnen und vielen Datenpunkten zu arbeiten, ohne dass du viele einzelne Tabellen benötigst (die Datenverschachtelung).

  • Einfacher Zugang über eine Weboberfläche mit einem sicheren OAuth2-Login.

  • Fein abgestufte Benutzerzugriffsfunktionen von Projekt, Datensatz und Tabelle bis hin zur Möglichkeit, Benutzern nur Zugriff auf einzelne Zeilen und Spalten zu geben.

  • Eine leistungsstarke externe API, die alle Funktionen abdeckt, die es dir ermöglicht, sowohl deine eigenen Anwendungen zu erstellen als auch Software von Drittanbietern zu wählen, die dieselbe API verwendet haben, um hilfreiche Middleware zu erstellen.

  • Integration mit anderen Clouds wie AWS und Azure, um deine bestehenden Datenstapel zu importieren/exportieren - zum Beispiel mit BigQuery Omni kannst du Daten direkt bei anderen Cloud-Providern abfragen.

  • Streaming-Datenanwendungen für Updates fast in Echtzeit.

  • Die Fähigkeit, das Datenschema automatisch zu erkennen und beim Hinzufügen neuer Felder einigermaßen flexibel zu sein.

BigQuery verfügt über diese Funktionen, weil es als ultimative Analysedatenbank entwickelt wurde, während sich herkömmliche SQL-Datenbanken auf den schnellen Transaktionszugriff auf Zeilen konzentrieren, was zu Lasten der Geschwindigkeit beim Durchsuchen von Spalten geht.

BigQuery war eines der ersten Cloud-Datenbanksysteme für Analysen, aber ab 2022 gibt es mehrere andere Datenbankplattformen, die eine ähnliche Leistung bieten, wie z. B. Snowflake, was die Branche wettbewerbsfähiger macht. Dies treibt die Innovation in BigQuery und darüber hinaus voran, und das kann für die Nutzer/innen jeder Plattform nur gut sein. Unabhängig davon sollten die gleichen Grundsätze gelten. Bevor wir uns mit den Details der SQL-Abfragen befassen, schauen wir uns an, wie die Datensätze inBigQuery organisiert sind.

Organisation des Datensatzes

Bei der Arbeit mit BigQuery-Datensätzen habe ich ein paar Grundsätze gelernt, die ich hier weitergeben kann.

Die erste Überlegung ist, deinen Datensatz in einer Region zu platzieren, die für deine Nutzer relevant ist. Eine der wenigen Einschränkungen von BigQuery SQL ist, dass du Datentabellen nicht über Regionen hinweg zusammenführen kannst, was bedeutet, dass deine Daten aus der EU und den USA nicht einfach zusammengeführt werden können. Wenn du zum Beispiel von der EU aus arbeitest, musst du bei der Erstellung von Datensätzen normalerweise die EU-Region angeben.

Tipp

Standardmäßig geht BigQuery davon aus, dass du deine Daten in den USA haben möchtest. Es wird empfohlen, die Region immer anzugeben, wenn du dein Dataset erstellst, damit du sicher sein kannst, wo es sich befindet, und damit du später nicht für alle deine Daten einen Regionstransfer durchführen musst. Das ist besonders wichtig, wenn es um die Einhaltung von Datenschutzbestimmungen geht.

Eine gute Namensstruktur für deine Datensätze ist ebenfalls nützlich, damit die Nutzer die gesuchten Daten schnell finden können. So solltest du z. B. immer die Quelle und die Rolle des Datensatzes angeben und nicht nur numerische IDs: ga4_tidy und nicht die GA4 MeasurementId G-1234567.

Scheue dich auch nicht, Daten in anderen GCP-Projekten abzulegen, wenn es organisatorisch sinnvoll ist - BigQuery SQL funktioniert projektübergreifend, so dass ein Benutzer, der Zugriff auf beide Projekte hat, sie abfragen kann (wenn sich beide Tabellen in derselben Region befinden). Eine gängigeAnwendung ist die Verwendung von Entwicklungs-, Staging- und Produktionsprojekten. Ein Vorschlag für die Kategorisierung deiner BigQuery-Datensätze folgt den Hauptthemen dieses Buches:

Rohdatensätze

Datensätze, die das erste Ziel für externe APIs oder Dienste sind.

Aufgeräumte Datensätze

Datensätze, die aufgeräumt werden und bei denen vielleicht Aggregationen oder Joins durchgeführt werden, um zu einem nützlichen Basiszustand zu gelangen, den andere abgeleitete Tabellen als "Quelle der Wahrheit" nutzen.

Datensätze modellieren

Datensätze, die die Modellergebnisse abdecken, die in der Regel die aufgeräumten Datensätze als Quelle haben und als Zwischentabellen für die späteren Aktivierungstabellen dienen können.

Aktivierungsdatensätze

Datensätze, die die Ansichten und bereinigten Tabellen enthalten, werden für alle Aktivierungsarbeiten wie Dashboards, API-Endpunkte oder Exporte externer Anbieter erstellt.

Test-/Entwicklungsdatensätze

Normalerweise erstelle ich einen Datensatz mit einer Ablaufzeit von 90 Tagen für die Entwicklungsarbeit, damit die Benutzer/innen einen Notizblock haben, um Tabellen zu erstellen, ohne die produktionsreifen Datensätze zu überladen.

Mit einer guten Benennungsstruktur für Datensätze nimmst du die Gelegenheit wahr, nützliche Metadaten zu deinen BigQuery-Tabellen hinzuzufügen, die es dem Rest deines Unternehmens ermöglichen, das Gesuchte schnell und einfach zu finden, die Schulungskosten zu senken und deinen Datenanalysten mehr Selbstmanagement zu ermöglichen.

Bisher haben wir uns mit der Organisation der Datensätze befasst, aber jetzt geht es um die technischen Spezifikationen der Tabellen in diesen Datensätzen.

Tisch-Tipps

Dieser Abschnitt behandelt einige Lektionen, die ich gelernt habe wenn ich mit Tabellen in BigQuery arbeite. Es geht um Strategien, die das Laden, Abfragen und Extrahieren von Daten einfacher machen. Wenn du diese Tipps bei der Arbeit mit deinen Daten befolgst, bist du für die Zukunft gerüstet:

Partitionieren und clustern, wenn möglich

Wenn du mit regelmäßigen Datenaktualisierungen zu tun hast, ist es besser, partitionierte Tabellen zu verwenden, die deine Daten in tägliche (oder stündliche, monatliche, jährliche, usw.) Tabellen aufteilen. So kannst du alle deine Daten problemlos abfragen, hast aber trotzdem die Möglichkeit, die Tabellen bei Bedarf auf bestimmte Zeiträume zu beschränken. Clustering ist eine weitere Funktion von BigQuery, mit der du die Daten so organisieren kannst, dass du sie schneller abfragen kannst - das kannst du beim Import deiner Daten einrichten. Mehr über beide Funktionen und wie sie sich auf deine Daten auswirken, erfährst du in Googles "Introduction to Partitioned Tables".

Abschneiden statt Anhängen

Beim Importieren von Daten versuche ich das APPEND Modell des Hinzufügens von Daten zum Datensatz zu vermeiden und bevorzuge eine zustandslosere WRITE_TRUNCATE (z. B. Überschreiben) Strategie. Dies ermöglicht Wiederholungen, ohne dass vorher Daten gelöscht werden müssen, z. B. ein idempotenter Workflow, der zustandslos ist. Dies funktioniert am besten mit gesplitteten oder partitionierten Tabellen. Dies ist möglicherweise nicht möglich, wenn du sehr große Datenmengen importierst und es zu kostspielig ist, einen vollständigen Reload zu erstellen.

Standardmäßig flach, aber aus Leistungsgründen verschachtelt

Wenn du Tabellen an weniger erfahrene SQL-Benutzer weitergibst, ist eine flache Tabelle für sie viel einfacher zu handhaben als die verschachtelte Struktur, die BigQuery erlaubt. Eine flache Tabelle kann viel größer sein als eine verschachtelte Tabelle, aber du solltest trotzdem aggregieren und filtern, um das Datenvolumen zu verringern. Verschachtelte Tabellen sind jedoch eine gute Möglichkeit, um sicherzustellen, dass du nicht zu viele Joins über die Daten hast. Als Faustregel gilt: Wenn du deinen Datensatz immer wieder mit einem anderen verknüpfst, sind die Daten in einer verschachtelten Struktur vielleicht besser aufgehoben. Diese verschachtelten Tabellen sind in den Rohdatensätzen häufiger zu finden.

Wenn du diese Tipps umsetzt, musst du dir bei einem erneuten Import keine Sorgen um doppelte Daten machen. Der falsche Tag wird gelöscht und die neuen, frischen Daten werden an seine Stelle gesetzt, aber nur für diese Partition, damit du nicht deinen gesamten Datensatz neu importieren musst, um sicher zu sein, dass du die Wahrheit kennst.

Kosten für SELECT *

Ich würde sogar so weit gehen, eine Faustregel aufzustellen, niemals SELECT* in deinen Produktionstabellen zu verwenden, da dies schnell zu hohen Kosten führen kann. Das wird noch deutlicher, wenn du damit einen View erstellst, der häufig abgefragt wird. Da die Kosten für BigQuery eher davon abhängen, wie viele Spalten und nicht wie viele Zeilen in der Abfrage enthalten sind, wählt SELECT* alle Spalten aus und verursacht die höchsten Kosten. Sei außerdem vorsichtig, wenn du Spalten entschachtelst, denn auch das kann das Datenvolumen erhöhen, für das du bezahlt wirst.

In diesem Buch gibt es viele SQL-Beispiele, die sich mit spezifischen Anwendungsfällen befassen, daher geht es in diesem Abschnitt eher um die Spezifikation der Tabellen, auf die SQL angewendet wird. Die allgemeinen Grundsätze sollen dir helfen, deine BigQuery-Daten sauber und effizient zu verwalten, damit sie zu einem beliebten Werkzeug in deinem Unternehmen werden.

BigQuery kann zwar mit Streaming-Daten umgehen, aber manchmal brauchen ereignisbasierte Daten ein spezielleres Tool, und dann kommt Pub/Sub ins Spiel.

Kneipe/Verleih

Pub/Sub ist für viele Datenimporte unverzichtbar. Pub/Sub ist ein globales Nachrichtensystem, d.h. es ist eine Möglichkeit, die Pipes zwischen Datenquellen ereignisgesteuert zu betreiben.

Pub/Sub-Nachrichten werden garantiert mindestens einmal zugestellt. So kannst du die Konsistenz deiner Pipelines sicherstellen. Dies unterscheidet sich z. B. von HTTP-API-Aufrufen, bei denen du dich nicht darauf verlassen kannst, dass sie zu 100% funktionieren. Pub/Sub erreicht dies dadurch, dass die empfangenden Systeme den Empfang der Pub/Sub-Nachricht bestätigen müssen ("ack"). Kommt kein "ack" zurück, stellt Pub/Sub die Nachricht in die Warteschlange und sendet sie erneut. Dies geschieht in großem Maßstab - Milliarden von Treffern können über Pub/Sub gesendet werden. Die Technologie ähnelt dem Googlebot, der das gesamte World Wide Web für die Google-Suche durchforstet.

Pub/Sub ist keine Datenspeicherung im eigentlichen Sinne, aber es funktioniert wie die Pipes zwischen den Speicherlösungen auf GCP und ist daher hier relevant. Pub/Sub funktioniert wie eine allgemeine Pipeline, an die du Daten über die Topics senden kannst, die du dann am anderen Ende über die Subscriptions konsumieren kannst. Du kannst einem Topic viele Abonnements zuordnen. Außerdem ist es skalierbar: Du kannst Milliarden von Ereignissen senden, ohne dir Gedanken über die Einrichtung von Servern machen zu müssen, und dank der garantierten einmaligen Zustellung kannst du sicher sein, dass sie auch ankommen. Diese Garantie ist möglich, weil jedes Abonnement bestätigen muss, dass es die gesendeten Daten erhalten hat (oder "ack", wie es in den Nachrichtenwarteschlangen heißt), sonst stellt es sie in die Warteschlange, um sie erneut zu senden.

Dieses Themen-/Abonnementmodell bedeutet, dass ein Ereignis an mehrere Speicheranwendungen oder ereignisbasierte Auslöser gesendet werden kann. Fast jede Aktion auf GCP hat eine Option, um ein Pub/Sub-Ereignis zu senden, da sie auch über Logging-Filter ausgelöst werden können. Dies war meine erste Anwendung, bei der ich sie eingesetzt habe: Die Exporte von BigQuery GA360 sind dafür bekannt, dass sie nicht immer zur gleichen Zeit am Tag eintreffen, was nachgelagerte Importaufträge stören kann, wenn sie nach einem Zeitplan eingerichtet wurden. Wenn du im Log nachverfolgst, wann die BigQuery-Tabellen tatsächlich befüllt wurden, kannst du ein Pub/Sub-Ereignis auslösen, das die Aufträge startet.

Einrichten eines Pub/Sub-Themas für GA4 BigQuery-Exporte

Ein nützliches Pub/Sub-Ereignis tritt ein, wenn deine GA4 BigQuery-Exporte fertig sind, die wir später für andere Anwendungen (wie "Cloud Build") nutzen können .

Dazu können wir die allgemeinen Protokolle der Google Cloud Console verwenden, die Cloud Logging genannt werden. Hier befinden sich alle Logs für alle Dienste, die du verwendest, einschließlich BigQuery. Wenn wir die Log-Einträge der Dienste für die Aktivität, die du überwachen willst, herausfiltern können, kannst du eine logs-basierte Metrik einrichten, die ein Pub/Sub-Thema auslöst.

Zuerst müssen wir ein Pub/Sub-Thema aus den Cloud Logging-Einträgen erstellen, die deine BigQuery-Aktivitäten aufzeichnen, wenn der GA4-Export fertig ist.

Beispiel 4-4 zeigt ein Beispiel für einen solchen Filter, mit den Ergebnissen in Abbildung 4-2.

Beispiel 4-4. Ein Filter, den du in Cloud Logging verwenden kannst, um zu sehen, wann dein GA4 BigQuery-Export fertig ist
resource.type="bigquery_resource"
protoPayload.authenticationInfo.principalEmail=
    "firebase-measurement@system.gserviceaccount.com"
protoPayload.methodName="jobservice.jobcompleted"

Wenn wir diesen Filter anwenden, sehen wir nur die Einträge, wenn der Firebase-Dienstschlüssel firebase-measurement@system.gserviceaccount.com die Aktualisierung deiner BigQuery-Tabelle abgeschlossen hat.

A Cloud Logging filter for seeing when your GA4 BigQuery exports are ready. We shall use this to create a Pub/Sub topic.
Abbildung 4-2. Ein Cloud Logging-Filter, mit dem du sehen kannst, wann deine GA4-BigQuery-Exporte bereit sind, um ein Pub/Sub-Thema zu erstellen

Wenn du mit dem Log-Filter zufrieden bist, wähle den "Logs Router", um die Logs an Pub/Sub weiterzuleiten. Ein Beispiel für den Einrichtungsbildschirm findest du in Abbildung 4-3.

Sobald das Protokoll erstellt ist, solltest du jedes Mal eine Pub/Sub-Meldung erhalten, wenn der BigQuery-Export für die spätere Verwendung bereit ist. Ich empfehle, die Daten mit Cloud Build zu verarbeiten, wie in "Cloud Build" beschrieben , oder dem Beispiel im nächsten Abschnitt zu folgen, das eine partitionierte BigQuery-Tabelle erstellt.

Setting up your GA4 BigQuery log so it sends the entries to Pub/Sub topic named ga4-bigquery
Abbildung 4-3. Einrichten des GA4-BigQuery-Protokolls, so dass es die Einträge an das Pub/Sub-Thema mit dem Namen ga4-bigquery

Partitionierte BigQuery-Tabellen aus deinem GA4-Export erstellen

Standardmäßig werden die GA4-Exporte in "Sharded"-Tabellen gespeichert: . Das bedeutet, dass jede Tabelle separat erstellt wird und du Wildcards in der SQL verwendest, um sie alle abzurufen, z.B. heißen die Tabellen für drei Tage events_20210101, events_20210102 und events_20210103, die du mit dem SQL-Snippet SELECT * FROM dataset.events_*abfragen kannst - * ist der Wildcard.

Das funktioniert, aber wenn du deine nachgelagerten Abfragen optimieren möchtest, erleichtert das Zusammenfassen der Tabellen in eine partitionierte Tabelle den Ablauf einiger Aufträge und ermöglicht einige Abfrageoptimierungen für mehr Geschwindigkeit. Wir verwenden das in Abbildung 4-3 dargestellte Pub/Sub-Thema, um einen Auftrag auszulösen, der die Tabelle in eine partitionierte Tabelle kopiert.

Dazu gehst du zum Thema Pub/Sub und erstellst eine Cloud-Funktion, die von diesem Thema ausgelöst wird, indem du auf die Schaltfläche oben drückst. Den Code zum Kopieren der Tabelle in eine partitionierte Tabelle findest du in Beispiel 4-5.

Beispiel 4-5. Python-Code für eine Cloud-Funktion zum Kopieren deiner GA4 BigQuery-Exporte in eine partitionierte Tabelle
import logging
import base64
import JSON
from google.cloud import bigquery # pip google-cloud-bigquery==1.5.1
import re

# replace with your dataset
DEST_DATASET = 'REPLACE_DATASET'

def make_partition_tbl_name(table_id):
  t_split = table_id.split('_20')

  name = t_split[0]

  suffix = ''.join(re.findall("\d\d", table_id)[0:4])
  name = name + '$' + suffix

  logging.info('partition table name: {}'.format(name))

  return name


def copy_bq(dataset_id, table_id):
  client = bigquery.Client()
  dest_dataset = DEST_DATASET
  dest_table = make_partition_tbl_name(table_id)

  source_table_ref = client.dataset(dataset_id).table(table_id)
  dest_table_ref = client.dataset(dest_dataset).table(dest_table)

  job = client.copy_table(
    source_table_ref,
    dest_table_ref,
    location = 'EU') # API request

  logging.info(f"Copy job:
   dataset {dataset_id}: tableId {table_id} ->
   dataset {dest_dataset}: tableId {dest_table} -
   check BigQuery logs of job_id: {job.job_id}
   for status")

def extract_data(data):
  """Gets the tableId, datasetId from pub/sub data"""
  data = JSON.loads(data)
  complete = data['protoPayload']['serviceData']['jobCompletedEvent']['job']
  table_info = complete['jobConfiguration']['load']['destinationTable']
  logging.info('Found data: {}'.format(JSON.dumps(table_info)))
  return table_info

def bq_to_bq(data, context):
  if 'data' in data:
    table_info = extract_data(base64.b64decode(data['data']).decode('utf-8'))
    copy_bq(dataset_id=table_info['datasetId'], table_id=table_info['tableId'])
  else:
    raise ValueError('No data found in pub-sub')

Setze die Cloud-Funktion mit einem eigenen Dienstkonto ein und erteile diesem Dienstkonto BigQuery Data Owner-Berechtigungen. Als bewährte Methode solltest du dich möglichst auf einen bestimmten Datensatz oder eine Tabelle beschränken.

Sobald die Cloud Function eingesetzt wird, werden deine GA4-BigQuery-Exporte in eine partitionierte Tabelle in einem anderen Dataset dupliziert. Die Cloud Function reagiert auf die Pub/Sub-Meldung, dass der GA4-Export bereit ist, und löst einen BigQuery-Auftrag zum Kopieren der Tabelle aus. Dies ist hilfreich für Anwendungen wie die Data Loss Prevention API, die nicht mit Sharded-Tabellen arbeitet. Dies wird in einer Beispielanwendung in "Data Loss Prevention API" gezeigt .

Server-seitiger Push zu Pub/Sub

Pub/Sub kann auch als Teil deiner Datenerfassungspipeline verwendet werden, wenn du mit GTM SS verwendest. Von deinem GTM SS-Container aus kannst du alle Ereignisdaten zur späteren Verwendung an einen Pub/Sub-Endpunkt senden.

In GTM SS kannst du einen Container erstellen, der alle Ereignisdaten an einen HTTP-Endpunkt sendet. Dieser HTTP-Endpunkt kann eine Cloud-Funktion sein, die die Daten an ein Pub/Sub-Thema überträgt - der entsprechende Code ist in Beispiel 4-6 zu sehen.

Beispiel 4-6. Ein Beispielcode, der zeigt, wie man die GTM SS-Ereignisse an einen HTTP-Endpunkt sendet, der sie in ein Pub/Sub-Thema umwandelt
const getAllEventData = require('getAllEventData');
const log = require("logToConsole");
const JSON = require("JSON");
const sendHttpRequest = require('sendHttpRequest');

log(data);

const postBody = JSON.stringify(getAllEventData());

log('postBody parsed to:', postBody);

const url = data.endpoint + '/' + data.topic_path;

log('Sending event data to:' + url);

const options = {method: 'POST',
         headers: {'Content-Type':'application/JSON'}};

// Sends a POST request
sendHttpRequest(url, (statusCode) => {
 if (statusCode >= 200 && statusCode < 300) {
  data.gtmOnSuccess();
 } else {
  data.gtmOnFailure();
 }
}, options, postBody);

Eine Cloud-Funktion kann eingesetzt werden, um diesen HTTP-Endpunkt mit dem GTM SS-Ereignis-Payload zu empfangen und ein Pub/Sub-Thema zu erstellen, wie in Beispiel 4-7 gezeigt.

Beispiel 4-7. Eine HTTP-Cloud-Funktion, auf die innerhalb des GTM SS-Tags verwiesen wird, die die GTM SS-Ereignisdaten abruft und ein Pub/Sub-Thema mit ihrem Inhalt erstellt
import os, JSON
from google.cloud import pubsub_v1 # google-cloud-Pub/Sub==2.8.0

def http_to_Pub/Sub(request):
  request_JSON = request.get_JSON()
  request_args = request.args

  print('Request JSON: {}'.format(request_JSON))

  if request_JSON:
    res = trigger(JSON.dumps(request_JSON).encode('utf-8'), request.path)
    return res
  else:
    return 'No data found', 204


def trigger(data, topic_name):
 publisher = Pub/Sub_v1.PublisherClient()

 project_id = os.getenv('GCP_PROJECT')
 topic_name = f"projects/{project_id}/topics/{topic_name}"

 print ('Publishing message to topic {}'.format(topic_name))

 # create topic if necessary
 try:
  future = publisher.publish(topic_name, data)
  future_return = future.result()
  print('Published message {}'.format(future_return))

  return future_return

 except Exception as e:
  print('Topic {} does not exist? Attempting to create it'.format(topic_name))
  print('Error: {}'.format(e))

  publisher.create_topic(name=topic_name)
  print ('Topic created ' + topic_name)

  return 'Topic Created', 201

Firestore

Firestore ist eine NoSQL-Datenbank im Gegensatz zu SQL, das du in Produkten wie "BigQuery" verwenden kannst . Als Ergänzung zu BigQuery ist Firestore (oder Datastore) ein Gegenstück, das sich auf schnelle Antwortzeiten konzentriert. Firestore arbeitet mit Schlüsseln, die für schnelle Abfragen der damit verbundenen Daten verwendet werden - und mit schnell meinen wir weniger als eine Sekunde. Das bedeutet, dass du mit Firestore anders arbeiten musst als mit BigQuery. Meistens sollten sich Anfragen an die Datenbank auf einen Schlüssel (wie eine Benutzer-ID) beziehen, der ein Objekt (wie Benutzereigenschaften) zurückgibt.

Hinweis

Firestore hieß früher Datastore und ist ein Rebranding des Produkts. Firestore ist eine NoSQL-Dokumentendatenbank, die das Beste aus Datastore und einem anderen Produkt namens Firebase Realtime Database vereint und auf automatische Skalierung, hohe Leistung und einfache Anwendungsentwicklung ausgelegt ist.

Firestore ist mit den Produkten der Firebase-Suite verbunden und wird normalerweise für mobile Anwendungen verwendet, die über Caching, Batching usw. erste Lookups mit mobiler Unterstützung benötigen. Seine Eigenschaften können auch für Analytics-Anwendungen hilfreich sein, denn er ist ideal für schnelle Lookups bei der Angabe einer ID, z. B. einer Benutzer-ID.

Wann sollte man Firestore verwenden?

Ich verwende Firestore in der Regel, wenn ich APIs erstellen möchte, die möglicherweise mehrmals pro Sekunde aufgerufen werden, z. B. um die Attribute eines Benutzers bei Angabe seiner Benutzer-ID abzurufen. In der Regel geht es dabei eher darum, die Datenaktivierung eines Projekts zu unterstützen, mit einer leichten API, die deine ID nimmt, den Firestore abfragt und die Attribute innerhalb weniger Mikrosekunden zurückgibt.

Wenn du mal eine schnelle Suche brauchst, ist Firestore ebenfalls sehr praktisch. Ein Beispiel, das sich gut für die Nachverfolgung von Analysen eignet, ist die Speicherung deiner Produktdatenbank in einem Firestore mit einer Suche nach der Produkt-SKU, die den Preis, die Marke, die Kategorie usw. des Produkts liefert. Mit einer solchen Datenbank kannst du deine Analytics-Sammlung verbessern, indem du die E-Commerce-Treffer auf die SKU reduzierst und die Daten nachschlägst, bevor du sie an GA4 sendest. So kannst du viel kleinere Treffer aus dem Webbrowser des Nutzers senden, was Vorteile in Bezug auf Sicherheit, Geschwindigkeit und Effizienz mit sich bringt.

Zugriff auf Firestore-Daten über eine API

Um auf Firestore zuzugreifen, musst du zunächst deine Daten in eine Firestore-Instanz importieren. Das kannst du über die Import-APIs tun oder auch manuell über die WebUI eingeben. Die Voraussetzung für den Datensatz ist, dass du immer einen Schlüssel hast, den du typischerweise an die Datenbank sendest, um Daten zurückzugeben, und dann eine verschachtelte JSON-Struktur von Daten zurückkommt.

Das Hinzufügen von Daten zu Firestore beinhaltet die Definition des Objekts, das du aufzeichnen möchtest, das sich in einer verschachtelten Struktur befinden könnte, und dessen Position in der Datenbank. Insgesamt wird dadurch ein Firestore-Dokument definiert. Ein Beispiel für das Hinzufügen von Daten mit Python ist in Beispiel 4-8 dargestellt.

Beispiel 4-8. Importieren einer Datenstruktur in Firestore mithilfe des Python SDK, in diesem Fall eine Demo-Produkt-SKU mit einigen Details
from google.cloud import firestore
db = firestore.Client()

product_id = u'SKU12345'

data = {
  u'name': u'Muffins',
  u'brand': u'Mule',
  u'price': 15.78
}

# Add a new doc in collection 'your-firestore-collection'
db.collection(u'your-firestore-collection').document(product_id).set(data)

Das bedeutet, dass du möglicherweise eine zusätzliche Datenpipeline für den Import deiner Daten in Firebase benötigst, damit du die Daten aus deinen Anwendungen heraus nachschlagen kannst, was einen ähnlichen Code wie in Beispiel 4-8 erfordern würde.

Sobald du deine Daten in Firestore hast, kannst du sie über deine Anwendung erreichen. Beispiel 4-9 zeigt eine Python-Funktion, die du in einer Cloud Function oder App Engine-Anwendung verwenden kannst. Wir gehen davon aus, dass diese Funktion verwendet wird, um Produktinformationen abzurufen, wenn sie auf product_id bereitgestellt wird.

Beispiel 4-9. Ein Beispiel für das Lesen von Daten aus einer Firestore-Datenbank mit Python innerhalb einer Cloud-Funktion
# pip google-cloud-firestore==2.3.4
from google.cloud import firestore

def read_firestore(product_id):
 db = firestore.Client()
 fs = 'your-firestore-collection'
 try:
  doc_ref = db.collection(fs).document(product_id)
 except:
  print(f'Could not connect to firestore collection: {fs}')
  return {}

 doc = doc_ref.get()
 if doc.exists:
  print(f'product_id data found: {doc.to_dict()}')
  return doc.to_dict()
 else:
  print(f'Could not find entry for product_id: {product_id}')
  return {}

Firestore ist ein weiteres Tool, das dir bei deinen digitalen Analyse-Workflows helfen kann. Es kommt vor allem dann zum Einsatz, wenn du Echtzeitanwendungen und Reaktionszeiten im Millisekundenbereich benötigst, z. B. beim Aufruf einer API oder wenn ein Nutzer auf deiner Website surft und du keine Latenzzeit in Kauf nehmen willst. Sie eignet sich eher für Webanwendungs-Frameworks als für Datenanalyseaufgaben und wird daher oft in den letzten Schritten der Datenaktivierung eingesetzt.

BigQuery und Firestore sind beides Beispiele für Datenbanken, die mit strukturierten Daten arbeiten, aber du wirst auch auf unstrukturierte Daten wie Videos, Bilder oder Audiodaten stoßen oder einfach auf Daten, deren Form du nicht kennst, bevor du sie verarbeitest. In diesem Fall müssen deine Speicheroptionen auf einer niedrigeren Ebene der Bytespeicherung arbeiten, und hier kommt die Cloud-Speicherung ins Spiel.

GCS

Wir haben bereits unter "Google Cloud Storage" über die Verwendung von GCS für die Eingabe von Daten in CRM-Systeme gesprochen , aber in diesem Abschnitt geht es eher um die allgemeine Verwendung von GCS. GCS ist für verschiedene Aufgaben nützlich, denn es zeichnet sich durch eine einfache Aufgabe aus: Bytes sicher und sofort verfügbar zu halten.

GCS ist das Speichersystem des GCP-Dienstes, das am ehesten mit der Festplatte vergleichbar ist, auf der die Dateien auf deinem Computer gespeichert sind. Du kannst die Daten nicht verändern oder bearbeiten, bis du sie in einer Anwendung öffnest, aber es speichert TBs an Daten, auf die du sicher und zugänglich zugreifen kannst. Ich verwende es für die folgenden Aufgaben:

Unstrukturierte Daten

Für Objekte, die nicht in eine Datenbank geladen werden können, wie z.B. Videos und Bilder, ist GCS ein Ort, der immer helfen kann. Es kann alles innerhalb von Bytes in seinen Buckets speichern, Objekte, die liebevoll als "Blobs" bezeichnet werden. Wenn du mit Google-APIs wie Sprache-zu-Text oder Bilderkennung arbeitest, müssen die Dateien in der Regel zuerst in GCS hochgeladen werden.

Rohdaten-Backups

Auch für strukturierte Daten ist GCS hilfreich als Rohdaten-Backup, das mit seinen niedrigen Archivierungsraten gespeichert werden kann, damit du nach einem Ausfall immer wieder zurückspulen oder ein Disaster-Recovery durchführen kannst.

Landeplätze für den Datenimport

Wie in "Google Cloud Storage" zu sehen ist , ist GCS als Landeplatz für Exportdaten hilfreich, da es keine Rücksicht auf das Datenschema oder -format nimmt. Da es auch Pub/Sub-Ereignisse auslöst, wenn Daten ankommen, kann es die ereignisbasierten Datenflusssysteme starten.

Hosting von Websites

Du kannst Dateien über HTTP-Endpunkte öffentlich zugänglich machen:. Wenn du also HTML- oder andere von Webbrowsern unterstützte Dateien platzierst, kannst du statische Websites im GCS hosten lassen. Dies kann auch für statische Dateien hilfreich sein, die du in Websites importieren möchtest, z. B. für Zählpixel oder Bilder.

Dropbox

Du kannst bestimmten Nutzern einen öffentlichen oder feiner abgestuften Zugang geben, damit du große Dateien sicher weitergeben kannst. Es werden bis zu 5 TB pro Objekt unterstützt, wobei die gesamte Speicherung unbegrenzt ist (wenn du bereit bist, dafür zu bezahlen!). Das macht ihn zu einem potenziellen Ziel für die Datenverarbeitung, z. B. eine CSV-Datei, die Kollegen zur Verfügung gestellt wird, die sie lokal in Excel importieren möchten.

Die in GCS gespeicherten Objekte werden alle unter ihrer eigenen URI gespeichert, die wie eine HTTP-Adresse (https://example.com) ist, aber ein eigenes Protokoll hat: gs://. Du kannst sie auch unter einer normalen HTTP-Adresse zur Verfügung stellen - du könntest sogar HTML-Dateien hosten und GCS würde als Webhosting dienen.

Die Bucket-Namen, die du verwendest, sind global eindeutig, sodass du von jedem Projekt aus darauf zugreifen kannst, auch wenn sich der Bucket in einem anderen Projekt befindet. Du kannst den öffentlichen Zugriff über HTTP festlegen oder nur bestimmte Benutzer oder Service-E-Mails, die im Namen deiner Datenanwendungen arbeiten. Abbildung 4-4 zeigt ein Beispiel dafür, wie dies über die WebUI aussieht, aber auf die Dateien darin wird normalerweise über Code zugegriffen.

Files sitting within Cloud Storage in its WebUI
Abbildung 4-4. Dateien innerhalb des GCS in der WebUI

Jedem Objekt in GCS sind einige Metadaten zugeordnet, die du nutzen kannst, um es an deine Bedürfnisse bei der Speicherung anzupassen. Wir gehen die in Abbildung 4-5 gezeigte Beispieldatei durch, um zu zeigen, was alles möglich ist.

Various metadata associated with a file upload to Google Cloud Storage
Abbildung 4-5. Verschiedene Metadaten, die mit einem Datei-Upload zum GCS verbunden sind

Zu den Metadaten, die für jedes Objekt im GCS verfügbar sind, gehören:

Typ

Dies ist ein HTTP-MIME-Typ (Multipurpose Internet Mail Extensions), wie er für Web-Objekte festgelegt ist. Auf der Mozilla-Website findest du einige Informationen zu HTTP-MIME-Typen. Es lohnt sich, diese Einstellung vorzunehmen, wenn deine Anwendung anhand dieses Typs prüft, wie die Datei zu behandeln ist. So bedeutet z. B. eine .csv Datei mit dem MIME-Typ text/csv in Abbildung 4-5, dass Anwendungen, die diese Datei herunterladen, versuchen werden, sie als Tabelle zu lesen. Andere gängige MIME-Typen sind JSON (application/JSON), HTML für Webseiten (text/html), Bilder wie image/png und Videos (video/mp4).

Größe

Die Größe der Bytes des Objekts auf der Festplatte. Du kannst bis zu 5 TB pro Objekt speichern.

Erstellt

Wann das Objekt zum ersten Mal erstellt wurde.

Zuletzt geändert

Du kannst Objekte aktualisieren, indem du ihnen denselben Namen gibst, wie du sie beim ersten Mal erstellt hast, und die Objektversionierung aktiviert hast.

Klasse der Speicherung

Das Preismodell, unter dem das Objekt gespeichert wird, wird auf der Bucket-Ebene festgelegt. Die Speicherklassen sind in der Regel ein Kompromiss zwischen den Kosten für die Speicherung und den Zugriffskosten. Die Kosten für die Speicherung variieren je nach Region, aber als Anhaltspunkt hier einige Beispiele für GB pro Monat. Standard ist für Daten, auf die häufig zugegriffen wird ($0,02), Nearline für Daten, auf die nur wenige Male im Jahr zugegriffen wird ($0,01), Coldline für Daten, auf die nur einmal im Jahr oder seltener zugegriffen wird ($0,004), und Archive für Daten, auf die außer im Notfall nie zugegriffen wird ($0,0012). Achte darauf, dass du deine Objekte in die richtige Klasse einordnest, sonst zahlst du am Ende zu viel für den Datenzugriff, weil die Objektkosten für den Zugriff auf Archivdaten höher sind als z. B. für Standard.

Benutzerdefinierte Zeit

Vielleicht hast du wichtige Daten oder Zeiten, die du mit dem Objekt verbinden möchtest und die du hier als Metadaten hinzufügen kannst.

Öffentliche URL

Wenn du dein Objekt öffentlich machen willst, wird die URL hier aufgeführt. Dies unterscheidet sich von der URL für die Authentifizierung.

Authentifizierte URL

Dies ist die URL, wenn du einem Benutzer oder einer Anwendung einen eingeschränkten, nicht öffentlichen Zugang gewährst. Sie prüft die Berechtigung des Benutzers, bevor sie das Objekt bereitstellt.

gstuil URI

Die gs:// Form des Zugriffs auf das Objekt, in der Regel bei der programmatischen Nutzung über die API oder eines der SDKs von GCS.

Erlaubnis

Informationen darüber, wer auf das Objekt zugreifen darf. Heutzutage ist es üblich, die Erlaubnis auf Bucket-Ebene zu erteilen, aber du kannst den Zugriff auf Objekte auch feinkörnig steuern. In der Regel ist es einfacher, zwei getrennte Bereiche für die Zugriffskontrolle zu haben, z. B. öffentlich und eingeschränkt.

Schutz

Es gibt verschiedene Methoden, die du aktivieren kannst, um zu steuern, wie das Objekt bestehen bleibt, die in diesem Abschnitt hervorgehoben werden.

Status halten

Du kannst zeitlich begrenzte oder ereignisbasierte Sperren für das Objekt erzwingen, d.h. es kann nicht gelöscht oder geändert werden, wenn es vorhanden ist, entweder durch ein Zeitlimit oder wenn ein bestimmtes, durch einen API-Aufruf ausgelöstes Ereignis eintritt. Dies kann hilfreich sein, um sich vor versehentlichem Löschen zu schützen, oder wenn du zum Beispiel über eine Aufbewahrungsrichtlinie ein Datenverfallsdatum für den Bucket festgelegt hast, aber bestimmte Objekte von dieser Richtlinie ausnehmen möchtest.

Versionsgeschichte

Du kannst die Versionierung deines Objekts aktivieren, so dass selbst bei einer Änderung die ältere Version noch zugänglich ist. Das kann hilfreich sein, um einen Überblick über die geplanten Daten zu behalten.

Aufbewahrungspolitik

Du kannst verschiedene Regeln aktivieren, die festlegen, wie lange ein Objekt aufbewahrt wird. Das ist wichtig, wenn du mit persönlichen Nutzerdaten arbeitest, um alte Archive zu löschen, wenn du keine Berechtigung mehr hast, diese Daten zu speichern. Außerdem kannst du damit Daten in eine kostengünstigere Speicherung verschieben, wenn auf sie nach einer bestimmten Anzahl von Tagen nicht mehr zugegriffen wird.

Verschlüsselungsart

Standardmäßig verschlüsselt Google alle deine Daten im GCP, aber du möchtest vielleicht eine strengere Sicherheitsrichtlinie durchsetzen, bei der nicht einmal Google die Daten sehen kann. Dazu kannst du deine eigenen Sicherheitsschlüssel verwenden.

GCS ist ein einzigartiger, aber grundlegender Dienst: Er speichert deine Bytes sicher und geschützt. Es ist die Grundlage für viele andere GCP-Dienste, auch wenn sie für den Endnutzer nicht sichtbar sind, und kann diese Aufgabe auch für dich übernehmen. Es ist eine unendliche Festplatte in der Cloud und nicht auf deinem eigenen Computer, auf die du von überall auf der Welt zugreifen kannst.

Wir haben uns nun die drei wichtigsten Arten der Datenspeicherung angesehen: BigQuery für strukturierte SQL-Daten, Firestore für NoSQL-Daten und GCS für unstrukturierte Rohdaten. Jetzt geht es darum, wie du regelmäßig mit ihnen arbeitest, indem wir uns Techniken für die Planung und das Streaming von Datenströmen ansehen. Beginnen wir mit der häufigsten Anwendung, den geplanten Datenströmen.

Zeitplanungsprogramm für Datenimporte

Dieser Abschnitt befasst sich mit einer der wichtigsten Aufgaben für jeden Dateningenieur, der Workflows entwickelt: die Planung von Datenflüssen innerhalb deiner Anwendungen. Wenn dein Proof of Concept funktioniert, ist der nächste Schritt zur Produktionsreife die regelmäßige Aktualisierung der beteiligten Daten. Anstatt jeden Tag eine Tabelle zu aktualisieren oder ein API-Skript auszuführen, kannst du diese Aufgabe den vielen Automatisierungsgeräten überlassen, die dir auf GCP zur Verfügung stehen.

Es gibt viele Möglichkeiten, Daten zu aktualisieren. In diesem Abschnitt wird erläutert, wie du deine GA4-Daten und die dazugehörigen Datensätze verschieben möchtest.

Arten des Datenimports: Streaming versus Zeitplannungsprogramm Batches

Das Streamen von Daten gegenüber dem Stapeln von Daten ist eine der Entscheidungen, die du bei der Entwicklung von Datenanwendungssystemen treffen musst. Dieser Abschnitt befasst sich mit den Vor- und Nachteilen der beiden Varianten.

Streaming-Datenströme erfolgen in Echtzeit und verwenden ereignisbasierte kleine Datenpakete, die kontinuierlich aktualisiert werden. Batched-Daten werden regelmäßig in einem langsameren Intervall, z. B. täglich oder stündlich, mit größeren Datenimporten pro Auftrag geplant.

Die Streaming-Optionen für Daten werden im Abschnitt "Streaming Data Flows" ausführlicher behandelt , aber ein Vergleich mit Stapeldaten kann bei einigen grundlegenden Entscheidungen in der frühen Phase deines Anwendungsdesigns helfen.

Gebündelte Datenflüsse

Die Stapelverarbeitung ist die gängigste und traditionellste Art, Datenströme zu importieren, und für die meisten Anwendungsfälle ist sie vollkommen ausreichend. Eine wichtige Frage bei der Erstellung deines Anwendungsfalls ist, wie schnell du die Daten brauchst. In der Regel soll die erste Reaktion so schnell wie möglich oder nahezu in Echtzeit erfolgen. Aber wenn du dir die Einzelheiten ansiehst, wirst du feststellen, dass die Auswirkungen stündlicher oder sogar täglicher Aktualisierungen im Vergleich zur Echtzeit unbemerkt bleiben. Wenn die Daten, die du aktualisierst, auch in Stapeln vorliegen (z. B. ein nächtlicher CRM-Export), gibt es kaum einen Grund, die nachgelagerten Daten in Echtzeit bereitzustellen. Wie immer solltest du dir den Anwendungsfall ansehen und prüfen, ob er sinnvoll ist. Batched Data Workflows werden unrentabel, wenn du dich nicht darauf verlassen kannst, dass die geplanten Aktualisierungen pünktlich erfolgen. Dann musst du eventuell Ausweichoptionen für den Fall schaffen, dass ein Import fehlschlägt (und du solltest immer für den Fall eines Fehlschlags planen).

Streaming von Datenströmen

Das Streamen von Daten ist heutzutage dank der neuen Technologien in modernen Datenstapeln einfacher zu bewerkstelligen, und es gibt Befürworter, die sagen, dass möglichst alle Datenströme gestreamt werden sollten. Es kann gut sein, dass du neue Anwendungsfälle entdeckst, sobald du dich von den Fesseln der Zeitplanungsprogramme für gestapelte Daten befreist. Auch wenn du nicht unmittelbar Echtzeitdaten benötigst, hat das gewisse Vorteile, denn wenn wir zu einem ereignisbasierten Datenmodell übergehen, reagieren wir, wenn etwas passiert, und nicht erst, wenn ein bestimmter Zeitstempel erreicht ist, was bedeutet, dass wir flexibler sein können, wann Datenströme auftreten. Ein gutes Beispiel dafür sind die GA4 BigQuery Datenexporte, die bei einer Verzögerung die nachgelagerten Dashboards und Anwendungen zerstören würden. Wenn du ereignisbasierte Reaktionen auf die Verfügbarkeit der Daten einrichtest, bekommst du die Daten, sobald sie da sind, und musst nicht auf die Lieferung am nächsten Tag warten. Der größte Nachteil sind die Kosten, denn diese Datenströme sind in der Regel teurer. Deine Dateningenieure müssen außerdem über andere Fähigkeiten verfügen, um Streaming-Pipelines zu entwickeln und Fehler zu beheben.

Bei der Betrachtung von geplanten Aufträgen beginnen wir mit den BigQuery-eigenen Ressourcen und gehen dann zu komplexeren Lösungen wie Cloud Composer, Cloud Scheduler und Cloud Build über.

BigQuery Ansichten

In manchen Fällen ist der einfachste Weg, um umgewandelte Daten zu präsentieren, eine BigQuery-Ansicht einzurichten oder BigQuery SQL zu planen. Dies ist die einfachste Möglichkeit und erfordert keine weiteren Dienste.

BigQuery Views sind keine Tabellen im herkömmlichen Sinne, sondern stellen vielmehr eine Tabelle dar, die sich aus der SQL ergibt, die du zu ihrer Definition verwendest. Das bedeutet, dass du bei der Erstellung deiner SQL dynamische Daten einbeziehen kannst und somit immer die aktuellsten Daten hast. Du könntest zum Beispiel deine GA4 BigQuery Datenexporte mit einer View abfragen, die du wie in Beispiel 4-10erstellt hast - soerhältst du immer die Daten von gestern zurück.

Beispiel 4-10. Dieses SQL kann in einer BigQuery-Ansicht verwendet werden, um immer die Daten von gestern anzuzeigen (angepasst an Beispiel 3-6)
SELECT
  -- event_date (the date on which the event was logged)
  parse_date('%Y%m%d',event_date) as event_date,
  -- event_timestamp (in microseconds, utc)
  timestamp_micros(event_timestamp) as event_timestamp,
  -- event_name (the name of the event)
  event_name,
  -- event_key (the event parameter's key)
  (SELECT key FROM UNNEST(event_params)
   WHERE key = 'page_location') as event_key,
  -- event_string_value (the string value of the event parameter)
  (SELECT value.string_value FROM UNNEST(event_params)
   WHERE key = 'page_location') as event_string_value
FROM
  -- your GA4 exports - change to your location
  `learning-ga4.analytics_250021309.events_*`
WHERE
  -- limits query to use table from yesterday only
  _TABLE_SUFFIX = FORMAT_DATE('%Y%m%d',date_sub(current_date(), INTERVAL 1 day))
  -- limits query to only show this event
  and event_name = 'page_view'

Die Schlüsselzeile ist FORMAT_DATE('%Y%m%d',date_sub(current_date(), INTERVAL 1 day)), die yesterday zurückgibt, was den Vorteil der Spalte _TABLE_SUFFIX nutzt, die BigQuery als Metainformation über die Tabelle hinzufügt, damit du leichter mehrere Tabellen abfragen kannst.

BigQuery Views haben ihren Platz, aber sei vorsichtig bei ihrer Verwendung. Da das SQL der View unterhalb aller anderen Abfragen läuft, kann es zu teuren oder langsamen Ergebnissen kommen. Dies wurde kürzlich mit Materialized Views entschärft, einer Technologie, die sicherstellt, dass du nicht die gesamte Tabelle abfragst, wenn du Abfragen über Views machst. In manchen Fällen ist es besser, eine eigene Zwischentabelle zu erstellen, vielleicht mit Hilfe eines Zeitplannungsprogramms, das wir im nächsten Abschnitt behandeln.

BigQuery Geplante Abfragen

BigQuery bietet native Unterstützung für die Planung von Abfragen, die du über die Menüleiste oben links oder durch Auswahl von "Planen" beim Erstellen der Abfrage aufrufen kannst. Für kleine Aufträge und Importe ist das in Ordnung; ich würde jedoch davor warnen, sich darauf zu verlassen, wenn es sich nicht um einfache Transformationen in einem Schritt handelt. Sobald es um kompliziertere Datenflüsse geht, ist es einfacher, spezielle Tools für diese Aufgabe zu verwenden, sowohl aus Sicht der Verwaltung als auch der Robustheit.

Geplante Abfragen sind an die Benutzerauthentifizierung gebunden, die sie einrichtet. Wenn diese Person also geht, muss das Zeitplannungsprogramm über den gcloud-Befehl bq update --transfer-config --update-credentials aktualisiert werden. Nutze dies vielleicht, um deine Verbindung zu Dienstkonten zu aktualisieren, die nicht an eine Person gebunden sind. Außerdem hast du nur die Oberfläche des Zeitplannungsprogramms BigQuery zur Verfügung, um die Abfragen zu steuern - bei großen, komplizierten Abfragen, die du ändern willst, wird es schwierig, einen Änderungsverlauf oder eine Übersicht zu sehen.

Aber für einfache, nicht geschäftskritische Abfragen, die vielleicht nur für eine begrenzte Anzahl von Personen benötigt werden, ist sie schnell und einfach in der Oberfläche selbst einzurichten und eignet sich besser als Views, z. B. für den Export in Dashboard-Lösungen wie Looker oder Data Studio. Wie in Abbildung 4-6 zu sehen ist, kannst du, sobald du deine SQL-Abfrage entwickelt hast und dir die Ergebnisse gefallen, auf die Schaltfläche "Planen" klicken und hast die Daten am nächsten Tag zur Verfügung, wenn du dich einloggst.

Setting up the query into a scheduled query
Abbildung 4-6. Einrichten einer geplanten Abfrage aus Beispiel 4-10, die möglicherweise besser funktioniert als die Erstellung einer BigQuery-Ansicht mit denselben Daten zur Verwendung in Dashboards usw.

Wenn du jedoch anfängst, Fragen zu stellen wie "Wie kann ich diese geplante Abfrage robuster machen?" oder "Wie kann ich Abfragen auf der Grundlage der Daten, die ich in dieser Tabelle erstelle, auslösen?", ist das ein Zeichen dafür, dass du eine robustere Lösung für die Planung brauchst. Das Tool für diese Aufgabe ist Airflow, und zwar über seine gehostete Version auf GCP namens Cloud Composer, über die wir im nächsten Abschnitt sprechen.

Cloud Composer

Cloud Composer ist eine von Google verwaltete Lösung für Airflow, ein beliebtes Open-Source-Zeitplanungsprogramm. Es kostet rund 300 US-Dollar pro Monat und lohnt sich daher nur, wenn du einen guten Geschäftswert hast, der es rechtfertigt. Aber es ist die Lösung, der ich am meisten vertraue, wenn es um komplizierte Datenflüsse über mehrere Systeme hinweg geht und die Backfilling, Warnsysteme und Konfiguration über Python bietet. Für viele Unternehmen ist es das Rückgrat all ihrer Aufträge, die sie planen.

Hinweis

Ich werde in diesem Buch den Namen Cloud Composer verwenden, da verwaltetes Airflow innerhalb von GCP so genannt wird, aber ein großer Teil des Inhalts wird auch für Airflow auf anderen Plattformen anwendbar sein, z. B. bei anderen Cloud-Providern oder selbst gehostetenPlattformen.

Ich habe angefangen, Cloud Composer zu nutzen, als ich Aufträge hatte, die die folgenden Kriterien erfüllten:

Multilevel-Abhängigkeiten

Sobald du in deinen Datenpipelines eine Situation hast, in der ein geplanter Auftrag von einem anderen abhängt, würde ich den Cloud Composer einsetzen, da er gut in die Struktur des gerichteten azyklischen Graphen (DAG) passt. Beispiele dafür sind Ketten von SQL-Aufträgen: ein SQL-Skript, um die Daten zu bereinigen, ein anderes SQL-Skript, um deine Modelldaten zu erstellen. Wenn du diese SQL-Skripte im Cloud Composer ausführst, kannst du deine geplanten Aufträge in kleinere, einfachere Komponenten aufteilen, als wenn du versuchen würdest, sie alle in einem einzigen großen Auftrag auszuführen. Sobald du die Freiheit hast, Abhängigkeiten festzulegen, empfehle ich dir, die Pipelines zu verbessern, indem du Prüfungen und Validierungsschritte hinzufügst, die in einem einzelnen geplanten Auftrag zu komplex wären.

Verfüllt

Es ist üblich, zu Beginn des Projekts einen historischen Import einzurichten, um alle Daten aufzufüllen, die du gehabt hättest, wenn der Zeitplanauftrag z.B. die letzten 12 Monate gelaufen wäre. Was zur Verfügung steht, ist von Auftrag zu Auftrag unterschiedlich, aber wenn du Importe pro Tag eingerichtet hast, kann es manchmal nicht trivial sein, historische Importe einzurichten. Der Cloud Composer führt Aufträge als Simulation des Tages aus, und du kannst ein beliebiges Startdatum festlegen, so dass er alle Daten langsam wieder auffüllt, wenn du ihn lässt.

Mehrere Interaktionssysteme

Wenn du Daten in mehrere Systeme wie FTP, Cloud-Produkte, SQL-Datenbanken und APIs einspeist oder an sie sendest, wird die Koordinierung zwischen diesen Systemen komplex und erfordert möglicherweise die Verteilung auf verschiedene Importskripte. Dank der vielen Konnektoren, die der Cloud Composer über seine Operatoren und Hooks bereitstellt, kann er sich mit praktisch allen Systemen verbinden, so dass du sie alle von einem Ort aus verwalten kannst, was die Wartung erheblich erleichtert.

Wiederholungen

Wenn du über HTTP importierst, wird es in vielen Fällen zu Ausfällen kommen ( ). Es kann schwierig sein, zu konfigurieren, wann und wie oft diese Importe oder Exporte wiederholt werden sollen. Der Cloud Composer hilft dir dabei mit seinem konfigurierbaren Wiederholungssystem, das jede Aufgabe kontrolliert.

Wenn du mit Datenflüssen arbeitest, wirst du schnell auf Probleme wie die genannten stoßen und brauchst eine Möglichkeit, sie einfach zu lösen. Es gibt ähnliche Lösungen, aber Cloud Composer ist diejenige, die ich am häufigsten verwendet habe und die schnell zum Rückgrat vieler Datenprojekte geworden ist. Um sich komplizierte Prozesse vorstellen zu können, ist es wichtig, diese Abläufe intuitiv darzustellen, was Cloud Composer mit einer Darstellung löst, über die wir im Folgenden sprechen.

DAGs

Die zentrale Funktion des Cloud Composers sind DAGs, die den Fluss deiner Daten darstellen, während sie aufgenommen, verarbeitet und extrahiert werden. Der Name bezieht sich auf eine Struktur aus Knoten und Kanten, wobei die Richtungen zwischen diesen Knoten durch Pfeile angegeben werden. Ein Beispiel dafür, was das für deine eigene GA4-Pipeline bedeuten könnte, findest du in Abbildung 4-7.

An example of an Airflow DAG.
Abbildung 4-7. Ein Beispiel für eine DAG, die in einem GA4-Prozess verwendet werden könnte

Die Knoten stellen eine Datenoperation dar, wobei die Kanten die Reihenfolge der Ereignisse und die Abhängigkeit der Operationen voneinander zeigen. Eine der wichtigsten Funktionen von Airflow ist, dass es für den Fall, dass ein Knoten fehlschlägt (was letztendlich alle tun), Strategien gibt, um entweder zu warten, es erneut zu versuchen oder nachgelagerte Operationen zu überspringen. Airflow verfügt außerdem über einige Backfill-Funktionen, die eine Menge Kopfzerbrechen bei der Durchführung historischer Aktualisierungen verhindern können, und bietet einige vordefinierte Makros, mit denen du z. B. das heutige Datum dynamisch in deine Skripte einfügen kannst.

Ein Beispiel für eine DAG, die aus deinen GA4 BigQuery-Exporten importiert wird, findest du in Beispiel 4-11.

Beispiel 4-11. Ein Beispiel-DAG, der deinen GA4-Export nimmt und ihn mit Hilfe von SQL aggregiert, das du zuvor entwickelt hast und in einerga4-bigquery.sql-Datei, die mit deinem Skript hochgeladen wurde
from airflow.contrib.operators.bigquery_operator import BigQueryOperator
from airflow.contrib.operators.bigquery_check_operator import BigQueryCheckOperator
from airflow.operators.dummy_operator import DummyOperator
from airflow import DAG
from airflow.utils.dates import days_ago
import datetime

VERSION = '0.1.7' # increment this each version of the DAG

DAG_NAME = 'ga4-transformation-' + VERSION

default_args = {
  'start_date': days_ago(1), # change this to a fixed date for backfilling
  'email_on_failure': True,
  'email': 'mark@example.com',
  'email_on_retry': False,
  'depends_on_past': False,
  'retries': 3,
  'retry_delay': datetime.timedelta(minutes=10),
  'project_id': 'learning-ga4',
  'execution_timeout': datetime.timedelta(minutes=60)
}

schedule_interval = '2 4 * * *' # min, hour, day of month, month, day of week

dag = DAG(DAG_NAME, default_args=default_args, schedule_interval=schedule_interval)


start = DummyOperator(
  task_id='start',
  dag=dag
)

# uses the Airflow macro {{ ds_nodash }} to insert todays date in YYYYMMDD form
check_table = BigQueryCheckOperator(
  task_id='check_table',
  dag=dag,
  sql='''
  SELECT count(1) > 5000
  FROM `learning-ga4.analytics_250021309.events_{{ ds_nodash }}`"
  '''
)

checked = DummyOperator(
  task_id='checked',
  dag=dag
)

# a function so you can loop over many tables, SQL files
def make_bq(table_id):

  task = BigQueryOperator(
    task_id='make_bq_'+table_id,
    write_disposition='WRITE_TRUNCATE',
    create_disposition='CREATE_IF_NEEDED',
    destination_dataset_table=
        'learning_ga4.ga4_aggregations.{}${{ ds_nodash}}'.format(table_id),
    sql='./ga4_sql/{}.sql'.format(table_id),
    use_legacy_sql=False,
    dag=dag
  )

  return task

ga_tables = [
 'pageview-aggs',
 'ga4-join-crm',
 'ecom-fields'
]

ga_aggregations = [] # helpful if you are doing other downstream transformations
for table in ga_tables:
 task = make_bq(table)
 checked >> task
 ga_aggregations.append(task)


# create the DAG
start >> check_table >> checked

Um die Knotenpunkte für deine DAG zu erstellen, verwendest du Airflow Operators. Das sind verschiedene vorgefertigte Funktionen, die sich mit einer Vielzahl von Anwendungen verbinden lassen, darunter eine umfangreiche Palette von GCP-Diensten wie BigQuery, FTP, Kubernetes-Cluster und so weiter.

Für das Beispiel in Beispiel 4-11 werden die Knoten von erstellt:

Start

Eine DummyOperator(), um den Beginn der DAG zu markieren.

check_table

Eine BigQueryCheckOperator(), die überprüft, ob du an diesem Tag Daten in der GA4-Tabelle hast. Wenn dies fehlschlägt, indem FALSE für das angezeigte Inline-SQL zurückgegeben wird, schlägt Airflow die Aufgabe fehl und versucht sie alle 10 Minuten bis zu dreimal erneut. Du kannst dies an deine Erwartungen anpassen.

geprüft

Ein weiteres DummyOperator(), um zu signalisieren, dass die Tabelle geprüft wurde.

make_bq

Dadurch wird eine partitionierte Tabelle mit demselben Namen wie die task_id erstellt oder hinzugefügt. Das SQL, das ausgeführt wird, sollte ebenfalls den gleichen Namen haben und in dem mit der DAG hochgeladenen SQL-Ordner unter ./ga4_sql/ verfügbar sein, z.B. ./ga4_sql/pageview-aggs.sql. Sie ist so funktionalisiert, dass du eine Schleife über tableIds ziehen kannst, um den Code effizienter zu gestalten.

Die Kanten werden über bitweise Python-Operatoren am Ende des Tags und innerhalb der Schleifen behandelt, z. B. start >> check_table >> checked.

Die resultierende DAG siehst du in Abbildung 4-8. Nimm dieses Beispiel als Grundlage, die du für deine eigenen Arbeitsabläufe erweitern kannst.

An example of the DAG created in Airflow
Abbildung 4-8. Ein Beispiel für den DAG, der in Airflow durch den Code in Beispiel 4-11 erstellt wurde; um für weitere Transformationen zu skalieren, füge weitere SQL-Dateien zum Ordner hinzu und füge den Namen der Tabelle zur Liste ga_tables hinzu

Tipps zur Verwendung von Airflow/Cloud Composer

Die allgemeinen Hilfedateien sind hervorragend für geeignet, um zu lernen, wie man den Cloud Composer benutzt, aber im Folgenden findest du einige Tipps, die ich bei der Verwendung in Data-Science-Projekten erhalten habe:

Verwende Airflow nur für die Zeitplanung

Nutze das richtige Tool für die richtige Aufgabe - Airflow ist für die Zeitplanung und die Verbindung zu Datenspeicherungssystemen zuständig. Ich habe den Fehler gemacht, die Python-Bibliotheken zu verwenden, um die Daten zwischen den Planungsschritten ein wenig zu massieren, und geriet dabei in eine Python-Abhängigkeitshölle, die alle laufenden Aufgaben beeinträchtigte. Ich verwende lieber Docker-Container für jeden Code, der ausgeführt werden muss, und nutze stattdessen GKEPodOperator(), um diesen Code in einer kontrollierten Umgebung auszuführen.

Schreibe Funktionen für deine DAGS

Es ist viel übersichtlicher, Funktionen zu erstellen, die DAGs ausgeben, anstatt die Aufgaben jedes Mal neu zu schreiben. Das bedeutet auch, dass du sie in einer Schleife durchgehen und Abhängigkeiten für viele Datensätze auf einmal erstellen kannst, ohne Code einfügen zu müssen.

Dummy-Operatoren als Wegweiser verwenden

Die DAGs sehen beeindruckend aus, können aber auch verwirrend sein. Ein paar praktische Wegweiser entlang der Linie können dir zeigen, wo du fehlerhafte DAG-Läufe stoppen und starten kannst. Die Möglichkeit, alle nachgelagerten Daten aus dem Schild "Daten alle geladen" zu löschen, macht deutlich, was passieren wird. Weitere hilfreiche Funktionen sind Aufgabengruppen und Beschriftungen, mit denen du Metainformationen darüber anzeigen kannst, was deine DAG gerade tut.

Trenne deine SQL-Dateien

Du musst keine riesigen SQL-Strings für deine Operatoren schreiben. Stattdessen kannst du sie in .sql-Dateien speichern und dann die Datei mit dem SQL aufrufen. Das macht es viel einfacher, Änderungen zu verfolgen und im Auge zu behalten.

Version deiner DAG-Namen

Ich finde es auch hilfreich, die Version des DAG-Namens zu erhöhen, wenn du Änderungen und Aktualisierungen vornimmst. Airflow kann etwas langsam sein, wenn es darum geht, neue Aktualisierungen von Dateien zu erkennen. Wenn du also den Versionsnamen in der DAG hast, kannst du sicher sein, dass du immer mit der neuesten Version arbeitest.

Cloud Build für die Bereitstellung von DAGs einrichten

Wenn du deinen DAG-Code und deine Dateien jedes Mal hochladen musst, hält dich das davon ab, Änderungen vorzunehmen. Deshalb ist es viel einfacher, wenn du eine Cloud Build-Pipeline einrichtest, die deine DAGs bei jedem Commit auf GitHub bereitstellt.

Das war eine kleine Tour durch die Funktionen des Cloud Composers, aber es gibt noch viel mehr und ich empfehle die Airflow-Website, um mehr über die Optionen zu erfahren. Airflow ist ein schwergewichtiges Zeitplannungsprogramm, und es gibt noch eine weitere, viel leichtere Option, den Google Cloud Scheduler, den wir uns als Nächstes ansehen werden.

Zeitplanungsprogramm für die Cloud

Wenn du etwas leichteres als den Cloud Composer suchst, dann ist der Cloud Scheduler ein einfaches Zeitplanungsprogramm, mit dem du HTTP-Endpunkte auslösen kannst. Für einfache Aufgaben, die nicht die Komplexität der vom Cloud Composer unterstützten Datenflüsse benötigen, funktioniert er einfach.

Ich ordne ihn von den Fähigkeiten her irgendwo zwischen Cloud Composer und BigQuery-Zeitplanungsprogrammen ein, da der Cloud Scheduler nicht nur BigQuery-Abfragen, sondern auch jeden anderen GCP-Dienst ausführen kann, was sehr praktisch sein kann.

Dazu ist etwas zusätzliche Arbeit nötig , um das Pub/Sub-Thema und die Cloud-Funktion zu erstellen, die den BigQuery-Auftrag erzeugt. Wenn es nur um BigQuery geht, ist das vielleicht nicht nötig, aber wenn andere GCP-Dienste involviert sind, kann es auf lange Sicht besser sein, den Ort für die Zeitplanung zu zentralisieren. Ein Beispiel für die Einrichtung eines Pub/Sub-Themas findest du unter "Pub/Sub"; der einzige Unterschied besteht darin, dass du dann über das Zeitplanungsprogramm der Cloud ein Ereignis für dieses Thema planst. In Abbildung 4-9 siehst du einige Beispiele aus meinem eigenen GCP, die das Folgende zeigen:

Pakettest-Build

Ein wöchentlicher Zeitplan zum Auslösen eines API-Aufrufs zur Ausführung eines Cloud Builds

Slackbot-Zeitplan

Ein wöchentlicher Zeitplan, der einen HTTP-Endpunkt trifft, der einen Slackbot auslöst

Ziel_Pub/Sub_scheduler

Ein Tagesplan zum Auslösen eines Pub/Sub-Themas

Some Cloud Schedulers I have enabled for some tasks within my own Google Cloud Project
Abbildung 4-9. Einige Cloud-Zeitpläne, die ich für einige Aufgaben in meinem eigenen GCP aktiviert habe

Der Cloud Scheduler kann auch andere Dienste wie Cloud Run oder Cloud Build auslösen. Eine besonders leistungsstarke Kombination ist Cloud Scheduler und Cloud Build (siehe nächster Abschnitt, "Cloud Build"). Da Cloud Build langlaufende Aufgaben ausführen kann, hast du eine einfache Möglichkeit, ein serverloses System zu erstellen, das jeden Auftrag auf GCP ausführen kann, und zwar ereignisgesteuert, aber mit einem Zeitplannungsprogramm obendrein.

Cloud Build

Cloud Build ist ein leistungsfähiges Tool das man für Daten-Workflows in Betracht ziehen sollte, und es ist wahrscheinlich das Tool, das ich jeden Tag am meisten benutze (sogar mehr als BigQuery!). Cloud Build wurde auch im Abschnitt über die Dateneingabe in "Einrichten von Cloud Build CI/CD mit GitHub" vorgestellt , aber wir gehen hier noch weiter ins Detail.

Cloud Build wird als CI/CD-Tool eingestuft, was eine beliebte Strategie im modernen Datenbetrieb ist. Es schreibt vor, dass die Freigabe des Codes für die Produktion nicht am Ende massiver Entwicklungszeiten erfolgen sollte, sondern ständig mit kleinen Aktualisierungen, mit automatischen Test- und Bereitstellungsfunktionen, damit etwaige Fehler schnell entdeckt und rückgängig gemacht werden können. Das sind generell gute Praktiken, und ich empfehle dir, dich darüber zu informieren, wie du sie befolgen kannst. Cloud Build kann auch als allgemeiner Weg betrachtet werden, um jeden Code auf einem Compute-Cluster als Reaktion auf Ereignisse auszulösen. In erster Linie geht es darum, Code an ein Git-Repository wie GitHub zu übergeben, aber diese Ereignisse können auch sein, wenn eine Datei im GCS ankommt, eine Pub/Sub-Nachricht gesendet wird oder ein Zeitplannungsprogramm den Endpunkt anpingt.

Bei Cloud Build definierst du die Abfolge der Ereignisse, ähnlich wie bei einem Airflow DAG, aber mit einer einfacheren Struktur. Für jeden Schritt definierst du eine Docker-Umgebung, in der dein Code ausgeführt wird. Die Ergebnisse dieses Codes können an die nachfolgenden Schritte weitergegeben oder auf GCS archiviert werden. Da es mit jedem Docker-Container funktioniert, kannst du eine Vielzahl verschiedener Code-Umgebungen auf denselben Daten ausführen. Ein Schritt könnte zum Beispiel Python sein, um von einer API zu lesen, dann R, um sie zu parsen, und dann Go, um das Ergebnis an eine andere Stelle zu senden.

Ursprünglich wurde mir Cloud Build als Möglichkeit vorgestellt, Docker-Container auf GCP zu bauen. Du legst dein Dockerfile in einem GitHub-Repository ab und gibst es ab. Dadurch wird ein Auftrag ausgelöst, der Docker serverlos baut, und nicht wie sonst auf deinem eigenen Computer. Das ist die einzige Art, wie ich heutzutage Docker-Container baue, denn sie lokal zu bauen, kostet Zeit und eine Menge Festplattenplatz. In der Cloud zu bauen bedeutet in der Regel, dass du den Code festlegst, eine Tasse Tee trinkst und in 10 Minuten zurückkommst, um die Protokolle zu prüfen.

Cloud Build wurde jetzt auf erweitert, um nicht nur Dockerdateien, sondern auch eine eigene YAML-Konfigurationssyntax (cloudbuild.yaml) sowie Buildpacks zu erstellen. Das erweitert den Nutzen von Cloud Build enorm, denn mit denselben Aktionen (Git-Commit, Pub/Sub-Ereignis oder Zeitplan) kannst du Aufträge für eine ganze Reihe nützlicher Aufgaben auslösen, nicht nur für Docker-Container, sondern auch für jeden beliebigen Code, den du benötigst.

Ich habe die Lektionen, die ich aus der Arbeit mit Cloud Build und dem HTTP-Docker-Äquivalent Cloud Run und Cloud Scheduler gelernt habe, in mein R-Paket destilliert googleCloudRunnerDas ist das Tool, mit dem ich die meisten meiner Data-Engineering-Aufgaben für GA4 und andere Aufgaben auf GCP ausführe. Cloud Build verwendet Docker-Container, um alles auszuführen. Ich kann fast jede Sprache/jedes Programm oder jede Anwendung ausführen, auch R. Wenn ich eine einfache Möglichkeit habe, diese Builds von R aus zu erstellen und auszulösen, kann R als UI oder Gateway für jedes andere Programm dienen, z. B. kann R mit gcloud einen Cloud Build auslösen, um Cloud Run-Anwendungen bereitzustellen.

Cloud Build Konfigurationen

Zur Einführung: Eine Cloud-Build-YAML-Datei sieht so aus wie die in Beispiel 4-12 gezeigte. Das Beispiel zeigt, wie drei verschiedene Docker-Container innerhalb desselben Builds verwendet werden können, die unterschiedliche Dinge tun, aber mit denselben Daten arbeiten.

Beispiel 4-12. Ein Beispiel für einecloudbuild.yaml-Datei, die zum Erstellen von Builds verwendet wird. Jeder Schritt erfolgt nacheinander. Das Feld name enthält ein Docker-Image, das den im Feld args angegebenen Befehl ausführt.
steps:
- name: 'gcr.io/cloud-builders/docker'
 id: Docker Version
 args: ["version"]
- name: 'alpine'
 id: Hello Cloud Build
 args: ["echo", "Hello Cloud Build"]
- name: 'rocker/r-base'
 id: Hello R
 args: ["R", "-e", "paste0('1 + 1 = ', 1+1)"]

Du übermittelst diesen Build dann über die GCP-Webkonsole, gcloud oder googleCloudRunner, oder auf andere Weise über die Cloud Build API. Die gcloud Version ist gcloud builds submit --config cloudbuild.yaml --no-source. Dadurch wird ein Build in der Konsole ausgelöst, den du über die Logs oder auf andere Weise verfolgen kannst - siehe Abbildung 4-10 mit einem Beispiel für die googleCloudRunner Paketprüfungen:

A Cloud Build that has successfully built within the Google Cloud Console.
Abbildung 4-10. Ein Cloud Build, der erfolgreich in der Google Cloud Console erstellt wurde

In "Einrichten von Cloud Build CI/CD mit GitHub"haben wir bereits gesehen, wie Cloud Build zur Bereitstellung einer Cloud Function verwendet wird - diesesBeispiel wird in Beispiel 4-13 wiederholt. Für die Bereitstellung der Cloud Function aus Beispiel 3-9 ist nur ein Schritt erforderlich.

Beispiel 4-13. Cloud Build YAML für den Einsatz einer Cloud-Funktion aus Beispiel 3-9
steps:
- name: gcr.io/cloud-builders/gcloud
 args: ['functions',
     'deploy',
     'gcs_to_bq',
     '--runtime=python39',
     '--region=europe-west1',
     '--trigger-resource=marks-crm-imports-2021',
     '--trigger-event=google.storage.object.finalize']

Builds können manuell ausgelöst werden, aber oft möchtest du, dass dies ein automatischer Prozess ist, der die CI-Philosophie einbezieht. Für diese Fälle verwendest du Build Triggers.

Auslöser bauen

Build Triggers ist eine Konfiguration, die entscheidet wann dein Cloud Build ausgelöst wird. Du kannst Build Triggers so einrichten, dass sie auf Git-Pushes, Pub/Sub-Ereignisse oder Webhooks nur dann reagieren, wenn du sie manuell in der Konsole auslöst. Der Build kann in einer Datei oder inline in der Build-Trigger-Konfiguration angegeben werden. Wie du Build Triggers einrichtest, haben wir bereits im Abschnitt "Einrichten der GitHub-Verbindung zu Cloud Build" beschrieben , daher findest du dort einen Überblick.

Wir haben Cloud Build im Allgemeinen behandelt, aber jetzt kommen wir zu einem konkreten Beispiel für GA4.

GA4-Anwendungen für Cloud Build

Im Allgemeinen verteile ich den gesamten Code für die Arbeit mit GA4-Daten über Cloud Build, da er mit dem GitHub-Repository verknüpft ist, in dem ich meinen Code ablege, wenn ich nicht in der GA4-Schnittstelle arbeite. Dazu gehören Airflow-DAGs, Cloud-Funktionen, BigQuery-Tabellen usw. über verschiedene Cloud-Build-Schritte, die gcloud, meine R-Bibliotheken oder auf andere Weise aufrufen.

Bei der Verarbeitung der Standard-GA4-BigQuery-Exporte erstellt Cloud Logging einen Eintrag, der anzeigt, wann diese Tabellen bereit sind, und den du dann zur Erstellung einer Pub/Sub-Nachricht verwenden kannst. Diese kann einen ereignisgesteuerten Datenfluss auslösen, z. B. den Aufruf eines Airflow DAG oder die Ausführung von SQL-Abfragen.

Im folgenden Beispiel erstellen wir einen Cloud Build, der von einem Pub/Sub-Topic ausgelöst wird, sobald deine GA4 BigQuery-Exporte vorliegen. In "Einrichten eines Pub/Sub-Topics für GA4 BigQuery-Exporte" haben wir ein Pub/Sub-Topic namens "ga4-bigquery" erstellt, das jedes Mal ausgelöst wird, wenn die Exporte bereit sind. Wir werden diese Nachricht nun über einen Cloud Build konsumieren.

Erstelle einen Build-Trigger, der auf die Pub/Sub-Nachricht reagiert. Ein Beispiel findest du in Abbildung 4-11. Für diese Demonstration wird eine cloudbuild.yml-Datei gelesen, die sich im code-examples GitHub-Repository befindet. Dieses Repo enthält die Arbeit, die du für den BigQuery-Export an diesem Tag erledigen möchtest.

Setting up a Build Trigger that will build once the BigQuery export for GA4 is complete
Abbildung 4-11. Einrichten eines Build-Triggers, der erstellt wird, sobald der BigQuery-Export für GA4 abgeschlossen ist

Jetzt brauchen wir den Build, den der Build-Trigger auslöst, wenn er die Pub/Sub-Meldung erhält. Wir passen das Beispiel aus Beispiel 4-10 an und schreiben es in eine SQL-Datei. Diese wird in den GitHub-Quellcode übertragen, den der Build Trigger vor der Ausführung klonen wird. So kannst du die SQL-Datei leicht anpassen, indem du sie auf GitHub einstellst.

Beispiel 4-14. Der Build, den der Build-Trigger ausführt, wenn er das Pub/Sub-Ereignis von der GA4 BigQuery-Exportvervollständigung erhält; die SQL aus Beispiel 4-10 wird in eine separate Datei namensga4-agg.sql hochgeladen
steps:
- name: 'gcr.io/cloud-builders/gcloud'
 entrypoint: 'bash'
 dir: 'your/dir/on/Git'
 args: ['-c',
     'bq --location=eu \
     --project_id=$PROJECT_ID query \
     --use_legacy_sql=false \ --destination_table=tidydata.ga4_pageviews \
     < ./ga4-agg.sql']

Um Beispiel 4-14 erfolgreich auszuführen, müssen die Benutzerrechte so angepasst werden, dass ein autorisierter Benutzer die Abfrage durchführen kann. Dies wird nicht dein eigener Benutzer sein, da der Auftrag in deinem Namen vom Cloud Build Service Agent ausgeführt wird. In deinen Cloud Build-Einstellungen oder in der Google-Konsole findest du den Service-Benutzer, der die Befehle innerhalb des Cloud Builds ausführt. Er sieht etwa so aus: 123456789@cloudbuild.gserviceaccount.com. Du kannst diesen Benutzer verwenden, , oder du kannst ein eigenes Dienstkonto mit Cloud Build-Berechtigungen erstellen. Dieser Benutzer muss als BigQuery-Admin hinzugefügt werden, damit er Abfragen und andere BigQuery-Aufgaben, wie z. B. das Erstellen von Tabellen, ausführen kann, die du später vielleicht benötigst. Siehe Abbildung 4-12.

Für deine eigenen Anwendungsfälle musst du das SQL anpassen und vielleicht weitere Schritte hinzufügen, um mit den Daten zu arbeiten, sobald dieser Schritt abgeschlossen ist. Du siehst, dass Cloud Build eine ähnliche Funktion wie Cloud Composer hat, aber auf einfachere Weise. Es ist allgemeiner als das Planen von Abfragen in BigQuery, aber nicht so teuer oder funktionsreich wie Cloud Composer; ich finde, es ist ein gutes Werkzeug für einfache Aufgaben, die geplant oder ereignisgesteuert werden müssen.

Adding to the Cloud Build service account the permissions to execute BigQuery jobs
Abbildung 4-12. Hinzufügen der Berechtigungen zur Ausführung von BigQuery-Aufträgen zum Cloud Build Service-Konto

Cloud Build-Integrationen für CI/CD

Cloud Build kann über Zeitpläne, manuelle Aufrufe und Ereignisse ausgelöst werden. Die Ereignisse umfassen Pub/Sub- und GitHub-Commits, die für seine Rolle als CI/CD-Tool entscheidend sind. Generell ist es ratsam, beim Programmieren eine Versionskontrolle wie Git zu verwenden. Ich verwende GitHub, die beliebteste Version. Auf diese Weise kannst du alles, was du tust, aufzeichnen und hast außerdem eine unendliche Rückgängig-Möglichkeit, um Änderungen rückgängig zu machen - und wenn der Unterschied zwischen Erfolg und Misserfolg eine . an der falschen Stelle sein kann, dann ist das wünschenswert!

Sobald du Git für die Versionskontrolle verwendest, kannst du es auch für andere Zwecke nutzen, z.B. um Builds von jedem Commit auszulösen, um deinen Code zu überprüfen (Tests), um sicherzustellen, dass er den Stilrichtlinien entspricht (Linting), oder um tatsächliche Builds des Produkts auszulösen, das der Code erzeugt.

Cloud Build erlaubt jede Code-Sprache innerhalb von über die Verwendung von Docker-Containern, die zur Steuerung der Umgebungen für jeden Schritt verwendet werden. Es bietet auch eine einfache Authentifizierung für Google Cloud Services über gcloud auth, wie wir in Abbildung 4-12 gesehen haben, als wir es für BigQuery-Aufgaben eingerichtet haben. Die gcloud Befehle, die du für die Bereitstellung von Diensten verwendest, können auch in Cloud Build verwendet werden, um diese Bereitstellungen zu automatisieren. Außerdem kann die gesamte Ausführung auf dem Code basieren, den du in das Git-Repository einträgst, sodass du einen perfekten Überblick darüber hast, was wann passiert.

Wir können zum Beispiel DAGs in Airflow bereitstellen, wie in Beispiel 4-11 gezeigt wurde. Normalerweise musst du die Python-Datei in einen speziellen Ordner in der Cloud Composer-Umgebung kopieren, um deinen DAG bereitzustellen. Mit Cloud Build kannst du stattdessen gsutil (das GCS-Kommandozeilen-Tool) verwenden. Das fördert eine schnellere Entwicklung und gibt dir etwas Zeit zurück, um dich auf die wichtigen Dinge zu konzentrieren. Ein Beispiel für die Cloudbuild-Datei für deinen Trigger findest du in Beispiel 4-15.

Beispiel 4-15. Du kannst Python-DAGs für Airflow/Cloud Composer mit Cloud Build direkt aus deinem Git-Repository bereitstellen - hier ist $_AIRFLOW_BUCKET eine Ersatzvariable, die du in den Speicherort deiner Installation änderst, und die.sql-Dateien werden in einem Ordner namenssql am selben Ort angenommen
steps:
- name: gcr.io/google.com/cloudsdktool/cloud-sdk:alpine
 id: deploy dag
 entrypoint: 'gsutil'
 args: ['mv',
     'dags/ga4-aggregation.py',
     '$_AIRFLOW_BUCKET/dags/ga4-aggregation.py']
- name: gcr.io/google.com/cloudsdktool/cloud-sdk:alpine
 id: remove old SQL
 entrypoint: 'gsutil'
 args: ['rm',
     '-R',
     '${_AIRFLOW_BUCKET}/dags/sql']
- name: gcr.io/google.com/cloudsdktool/cloud-sdk:alpine
 entrypoint: 'gsutil'
 id: add new SQL
 args: ['cp',
     '-R',
     'dags/sql',
     '${_AIRFLOW_BUCKET}/dags/sql']

Ähnlich wie im vorherigen Beispiel für Cloud Composer kann Cloud Build auch für die Bereitstellung aller anderen GCP-Dienste verwendet werden. Wir verwenden es in Beispiel 3-15 erneut für die Bereitstellung von Cloud Functions, aber jeder Dienst, der einen gcloud-Befehl verwendet, kann automatisiert werden.

Zeitplannungsprogramme sind in der Regel das Herzstück aller Datenanwendungen, einschließlich derjenigen, die GA4 verwenden. Wir haben uns einige Optionen für die Zeitplanung angeschaut, darunter BigQuery-Zeitplanungsprogramme, Cloud Scheduler, Cloud Build und Cloud Composer/Airflow. Jede hat die folgenden Vor- undNachteile:

Geplante BigQuery-Abfragen

Einfach einzurichten, aber es fehlt an Verantwortlichkeit und funktioniert nur für BigQuery

Zeitplanungsprogramm für die Cloud

Funktioniert für alle Dienste, aber komplizierte Abhängigkeiten werden langsam schwer zu pflegen

Cloud Build

Ereignisbasiert und kann von Zeitplänen ausgelöst werden, normalerweise meine bevorzugte Wahl, unterstützt aber keine Abläufe, die Backfills und Wiederholungen benötigen

Cloud Composer

Umfassendes Zeitplanungsprogramm mit Backfills, Unterstützung für komplizierte Arbeitsabläufe und Funktionen für Wiederholungen und Service Level Agreements (SLA), aber das teuerste und am kompliziertesten zu bedienende

Wir hoffen, dass wir dir damit einige Ideen für deine eigenen Anwendungsfälle geben konnten. Im nächsten Abschnitt befassen wir uns mit Datenflüssen in Echtzeit und den Tools, die du verwenden kannst, wenn du deine Daten sofort verarbeiten musst.

Streaming-Datenströme

Für manche Arbeitsabläufe ist die Batch-Planung nicht ausreichend. Wenn du zum Beispiel reaktive Datenaktualisierungen in weniger als einer halben Stunde brauchst, ist es vielleicht an der Zeit, sich mit den Optionen für Streaming-Datenströme zu befassen. Einige der Lösungen haben dieselben Funktionen und Komponenten, aber die Kosten und die Komplexität, die mit Echtzeit-Datenströmen einhergehen, müssen berücksichtigt werden.

Pub/Sub für Streaming-Daten

Die bisherigen Beispiele haben nur Pub/Sub mit relativ geringen Datenmengen behandelt, nur Ereignisse, die sagen, dass etwas passiert ist. Sein Hauptzweck ist jedoch der Umgang mit großen Datenströmen, und hier kann es wirklich glänzen. Dank des "at-least-once"-Liefersystems kannst du zuverlässige Datenströme aufbauen, selbst wenn du Daten im Umfang von mehreren TB durch das System schickst. Auch der Googlebot, der Suchmaschinen-Bot, der die Google-Suche aufgebaut hat, läuft auf einer ähnlichen Infrastruktur und lädt regelmäßig das gesamte Internet herunter - du weißt also, dass Pub/Sub skalieren kann!

Die Unterstützung für Streaming-Daten wird höchstwahrscheinlich mit Pub/Sub als Eingangspunkt beginnen, an den andere Streaming-Systeme Daten von Kafka oder anderen On-Premises-Systemen senden. Bei der Einrichtung dieser Echtzeit-Ingests sind es in der Regel die internen Anwendungsentwickler, die diesen Stream einrichten, bevor sie ihn an das GCP weitergeben, sobald sie wollen, dass dieser Stream in das GCP fließt. Meine Aufgabe ist es, das Schema der Daten zu definieren, die in das Pub/Sub-Thema einfließen, und sie von dort aus weiterzuleiten.

Sobald die Daten in ein Pub/Sub-Thema fließen, gibt es sofort einsetzbare Lösungen, um sie an beliebte Ziele wie Cloud Speicherung und BigQuery zu streamen. Diese werden von Apache Beam oder der von Google gehosteten Version, Dataflow, bereitgestellt.

Apache Beam/DataFlow

Der beliebteste Dienst für das Streaming von Daten auf GCP ist Dataflow. Dataflow ist ein Dienst, der Aufträge ausführt, die in Apache Beam geschrieben wurden, einer Datenverarbeitungsbibliothek, die ursprünglich von Google entwickelt wurde, jetzt aber als Open Source verfügbar ist, sodass du sie auch als Standard für andere Clouds nutzen kannst.

Apache Beam erstellt virtuelle Maschinen (VMs), auf denen Apache Beam installiert ist und die so eingerichtet sind, dass sie Code ausführen, der auf jedes eintreffende Datenpaket reagiert. Es hat eine automatische Skalierung eingebaut, d.h. wenn die Ressourcen der Maschine überlastet sind (d.h. wenn die Schwellenwerte für CPU und/oder Speicher erreicht werden), wird eine andere Maschine gestartet und ein Teil des Datenverkehrs dorthin geleitet. Je nachdem, wie viele Daten du schickst, kostet das mehr oder weniger, wobei die Untergrenze bei 1 VM liegt.

Es gibt gängige Datenaufträge, die durch die Templates von Apache Beam beschleunigt werden. Eine häufige Aufgabe ist zum Beispiel das Streamen von Pub/Sub in BigQuery, das möglich ist, ohne dass du irgendeinen Code schreiben musst. Ein Beispiel ist in Abbildung 4-13 dargestellt.

Um mit der Vorlage zu arbeiten, musst du einen Bucket und die BigQuery-Tabelle erstellen, in die die Pub/Sub-Nachrichten fließen sollen. Die BigQuery-Tabelle muss das richtige Schema haben, das mit dem Pub/Sub-Datenschema übereinstimmen sollte.

Setting up a Dataflow from within the Google Cloud Console for a Pub/Sub topic into BigQuery via the pre-defined template
Abbildung 4-13. Einrichten eines Datenflusses in der Google Cloud Console für ein Pub/Sub-Thema in BigQuery über die vordefinierte Vorlage

In meinem Beispiel streame ich einige GA4-Ereignisse von meinem Blog über GTM SS in Pub/Sub (siehe "Streaming von GA4-Ereignissen in Pub/Sub mit GTM SS"). Standardmäßig versucht der Stream, jedes Pub/Sub-Feld in eine BigQuery-Tabelle zu schreiben, und dein BigQuery-Schema muss genau übereinstimmen, um erfolgreich zu sein. Das kann problematisch sein, wenn dein Pub/Sub Felder enthält, die in BigQuery ungültig sind, wie z. B. die Felder mit Bindestrichen (-), die in Beispiel 4-16 zu sehen sind.

Beispiel 4-16. Ein Beispiel für das JSON, das von einem GA4-Tag in GTM SS an Pub/Sub gesendet wird, das einige Felder enthält, die mit x-ga
{"x-ga-protocol_version":"2",
"x-ga-measurement_id":"G-43MXXXX",
"x-ga-gtm_version":"2reba1",
"x-ga-page_id":1015778133,
"screen_resolution":"1536x864",
"language":"ru-ru",
"client_id":"68920138.12345678",
"x-ga-request_count":1,
"page_location":"https://code.markedmondson.me/data-privacy-gtm/",
"page_referrer":"https://www.google.com/",
"page_title":"Data Privacy Engineering with Google Tag Manager Server Side and ...",
"ga_session_id":"12343456",
"ga_session_number":1,
"x-ga-mp2-seg":"0",
"event_name":"page_view",
"x-ga-system_properties":{"fv":"2","ss":"1"},
"debug_mode":"true",
"ip_override":"78.140.192.76",
"user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...",
"x-ga-gcs-origin":"not-specified",
"user_id":"123445678"}

Um deine Anpassungswünsche zu erfüllen, kannst du eine Transformationsfunktion bereitstellen, die den Stream verändert, bevor er an BigQuery übergeben wird. Wir können zum Beispiel die Felder herausfiltern, die mit x-ga beginnen.

Die benutzerdefinierte Dataflow-Funktion (UDF) in Beispiel 4-17 filtert diese Ereignisse heraus, damit der Rest der Vorlage die Daten an BigQuery senden kann. Diese UDF muss in einen Bucket hochgeladen werden, damit die Dataflow Worker sie herunterladen und verwenden können.

Beispiel 4-17. Eine benutzerdefinierte Dataflow-Funktion, die Pub/Sub-Themenfelder herausfiltert, die mit x-ga beginnen, damit der Rest der Daten in BigQuery geschrieben werden kann
/**
 * A transform function that filters out fields starting with x-ga
 * @param {string} inJSON
 * @return {string} outJSON
 */
 function transform(inJSON) {
  var obj = JSON.parse(inJSON);
  var keys = Object.keys(obj);
  var outJSON = {};

  // don't output keys that starts with x-ga
  var outJSON = keys.filter(function(key) {
    return !key.startsWith('x-ga');
  }).reduce(function(acc, key) {
    acc[key] = obj[key];
    return acc;
  }, {});

  return JSON.stringify(outJSON);
 }

Sobald der Dataflow-Auftrag eingerichtet ist, erhältst du unter eine DAG, ähnlich wie bei Cloud Composer/Airflow, aber in diesem System geht es um ereignisbasierte Echtzeit-Flows und nicht um Batches. Abbildung 4-14 zeigt, was du im Bereich Dataflow-Aufträge in der Webkonsole sehen solltest.

Starting up a running job for importing Pub/Sub messages into BigQuery in real-time
Abbildung 4-14. Starten eines laufenden Auftrags zum Importieren von Pub/Sub-Nachrichten in BigQuery in Echtzeit

Kosten des Datenflusses

Angesichts der Funktionsweise von Dataflow solltest du dich davor hüten, zu viele VMs aufzusetzen, denn wenn du einen Fehler bekommst, kann das eine Menge Hits in deine Pipeline schicken, die dich schnell teuer zu stehen kommen können. Sobald du eine Vorstellung von deiner Arbeitslast hast, ist es ratsam, eine Obergrenze für die VMs festzulegen, damit du die Datenspitzen bewältigen kannst, sie dir aber nicht davonlaufen, wenn etwas wirklich Unerwartetes passiert. Selbst mit diesen Vorkehrungen ist die Lösung immer noch teurer als Batch-Workflows, denn du musst mit Kosten in Höhe von 10 bis 30 US-Dollar pro Tag oder 300 bis 900 US-Dollar pro Monat rechnen.

Das BigQuery-Schema muss mit der Pub/Sub-Konfiguration übereinstimmen, also müssen wir eine Tabelle erstellen. Die Tabelle in Abbildung 4-15 ist ebenfalls für eine Zeitpartitionierung eingerichtet.

The BigQuery data schema to receive the Pub/Sub JSON
Abbildung 4-15. Das BigQuery-Datenschema zum Empfang der Pub/Sub JSON

Wenn du Fehler machst, überträgt der Dataflow-Job die Rohdaten in eine andere Tabelle desselben Datensatzes, wo du die Fehler überprüfen und korrigieren kannst, wie in Abbildung 4-16 dargestellt.

Any errors from the data flow will appear in its own BigQuery table so you can examine the payloads
Abbildung 4-16. Alle Fehler aus dem Dataflow werden in einer eigenen BigQuery-Tabelle angezeigt, so dass du die Payloads untersuchen kannst

Wenn alles gut läuft, solltest du sehen, dass deine Pub/Sub-Daten in BigQuery erscheinen - klopfe dir selbst auf die Schulter, wenn du etwas Ähnliches wie in Abbildung 4-17 siehst.

Eine Standard-BigQuery-Exportfunktionalität steht dir bereits kostenlos zur Verfügung, indem du die nativen BigQuery-Exporte von GA4 nutzt, aber dieser Prozess kann für andere Anwendungsfälle angepasst werden, indem du andere Endpunkte ansteuerst oder andere Transformationen vornimmst. Du könntest zum Beispiel mit einer Teilmenge deiner GA4-Ereignisse arbeiten, um den Treffer datenschutzfreundlicher zu gestalten oder ihn mit Produktmetadaten anzureichern, die nur über einen anderen Echtzeit-Stream verfügbar sind.

Erinnere dich daran, dass Dataflow eine VM für diesen Datenfluss kostet, also schalte sie aus, wenn du sie nicht brauchst. Wenn dein Datenvolumen nicht groß genug ist, um einen solchen Aufwand zu rechtfertigen, kannst du auch Cloud Functions zum Streamen von Daten verwenden.

A successful streaming import from GA4 into GTM-SS to Pub/Sub to BigQuery
Abbildung 4-17. Ein erfolgreicher Streaming-Import von GA4 in GTM SS zu Pub/Sub toBigQuery

Streaming über Cloud-Funktionen

Wenn dein Datenvolumen innerhalb der Quoten von Cloud Function liegt, kann dein Pub/Sub-Topic-Setup auch Cloud Functions nutzen, um die Daten an verschiedene Orte zu streamen. Beispiel 4-5 enthält einen Beispielcode für sporadische Ereignisse wie BigQuery-Tabellen. Du kannst aber auch auf regelmäßigere Datenströme reagieren und Cloud Function skaliert je nach Bedarf - jeder Aufruf des Pub/Sub-Ereignisses erzeugt eine Cloud Function-Instanz, die parallel zu anderen Funktionen läuft.

Zu den Beschränkungen (für Cloud-Funktionen der Generation 1) gehören nur 540 Sekunden (9 Minuten) Laufzeit und insgesamt 3.000 Sekunden gleichzeitige Aufrufe (wenn z. B. eine Funktion 100 Sekunden für die Ausführung braucht, kannst du bis zu 30 Funktionen gleichzeitig laufen lassen). Das bedeutet, dass du deine Cloud-Funktionen klein und effizient gestalten solltest.

Die folgende Cloud-Funktion sollte so klein sein, dass du etwa 300 Anfragen pro Sekunde haben kannst(Beispiel 4-18). Sie nimmt die Pub/Sub-Nachricht und legt sie als String in einer raw Datenspalte in BigQuery ab, zusammen mit dem Zeitstempel. Du kannst den Code ändern, um bei Bedarf spezifischere Schemata zu analysieren, oder BigQuery SQL selbst verwenden, um den rohen JSON-String später in ordentlichere Daten zu verarbeiten.

Beispiel 4-18. Ändere das pb Diktat innerhalb des Codes, um mehr Felder zu analysieren, wenn du eine maßgeschneiderte Tabelle erstellen willst. Füge die Umgebungsargumente dataset und table hinzu, die auf deine vorgefertigte BigQuery-Tabelle zeigen. Inspiriert von Milosevics Medium-Beitrag über das Kopieren von Daten aus Pub/Sub in BigQuery.
# python 3.7
# pip google-cloud-bigquery==2.23.2
from google.cloud import bigquery
import base64, JSON, sys, os, time

def Pub/Sub_to_bigq(event, context):
  Pub/Sub_message = base64.b64decode(event['data']).decode('utf-8')
  print(Pub/Sub_message)
  pb = JSON.loads(Pub/Sub_message)
  raw = JSON.dumps(pb)

  pb['timestamp'] = time.time()
  pb['raw'] = raw
  to_bigquery(os.getenv['dataset'], os.getenv['table'], pb)

def to_bigquery(dataset, table, document):
  bigquery_client = bigquery.Client()
  dataset_ref = bigquery_client.dataset(dataset)
  table_ref = dataset_ref.table(table)
  table = bigquery_client.get_table(table_ref)
  errors = bigquery_client.insert_rows(table, [document], ignore_unknown_values=True)
  if errors != [] :
   print(errors, file=sys.stderr)

Die Funktion benötigt Umgebungsargumente, um festzulegen, wohin die Daten gehen, wie in Abbildung 4-18 gezeigt. So kannst du mehrere Funktionen für verschiedene Streams einsetzen.

Die vorgefertigte BigQuery-Tabelle hat nur zwei Felder: raw, das den JSON-String enthält, und timestamp, wenn die Cloud-Funktion ausgeführt wird. Du kannst die JSON-Funktionen von BigQuery in SQL verwenden, um diesen rohen JSON-String zu parsen, wie in Beispiel 4-19 gezeigt.

Setting the environment arguments for use within the cloud function
Abbildung 4-18. Einstellen der Umgebungsargumente für die Verwendung in der Cloud-Funktion in Beispiel 4-18
Beispiel 4-19. BigQuery SQL zum Parsen eines rohen JSON-Strings
SELECT
 JSON_VALUE(raw, "$.event_name") AS event_name,
 JSON_VALUE(raw, "$.client_id") AS client_id,
 JSON_VALUE(raw, "$.page_location") AS page_location,
 timestamp,
 raw
FROM
 `learning-ga4.ga4_Pub/Sub_cf.ga4_Pub/Sub`
WHERE
 DATE(_PARTITIONTIME) IS NULL
LIMIT
 1000

Das Ergebnis des Codes in Beispiel 4-19 ist in Abbildung 4-19 dargestellt. Du kannst diesen SQL-Downstream in einem Zeitplan oder über eine BigQuery-Ansicht einrichten.

The raw data table receiving the Pub/Sub stream from GA4 via GTM SS can have its JSON parsed out with BigQuery's functions such as JSON_VALUE()
Abbildung 4-19. Die Rohdatentabelle, die den Pub/Sub-Stream von GA4 über GTM-SS empfängt, kann ihr JSON mit BigQuery-Funktionen geparst werden, wie JSON_VALUE()

Streaming-Dienste bieten eine Möglichkeit, den reaktionsschnellsten und modernsten Datenstack für dein GA4-Setup zu haben, aber sie sind mit finanziellen und technischen Kosten verbunden, die du mit einem guten Business Case rechtfertigen musst. Wenn du diese Argumente hast, solltest du mit diesen Tools in der Lage sein, eine Anwendung zum Laufen zu bringen, die vor 10 Jahren noch fast unmöglich gewesen wäre.

Digitale Analysedatenströme wie GA4 sind in der Regel dann am nützlichsten, wenn sie auf den einzelnen Nutzer zugeschnitten sind. Wenn du jedoch Daten verwendest, die mit einer Person in Verbindung gebracht werden können, musst du besonders auf die rechtlichen und ethischen Konsequenzen achten. Darauf gehen wir im nächsten Abschnitt näher ein.

Schutz der Privatsphäre der Nutzer

Der Abschnitt "Datenschutz" gibt auch einen Überblick über den Datenschutzvon Nutz ern, , aber dieser Abschnitt geht mehr in die Tiefe und enthält einige technische Ressourcen.

Im modernen Datenzeitalter wird der Wert von Daten von denen erkannt, die sie zur Verfügung stellen, und nicht nur von denen, die sie nutzen. Das Aneignen von Daten ohne Erlaubnis kann heute als unmoralisch oder Diebstahl angesehen werden. Um langfristig ein nachhaltiges Unternehmen zu sein, ist es daher immer wichtiger, das Vertrauen deiner Nutzer/innen zu gewinnen. Die vertrauenswürdigsten Marken werden diejenigen sein, die sich darüber im Klaren sind, welche Daten sie erfassen und wie sie diese Daten nutzen, sowie diejenigen, die den Nutzern einen einfachen Zugang zu ihren eigenen Daten ermöglichen, so dass sie in der Lage sind, informierte Entscheidungen zu treffen, ihre eigenen Daten zurückzufordern und die Erlaubnis zu ihrer Nutzung zurückzunehmen. Im Zuge dieser sich entwickelnden Ethik werden auch die Gesetze in verschiedenen Regionen immer strenger und einschneidender, und es drohen hohe Geldstrafen, wenn du dich nicht daran hältst.

Wenn du Daten speicherst, die zu einer Person zurückverfolgt werden können, bist du dafür verantwortlich, diese persönlichen Daten sowohl vor internem Missbrauch als auch vor externen bösartigen Akteuren zu schützen, die versuchen könnten, sie zu stehlen.

Dieser Abschnitt befasst sich mit Designmustern für die Speicherung von Daten, die dir helfen, den Datenschutzprozess zu vereinfachen. In einigen Fällen ist die Nichteinhaltung der Vorschriften nicht beabsichtigt, sondern auf ein schlecht konzipiertes System zurückzuführen, das wir zu verhindern suchen.

Datenschutz by Design

Der einfachste Weg, Datenschutzbedenken zu vermeiden ist, einfach keine persönlichen Daten zu speichern. Wenn du diese persönlichen Daten nicht unbedingt brauchst, ist es am einfachsten, sie aus deiner Datenerfassung zu entfernen oder sie zu löschen, sobald sie in die Speicherung gelangen. Das mag leichtfertig klingen, muss aber dennoch gesagt werden, denn es kommt häufig vor, dass Unternehmen diese Daten aus Versehen oder ohne wirklich über die Konsequenzen nachzudenken sammeln. Ein klassischer Fall bei der Webanalyse ist das versehentliche Speichern von Nutzer-E-Mails in den URLs von Webformularen oder in Suchfeldern. Selbst wenn dies versehentlich geschieht, verstößt es gegen die Nutzungsbedingungen von Google Analytics und birgt das Risiko, dass dein Konto geschlossen wird. Eine gewisse Datenbereinigung bei der Datenerfassung kann viel dazu beitragen, das Haus sauber zu halten.

Wenn du ein gewisses Maß an Personalisierung benötigst, musst du trotzdem nicht unbedingt Daten erfassen, die ein Datenschutzrisiko darstellen. An dieser Stelle kommt die Pseudonomyzierung ins Spiel, die der Standard für die Datenerfassung ist, einschließlich GA4. Hier wird einem Nutzer eine ID zugewiesen, aber es ist diese ID, die weitergegeben wird, nicht die persönlichen Daten des Nutzers. Ein Beispiel ist die Wahl zwischen einer zufälligen ID oder der Telefonnummer einer Person als Benutzer-ID. Wenn die zufällige ID versehentlich aufgedeckt wird, kann ein Angreifer damit nicht viel anfangen, es sei denn, er hat Zugang zu dem System, das die ID mit den übrigen Daten der Person verknüpft. Handelt es sich bei der aufgedeckten ID um die Telefonnummer einer Person, hat der Angreifer etwas, das ersofort nutzen kann. Die Pseudonomyzierung ist die erste Verteidigungslinie, um die persönlichen Daten eines Nutzers zu schützen. Auch hier gilt: Verwende niemals eine E-Mail oder eine Telefonnummer als ID, denn das führt zu Datenschutzverletzungen; Unternehmen, die dies getan haben, wurden zu Geldstrafen verurteilt.

Es kann sein, dass die ID alles ist, was du für deine Anwendungsfälle brauchst - zum Beispiel ist die standardmäßige GA4-Datenerfassung auf dieser Ebene. Erst wenn du anfängst, diese ID mit persönlichen Informationen zu verknüpfen, wie z. B. die Verknüpfung von GA4's client_id mit der E-Mail-Adresse eines Nutzers, musst du dich mit extremeren Datenschutzüberlegungen auseinandersetzen. Dies geschieht in der Regel, wenn du die Daten mit deinen Backend-Systemen wie einer CRM-Datenbank verknüpfst.

Sollten deine Anwendungsfälle personenbezogene Daten wie E-Mails oder Namen erfordern, gibt es einige Grundsätze, die du einhalten solltest und die durch Datenschutzgesetze wie die GDPR gefördert werden. Diese Schritte ermöglichen es dir, die Würde der Nutzer/innen zu wahren und gleichzeitig einen gewissen geschäftlichen Nutzen aus diesen Daten zu ziehen:

Persönliche Daten (PII) an einer minimalen Anzahl von Orten aufbewahren

Personenbezogene Daten sollten in so wenigen Datenbanken wie möglich gespeichert und dann über eine pseudonyme ID, die der Schlüssel für die jeweilige Tabelle ist, mit anderen Systemen verknüpft oder verbunden werden. Auf diese Weise musst du nur an einer Stelle nachsehen, wenn du die Daten einer Person löschen oder extrahieren willst, und du musst sie nicht auch noch an anderen Stellen löschen, an die sie kopiert oder verknüpft worden sind. Dies ergänzt den nächsten Punkt über die Verschlüsselung von Benutzerdaten, da du dies nur für die Benutzerdatenbank tun musst.

Benutzerdaten mit Salt-and-Peppered-Hashes verschlüsseln

Das Hashing-Verfahren ist eine Methode der Einwegverschlüsselung von Daten, so dass es unmöglich ist, die ursprünglichen Daten wiederherzustellen, ohne die Bestandteile zu kennen: "Mark Edmondson" zum Beispiel ist, wenn er mit dem beliebten sha256 Hashing-Algorithmus gehasht wird:

3e7e793f2b41a8f9c703898c5c0d4e08ab2f22aa1603f8d0f6e4872a8f542335

Es ist jedoch immer dieser Hash und sollte weltweit eindeutig sein, damit du ihn als zuverlässigen Schlüssel verwenden kannst. Den Hash zu "salzen und pfeffern" bedeutet, dass du den Daten auch ein eindeutiges Schlüsselwort hinzufügst, um sie noch sicherer zu machen, falls jemand den Hash-Algorithmus knackt oder denselben Hash für eine Verknüpfung verwenden kann. Wenn mein Salz zum Beispiel "Paviane" ist, stelle ich es meinem Datenpunkt voran, so dass aus "Mark Edmondson" "baboonsMark Edmondson" wird, und der Hash lautet:

a776b81a2a6b1c2fc787ea0a21932047b080b1f08e7bc6d6a2ccd1fb6443df48

z.B. völlig anders als vorher. Salts können global sein oder mit dem Nutzerpunkt gespeichert werden, um sie für jeden Nutzer einzigartig zu machen. Das "Pfeffern" oder "geheime Salzen" des Hashes ist ein ähnliches Konzept, aber dieses Mal wird das Schlüsselwort nicht zusammen mit den zu verschlüsselnden Daten aufbewahrt, sondern an einem anderen sicheren Ort. Dies schützt vor Datenbankverletzungen, da es nun zwei Speicherorte gibt. In diesem Fall würde das "Pepper" abgerufen und könnte "averylongSECRETthatnoonecanknow?" lauten, so dass mein endgültiger Hash "baboonsMark EdmondsonaverylongSECRETthatnoonecanknow?" lautet:

c9299fe251319ffa7ec66137acfe81c75ee115ceaa89b3e74b521a0b5e12d138

die es einem motivierten Hacker sehr schwer machen sollte, den Nutzer wieder zu identifizieren.

Persönliche Daten mit Verfallszeiten versehen

Manchmal hast du keine andere Wahl, als personenbezogene Daten zu kopieren, zum Beispiel, wenn du Daten aus verschiedenen Clouds oder Systemen importierst. In diesem Fall kannst du die Datenquelle als die Quelle der Wahrheit für alle deine Datenschutzinitiativen bestimmen und ein Verfallsdatum für alle Daten festlegen, die von dieser Quelle kopiert werden. In der Regel sind das dreißig Tage, was bedeutet, dass du mindestens alle 30 Tage (vielleicht sogar täglich) einen vollständigen Import durchführen musst, damit das Datenvolumen wächst. So kannst du sicher sein, dass bei der Aktualisierung von Benutzerberechtigungen und Werten in deiner Stammdatenbank alle Kopien dieser Daten vergänglich sind und nicht mehr existieren, sobald die Importe beendet sind.

Das Hinzufügen von Datenschutzgrundsätzen bedeutet zwar zusätzliche Arbeit, aber die Belohnung ist Seelenfrieden und Vertrauen in deine eigenen Systeme, das du auch deinen Kunden vermitteln kannst. Ein Beispiel für den letzten Punkt des Datenverfalls in einigen der Speichersysteme, über die wir in diesem Kapitel gesprochen haben, findest du im nächsten Abschnitt.

Datenverfall in BigQuery

Wenn du deine Datensätze, Tabellen und Buckets einrichtest, kannst du das Verfallsdatum für deine eingehenden Daten festlegen. Wie du sie in GCS einstellst, haben wir bereits in "Google Cloud Storage" beschrieben .

In BigQuery kannst du ein Verfallsdatum auf Datensatzebene festlegen, das sich auf alle Tabellen in diesem Datensatz auswirkt - siehe Abbildung 4-20 für ein Beispiel mit einem Testdatensatz.

You can configure the table expiration time when you create a dataset
Abbildung 4-20. Du kannst die Ablaufzeit der Tabelle konfigurieren, wenn du ein Dataset erstellst

Für partitionierte Tabellen brauchst du etwas anderes, da die Tabelle immer existieren wird, du aber möchtest, dass die Partitionen selbst mit der Zeit ablaufen und dir nur die neuesten Daten zur Verfügung stehen. Dazu musst du gcloud aufrufen oder das BigQuery-SQL zum Ändern von Tabelleneigenschaften verwenden, wie in Beispiel 4-20 gezeigt.

Beispiel 4-20. Festlegen eines Verfallsdatums für BigQuery-Partitionen in einer Partitionstabelle

Der gcloud-Weg (über deine lokale Bash-Konsole oder in der Cloud Console):

bq update --time_partitioning_field=event_date \
 --time_partitioning_expiration 604800 [PROJECT-ID]:[DATASET].partitioned_table

Oder über BigQuery DML:

ALTER TABLE `project-name`.dataset_name.table_name
SET OPTIONS (partition_expiration_days=7);

Neben den passiven Datenverfallszeiten kannst du über die Data Loss Prevention API auch aktiv Daten auf Datenschutzverletzungen überprüfen.

Data Loss Prevention API

Die Data Loss Prevention (DLP) API ist eine Möglichkeit , sensible Daten wie E-Mails, Telefonnummern und Kreditkartennummern automatisch zu erkennen und zu maskieren. Du kannst sie aufrufen und auf deine Daten in der Cloud Speicherung oder BigQuery anwenden.

Wenn du eine große Menge an Streaming-Daten hast, steht eine Dataflow-Vorlage zur Verfügung, mit der du CSV-Daten aus GCS lesen und die redigierten Daten in BigQuery eingeben kannst.

Für GA4 kannst du sie am einfachsten verwenden, um deine BigQuery-Exporte zu scannen, um zu sehen, ob versehentlich persönliche Daten gesammelt wurden. Die DLP-API scannt immer nur eine Tabelle auf einmal, also scannst du am besten jeden Tag deine eingehende Datentabelle. Wenn du viele Daten hast, empfehle ich, nur eine Stichprobe zu scannen und/oder den Scan auf die Felder zu beschränken, die möglicherweise sensible Daten enthalten. Bei GA4 BigQuery-Exporten ist dies höchstwahrscheinlich nur die event_params.value.string_value, da alle anderen Felder mehr oder weniger durch deine Konfiguration festgelegt sind (event_name, etc.).

Zusammenfassung

Da Daten in so vielen verschiedenen Formen und Verwendungszwecken vorliegen, gibt es viele verschiedene Systeme, um sie zu speichern. Die groben Kategorien, über die wir in diesem Kapitel gesprochen haben, sind strukturierte und unstrukturierte Daten sowie geplante und Streaming-Pipelines zwischen diesen Systemen. Du brauchst auch eine gute Organisationsstruktur, in der du dir Gedanken darüber machst, wer und was auf die einzelnen Daten zugreifen soll, denn letztendlich sind es die Menschen, die diese Daten nutzen werden, und die Verringerung der Reibungsverluste, damit die richtige Person die richtigen Daten sehen kann, ist ein großer Schritt zur Datenreife in deinemUnternehmen. Sobald du Daten benötigst, die über GA4 hinausgehen, musst du wissen, wie diese Systeme zusammenspielen. Mit den GA4 BigQuery-Exporten hast du jedoch einen guten Ausgangspunkt, der als eine der wichtigsten Funktionen gegenüber Universal Analytics hoch angesehen wird.

Nachdem wir nun darüber gesprochen haben, wie man Daten sammelt und speichert, werden wir im nächsten Kapitel näher darauf eingehen, wie diese Daten aktiv bearbeitet, umgewandelt und modelliert werden und wie sie normalerweise den größten Mehrwert in der Pipeline darstellen.

Get Google Analytics lernen now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.