Database
 sql >> Datenbank >  >> RDS >> Database

Interviewfragen für Dateningenieure mit Python

Zu Vorstellungsgesprächen zu gehen kann ein zeitraubender und ermüdender Prozess sein, und technische Vorstellungsgespräche können sogar noch stressiger sein! Dieses Tutorial soll Sie auf einige häufig gestellte Fragen vorbereiten, denen Sie während Ihres Vorstellungsgesprächs als Data Engineer begegnen werden. Sie erfahren, wie Sie Fragen zu Datenbanken, Python und SQL beantworten.

Am Ende dieses Tutorials können Sie:

  • Verstehen Sie allgemeine Fragen in Vorstellungsgesprächen für Data Engineers
  • Unterscheiden Sie zwischen relationalen und nicht relationalen Datenbanken
  • Datenbanken mit Python einrichten
  • Verwenden Sie Python zum Abfragen von Daten

Kostenloser Download: Holen Sie sich ein Beispielkapitel aus Python Tricks:The Book, das Ihnen die Best Practices von Python mit einfachen Beispielen zeigt, die Sie sofort anwenden können, um schöneren + Pythonic-Code zu schreiben.


Dateningenieur werden

Die Rolle des Data Engineering kann umfangreich und vielfältig sein. Sie müssen über praktische Kenntnisse in mehreren Technologien und Konzepten verfügen. Data Engineers sind flexibel in ihrem Denken. Daher können sie sich mit mehreren Themen wie Datenbanken, Softwareentwicklung, DevOps und Big Data auskennen.


Was macht ein Data Engineer?

Angesichts der vielfältigen Fähigkeiten kann eine Data Engineering-Rolle viele verschiedene Stellenbeschreibungen umfassen. Ein Data Engineer kann für das Datenbankdesign, das Schemadesign und die Erstellung mehrerer Datenbanklösungen verantwortlich sein. An dieser Arbeit kann auch ein Datenbankadministrator beteiligt sein.

Als Data Engineer , könnten Sie als Brücke zwischen der Datenbank und den Data-Science-Teams fungieren. In diesem Fall sind Sie auch für die Datenbereinigung und -vorbereitung verantwortlich. Wenn es um Big Data geht, ist es Ihre Aufgabe, eine effiziente Lösung für diese Daten zu finden. Diese Arbeit kann sich mit der DevOps-Rolle überschneiden.

Sie müssen auch effiziente Datenabfragen für Berichte und Analysen durchführen. Möglicherweise müssen Sie mit mehreren Datenbanken interagieren oder gespeicherte Prozeduren schreiben. Für viele Lösungen wie stark frequentierte Websites oder Dienste kann mehr als eine Datenbank vorhanden sein. In diesen Fällen ist der Data Engineer für die Einrichtung der Datenbanken, deren Wartung und den Datentransfer zwischen ihnen verantwortlich.



Wie kann Python Dateningenieuren helfen?

Python gilt als das Schweizer Taschenmesser der Programmiersprachen. Es ist besonders nützlich in Data Science, Backend-Systemen und serverseitigem Scripting. Das liegt daran, dass Python über eine starke Typisierung, eine einfache Syntax und eine Fülle von Bibliotheken von Drittanbietern verfügt, die verwendet werden können. Pandas, SciPy, Tensorflow, SQLAlchemy und NumPy sind einige der am häufigsten verwendeten Bibliotheken in der Produktion in verschiedenen Branchen.

Am wichtigsten ist, dass Python die Entwicklungszeit verkürzt, was für Unternehmen weniger Kosten bedeutet. Für einen Data Engineer ist die Codeausführung größtenteils datenbankgebunden, nicht CPU-gebunden. Aus diesem Grund ist es sinnvoll, die Einfachheit von Python zu nutzen, auch wenn die Leistung im Vergleich zu kompilierten Sprachen wie C# und Java geringer ist.




Beantwortung von Interviewfragen für Dateningenieure

Jetzt, da Sie wissen, worin Ihre Rolle bestehen könnte, ist es an der Zeit zu lernen, wie Sie einige Interviewfragen für Dateningenieure beantworten können! Es gibt zwar viel zu tun, aber Sie werden im gesamten Tutorial praktische Python-Beispiele sehen, die Sie auf dem Weg begleiten.



Fragen zu relationalen Datenbanken

Datenbanken sind eine der wichtigsten Komponenten in einem System. Ohne sie kann es keinen Staat und keine Geschichte geben. Auch wenn Sie das Datenbankdesign möglicherweise nicht als Priorität angesehen haben, sollten Sie wissen, dass es einen erheblichen Einfluss darauf haben kann, wie schnell Ihre Seite geladen wird. In den letzten Jahren haben mehrere große Unternehmen mehrere neue Tools und Techniken eingeführt:

  • NoSQL
  • Cache Datenbanken
  • Graphdatenbanken
  • NoSQL-Unterstützung in SQL-Datenbanken

Diese und andere Techniken wurden erfunden, um die Geschwindigkeit zu erhöhen, mit der Datenbanken Anforderungen verarbeiten. Wahrscheinlich müssen Sie in Ihrem Interview mit dem Data Engineer über diese Konzepte sprechen, also gehen wir einige Fragen durch!


F1:Relationale vs. nicht relationale Datenbanken

Eine relationale Datenbank ist eine, in der Daten in Form einer Tabelle gespeichert werden. Jede Tabelle hat ein Schema , das sind die Spalten und Typen, die ein Datensatz haben muss. Jedes Schema muss mindestens einen Primärschlüssel haben, der diesen Datensatz eindeutig identifiziert. Mit anderen Worten, es gibt keine doppelten Zeilen in Ihrer Datenbank. Darüber hinaus kann jede Tabelle über Fremdschlüssel mit anderen Tabellen verknüpft werden.

Ein wichtiger Aspekt relationaler Datenbanken ist, dass eine Änderung in einem Schema auf alle Datensätze angewendet werden muss. Dies kann manchmal zu Brüchen und großen Kopfschmerzen während der Migration führen. Nicht relationale Datenbanken Dinge anders angehen. Sie sind von Natur aus schemalos, was bedeutet, dass Datensätze mit verschiedenen Schemas und mit einer anderen, verschachtelten Struktur gespeichert werden können. Datensätze können immer noch Primärschlüssel haben, aber eine Änderung des Schemas erfolgt Eintrag für Eintrag.

Sie müssten einen Geschwindigkeitsvergleichstest basierend auf der Art der ausgeführten Funktion durchführen. Sie können INSERT wählen , UPDATE , DELETE , oder eine andere Funktion. Schemadesign, Indizes, die Anzahl der Aggregationen und die Anzahl der Datensätze wirken sich ebenfalls auf diese Analyse aus, daher müssen Sie gründlich testen. Später erfahren Sie mehr darüber, wie das geht.

Datenbanken unterscheiden sich auch in der Skalierbarkeit . Die Verteilung einer nicht relationalen Datenbank bereitet möglicherweise weniger Kopfschmerzen. Das liegt daran, dass eine Sammlung verwandter Datensätze einfach auf einem bestimmten Knoten gespeichert werden kann. Andererseits erfordern relationale Datenbanken mehr Überlegung und verwenden normalerweise ein Master-Slave-System.



Ein SQLite-Beispiel

Nachdem Sie nun beantwortet haben, was relationale Datenbanken sind, ist es an der Zeit, sich mit Python zu beschäftigen! SQLite ist eine praktische Datenbank, die Sie auf Ihrem lokalen Rechner verwenden können. Die Datenbank ist eine einzelne Datei, was sie ideal für Prototyping-Zwecke macht. Importieren Sie zunächst die erforderliche Python-Bibliothek und erstellen Sie eine neue Datenbank:

import sqlite3

db = sqlite3.connect(':memory:')  # Using an in-memory database
cur = db.cursor()

Sie sind jetzt mit einer In-Memory-Datenbank verbunden und haben Ihr Cursor-Objekt einsatzbereit.

Als Nächstes erstellen Sie die folgenden drei Tabellen:

  1. Kunde: Diese Tabelle enthält einen Primärschlüssel sowie den Vor- und Nachnamen des Kunden.
  2. Artikel: Diese Tabelle enthält einen Primärschlüssel, den Artikelnamen und den Artikelpreis.
  3. Gekaufte Artikel :Diese Tabelle enthält eine Bestellnummer, ein Datum und einen Preis. Es stellt auch eine Verbindung zu den Primärschlüsseln in den Artikel- und Kundentabellen her.

Nachdem Sie nun eine Vorstellung davon haben, wie Ihre Tabellen aussehen werden, können Sie fortfahren und sie erstellen:

cur.execute('''CREATE TABLE IF NOT EXISTS Customer (
                id integer PRIMARY KEY,
                firstname varchar(255),
                lastname varchar(255) )''')
cur.execute('''CREATE TABLE IF NOT EXISTS Item (
                id integer PRIMARY KEY,
                title varchar(255),
                price decimal )''')
cur.execute('''CREATE TABLE IF NOT EXISTS BoughtItem (
                ordernumber integer PRIMARY KEY,
                customerid integer,
                itemid integer,
                price decimal,
                CONSTRAINT customerid
                    FOREIGN KEY (customerid) REFERENCES Customer(id),
                CONSTRAINT itemid
                    FOREIGN KEY (itemid) REFERENCES Item(id) )''')

Sie haben eine Abfrage an cur.execute() übergeben um Ihre drei Tabellen zu erstellen.

Der letzte Schritt besteht darin, Ihre Tabellen mit Daten zu füllen:

cur.execute('''INSERT INTO Customer(firstname, lastname)
               VALUES ('Bob', 'Adams'),
                      ('Amy', 'Smith'),
                      ('Rob', 'Bennet');''')
cur.execute('''INSERT INTO Item(title, price)
               VALUES ('USB', 10.2),
                      ('Mouse', 12.23),
                      ('Monitor', 199.99);''')
cur.execute('''INSERT INTO BoughtItem(customerid, itemid, price)
               VALUES (1, 1, 10.2),
                      (1, 2, 12.23),
                      (1, 3, 199.99),
                      (2, 3, 180.00),
                      (3, 2, 11.23);''') # Discounted price 

Da nun jede Tabelle einige Datensätze enthält, können Sie diese Daten verwenden, um einige weitere Interviewfragen für Data Engineers zu beantworten.



Q2:SQL-Aggregationsfunktionen

Aggregationsfunktionen sind diejenigen, die eine mathematische Operation an einer Ergebnismenge ausführen. Einige Beispiele sind AVG , COUNT , MIN , MAX , und SUM . Oft benötigen Sie GROUP BY und HAVING Klauseln zur Ergänzung dieser Aggregationen. Eine nützliche Aggregationsfunktion ist AVG , mit der Sie den Mittelwert einer bestimmten Ergebnismenge berechnen können:

>>>
>>> cur.execute('''SELECT itemid, AVG(price) FROM BoughtItem GROUP BY itemid''')
>>> print(cur.fetchall())
[(1, 10.2), (2, 11.73), (3, 189.995)]

Hier haben Sie den Durchschnittspreis für jeden der in Ihrer Datenbank gekauften Artikel abgerufen. Sie können das Element mit einem itemid sehen von 1 hat einen Durchschnittspreis von 10,20 $.

Um die obige Ausgabe verständlicher zu machen, können Sie den Elementnamen anstelle von itemid anzeigen :

>>>
>>> cur.execute('''SELECT item.title, AVG(boughtitem.price) FROM BoughtItem as boughtitem
...             INNER JOIN Item as item on (item.id = boughtitem.itemid)
...             GROUP BY boughtitem.itemid''')
...
>>> print(cur.fetchall())
[('USB', 10.2), ('Mouse', 11.73), ('Monitor', 189.995)]

Jetzt sehen Sie leichter, dass der Artikel mit einem Durchschnittspreis von 10,20 $ der USB ist .

Eine weitere nützliche Aggregation ist SUM . Mit dieser Funktion können Sie den Gesamtbetrag anzeigen, den jeder Kunde ausgegeben hat:

>>>
>>> cur.execute('''SELECT customer.firstname, SUM(boughtitem.price) FROM BoughtItem as boughtitem
...             INNER JOIN Customer as customer on (customer.id = boughtitem.customerid)
...             GROUP BY customer.firstname''')
...
>>> print(cur.fetchall())
[('Amy', 180), ('Bob', 222.42000000000002), ('Rob', 11.23)]

Im Durchschnitt gab der Kunde namens Amy etwa 180 $ aus, während Rob nur 11,23 $ ausgab!

Wenn Ihr Gesprächspartner Datenbanken mag, sollten Sie verschachtelte Abfragen, Join-Typen und die Schritte, die eine relationale Datenbank zur Durchführung Ihrer Abfrage durchführt, auffrischen.



Q3:Beschleunigung von SQL-Abfragen

Die Geschwindigkeit hängt von verschiedenen Faktoren ab, wird aber hauptsächlich davon beeinflusst, wie viele der folgenden Elemente vorhanden sind:

  • Beitreten
  • Aggregationen
  • Durchläufe
  • Aufzeichnungen

Je größer die Anzahl der Joins, desto höher die Komplexität und desto größer die Anzahl der Durchläufe in Tabellen. Mehrere Joins sind ziemlich teuer für mehrere tausend Datensätze mit mehreren Tabellen, da die Datenbank auch das Zwischenergebnis zwischenspeichern muss! An diesem Punkt könnten Sie darüber nachdenken, wie Sie Ihre Speichergröße erhöhen können.

Die Geschwindigkeit wird auch davon beeinflusst, ob Indizes vorhanden sind oder nicht in der Datenbank vorhanden. Indizes sind äußerst wichtig und ermöglichen es Ihnen, eine Tabelle schnell zu durchsuchen und eine Übereinstimmung für eine in der Abfrage angegebene Spalte zu finden.

Indizes sortieren die Datensätze auf Kosten einer höheren Einfügezeit sowie etwas Speicherplatz. Mehrere Spalten können kombiniert werden, um einen einzelnen Index zu erstellen. Beispielsweise die Spalten date und price kombiniert werden, da Ihre Abfrage von beiden Bedingungen abhängt.



Q4:Debuggen von SQL-Abfragen

Die meisten Datenbanken enthalten einen EXPLAIN QUERY PLAN die die Schritte beschreibt, die die Datenbank zum Ausführen der Abfrage ausführt. Für SQLite können Sie diese Funktionalität aktivieren, indem Sie EXPLAIN QUERY PLAN hinzufügen vor einem SELECT Aussage:

>>>
>>> cur.execute('''EXPLAIN QUERY PLAN SELECT customer.firstname, item.title, 
...                item.price, boughtitem.price FROM BoughtItem as boughtitem
...                INNER JOIN Customer as customer on (customer.id = boughtitem.customerid)
...                INNER JOIN Item as item on (item.id = boughtitem.itemid)''')
...
>>> print(cur.fetchall())
[(4, 0, 0, 'SCAN TABLE BoughtItem AS boughtitem'), 
(6, 0, 0, 'SEARCH TABLE Customer AS customer USING INTEGER PRIMARY KEY (rowid=?)'), 
(9, 0, 0, 'SEARCH TABLE Item AS item USING INTEGER PRIMARY KEY (rowid=?)')]

Diese Abfrage versucht, den Vornamen, den Titel des Artikels, den Originalpreis und den Kaufpreis für alle gekauften Artikel aufzulisten.

So sieht der Abfrageplan selbst aus:

SCAN TABLE BoughtItem AS boughtitem
SEARCH TABLE Customer AS customer USING INTEGER PRIMARY KEY (rowid=?)
SEARCH TABLE Item AS item USING INTEGER PRIMARY KEY (rowid=?)

Beachten Sie, dass die fetch-Anweisung in Ihrem Python-Code nur die Erklärung, aber nicht die Ergebnisse zurückgibt. Das liegt daran, dass EXPLAIN QUERY PLAN ist nicht für die Verwendung in der Produktion vorgesehen.




Fragen zu nicht relationalen Datenbanken

Im vorherigen Abschnitt haben Sie die Unterschiede zwischen relationalen und nicht relationalen Datenbanken dargelegt und SQLite mit Python verwendet. Jetzt werden Sie sich auf NoSQL konzentrieren. Ihr Ziel ist es, seine Stärken, Unterschiede und Anwendungsfälle hervorzuheben.


Ein MongoDB-Beispiel

Sie verwenden die gleichen Daten wie zuvor, aber diesmal ist Ihre Datenbank MongoDB. Diese NoSQL-Datenbank ist dokumentenbasiert und sehr gut skalierbar. Das Wichtigste zuerst:Sie müssen die erforderliche Python-Bibliothek installieren:

$ pip install pymongo

Möglicherweise möchten Sie auch die MongoDB Compass Community installieren. Es enthält eine lokale IDE, die sich perfekt zur Visualisierung der Datenbank eignet. Damit können Sie die erstellten Datensätze sehen, Auslöser erstellen und als visueller Administrator für die Datenbank fungieren.

Hinweis: Um den Code in diesem Abschnitt auszuführen, benötigen Sie einen laufenden Datenbankserver. Weitere Informationen zur Einrichtung finden Sie unter Einführung in MongoDB und Python.

So erstellen Sie die Datenbank und fügen einige Daten ein:

import pymongo

client = pymongo.MongoClient("mongodb://localhost:27017/")

# Note: This database is not created until it is populated by some data
db = client["example_database"]

customers = db["customers"]
items = db["items"]

customers_data = [{ "firstname": "Bob", "lastname": "Adams" },
                  { "firstname": "Amy", "lastname": "Smith" },
                  { "firstname": "Rob", "lastname": "Bennet" },]
items_data = [{ "title": "USB", "price": 10.2 },
              { "title": "Mouse", "price": 12.23 },
              { "title": "Monitor", "price": 199.99 },]

customers.insert_many(customers_data)
items.insert_many(items_data)

Wie Sie vielleicht bemerkt haben, speichert MongoDB Datensätze in Sammlungen , die einer Liste von Wörterbüchern in Python entsprechen. In der Praxis speichert MongoDB BSON-Dokumente.



F5:Abfragen von Daten mit MongoDB

Versuchen wir, das BoughtItem zu replizieren Tabelle zuerst, wie Sie es in SQL getan haben. Dazu müssen Sie einem Kunden ein neues Feld hinzufügen. Die MongoDB-Dokumentation gibt an, dass der Schlüsselwortoperator set kann verwendet werden, um einen Datensatz zu aktualisieren, ohne alle vorhandenen Felder schreiben zu müssen:

# Just add "boughtitems" to the customer where the firstname is Bob
bob = customers.update_many(
        {"firstname": "Bob"},
        {
            "$set": {
                "boughtitems": [
                    {
                        "title": "USB",
                        "price": 10.2,
                        "currency": "EUR",
                        "notes": "Customer wants it delivered via FedEx",
                        "original_item_id": 1
                    }
                ]
            },
        }
    )

Beachten Sie, wie Sie dem customer zusätzliche Felder hinzugefügt haben ohne das Schema vorher explizit zu definieren. Klasse!

Tatsächlich können Sie einen anderen Kunden mit einem leicht geänderten Schema aktualisieren:

amy = customers.update_many(
        {"firstname": "Amy"},
        {
            "$set": {
                "boughtitems":[
                    {
                        "title": "Monitor",
                        "price": 199.99,
                        "original_item_id": 3,
                        "discounted": False
                    }
                ]
            } ,
        }
    )
print(type(amy))  # pymongo.results.UpdateResult

Ähnlich wie SQL erlauben auch dokumentenbasierte Datenbanken die Ausführung von Abfragen und Aggregationen. Die Funktionalität kann sich jedoch sowohl syntaktisch als auch in der zugrunde liegenden Ausführung unterscheiden. Tatsächlich haben Sie vielleicht bemerkt, dass MongoDB den $ reserviert Zeichen, um einen Befehl oder eine Aggregation für die Datensätze anzugeben, z. B. $group . Weitere Informationen zu diesem Verhalten finden Sie in den offiziellen Dokumenten.

Sie können Abfragen genauso ausführen wie in SQL. Zunächst können Sie einen Index erstellen:

>>>
>>> customers.create_index([("name", pymongo.DESCENDING)])

Dies ist optional, beschleunigt aber Abfragen, die Namenssuchen erfordern.

Anschließend können Sie die Kundennamen aufsteigend sortiert abrufen:

>>>
>>> items = customers.find().sort("name", pymongo.ASCENDING)

Sie können die gekauften Artikel auch durchlaufen und ausdrucken:

>>>
>>> for item in items:
...     print(item.get('boughtitems'))    
...
None
[{'title': 'Monitor', 'price': 199.99, 'original_item_id': 3, 'discounted': False}]
[{'title': 'USB', 'price': 10.2, 'currency': 'EUR', 'notes': 'Customer wants it delivered via FedEx', 'original_item_id': 1}]

Sie können sogar eine Liste eindeutiger Namen in der Datenbank abrufen:

>>>
>>> customers.distinct("firstname")
['Bob', 'Amy', 'Rob']

Da Sie nun die Namen der Kunden in Ihrer Datenbank kennen, können Sie eine Abfrage erstellen, um Informationen über sie abzurufen:

>>>
>>> for i in customers.find({"$or": [{'firstname':'Bob'}, {'firstname':'Amy'}]}, 
...                                  {'firstname':1, 'boughtitems':1, '_id':0}):
...     print(i)
...
{'firstname': 'Bob', 'boughtitems': [{'title': 'USB', 'price': 10.2, 'currency': 'EUR', 'notes': 'Customer wants it delivered via FedEx', 'original_item_id': 1}]}
{'firstname': 'Amy', 'boughtitems': [{'title': 'Monitor', 'price': 199.99, 'original_item_id': 3, 'discounted': False}]}

Hier ist die entsprechende SQL-Abfrage:

SELECT firstname, boughtitems FROM customers WHERE firstname LIKE ('Bob', 'Amy')

Beachten Sie, dass, obwohl sich die Syntax nur leicht unterscheiden kann, es einen drastischen Unterschied in der Art und Weise gibt, wie Abfragen unter der Haube ausgeführt werden. Dies ist aufgrund der unterschiedlichen Abfragestrukturen und Anwendungsfälle zwischen SQL- und NoSQL-Datenbanken zu erwarten.



F6:NoSQL vs. SQL

Wenn Sie ein sich ständig änderndes Schema haben, wie z. B. Finanzaufsichtsinformationen, kann NoSQL die Datensätze ändern und zugehörige Informationen verschachteln. Stellen Sie sich die Anzahl der Verknüpfungen vor, die Sie in SQL ausführen müssten, wenn Sie acht Verschachtelungsreihenfolgen hätten! Diese Situation ist jedoch häufiger als man denkt.

Was ist nun, wenn Sie Berichte ausführen, Informationen zu diesen Finanzdaten extrahieren und Schlussfolgerungen ziehen möchten? In diesem Fall müssen Sie komplexe Abfragen ausführen, und SQL ist in dieser Hinsicht tendenziell schneller.

Hinweis: SQL-Datenbanken, insbesondere PostgreSQL, haben auch eine Funktion veröffentlicht, mit der abfragbare JSON-Daten als Teil eines Datensatzes eingefügt werden können. Während dies das Beste aus beiden Welten kombinieren kann, kann die Geschwindigkeit von Bedeutung sein.

Es ist schneller, unstrukturierte Daten aus einer NoSQL-Datenbank abzufragen, als JSON-Felder aus einer JSON-Spalte in PostgreSQL abzufragen. Sie können jederzeit einen Geschwindigkeitsvergleichstest durchführen, um eine endgültige Antwort zu erhalten.

Dennoch kann diese Funktion den Bedarf an einer zusätzlichen Datenbank verringern. Manchmal werden eingelegte oder serialisierte Objekte in Datensätzen in Form von Binärtypen gespeichert und dann beim Lesen deserialisiert.

Geschwindigkeit ist jedoch nicht die einzige Metrik. Sie sollten auch Dinge wie Transaktionen, Atomarität, Haltbarkeit und Skalierbarkeit berücksichtigen. Transaktionen sind in Finanzanwendungen wichtig, und solche Merkmale haben Vorrang.

Da es eine große Auswahl an Datenbanken mit jeweils eigenen Funktionen gibt, ist es die Aufgabe des Dateningenieurs, eine fundierte Entscheidung darüber zu treffen, welche Datenbank in jeder Anwendung verwendet werden soll. Weitere Informationen finden Sie unter ACID-Eigenschaften in Bezug auf Datenbanktransaktionen.

Möglicherweise werden Sie in Ihrem Data Engineer-Interview auch gefragt, welche anderen Datenbanken Sie kennen. Es gibt mehrere andere relevante Datenbanken, die von vielen Unternehmen verwendet werden:

  • Elastic Search ist sehr effizient bei der Textsuche. Es nutzt seine dokumentbasierte Datenbank, um ein leistungsstarkes Suchwerkzeug zu erstellen.
  • Newt DB kombiniert ZODB und die PostgreSQL JSONB-Funktion, um eine Python-freundliche NoSQL-Datenbank zu erstellen.
  • InfluxDB wird in Zeitreihenanwendungen zum Speichern von Ereignissen verwendet.

Die Liste geht weiter, aber dies zeigt, wie eine Vielzahl verfügbarer Datenbanken alle ihre Nischenindustrie bedienen.




Fragen zu Cache-Datenbanken

Datenbanken zwischenspeichern halten häufig aufgerufene Daten. Sie leben neben den wichtigsten SQL- und NoSQL-Datenbanken. Ihr Ziel ist es, die Last zu verringern und Anfragen schneller zu bearbeiten.


Ein Redis-Beispiel

Sie haben SQL- und NoSQL-Datenbanken für langfristige Speicherlösungen abgedeckt, aber was ist mit einer schnelleren und unmittelbareren Speicherung? Wie kann ein Datentechniker ändern, wie schnell Daten aus einer Datenbank abgerufen werden?

Typische Webanwendungen rufen häufig verwendete Daten wie das Profil oder den Namen eines Benutzers sehr oft ab. Wenn alle Daten in einer Datenbank enthalten sind, dann die Anzahl der Treffer der Datenbankserver wird übertrieben und unnötig sein. Daher wird eine schnellere und unmittelbarere Speicherlösung benötigt.

Während dies die Serverlast reduziert, bereitet es dem Datentechniker, dem Backend-Team und dem DevOps-Team auch zwei Kopfschmerzen. Zunächst benötigen Sie jetzt eine Datenbank, die eine schnellere Lesezeit hat als Ihre Haupt-SQL- oder NoSQL-Datenbank. Allerdings müssen die Inhalte beider Datenbanken letztendlich übereinstimmen. (Willkommen beim Problem der Zustandskonsistenz zwischen Datenbanken! Viel Spaß.)

Das zweite Problem ist, dass sich DevOps jetzt um Skalierbarkeit, Redundanz usw. für die neue Cache-Datenbank kümmern muss. Im nächsten Abschnitt tauchen Sie mithilfe von Redis in Probleme wie diese ein.



F7:Verwendung von Cache-Datenbanken

Möglicherweise haben Sie aus der Einführung genügend Informationen erhalten, um diese Frage zu beantworten! Eine Cache-Datenbank ist eine schnelle Speicherlösung zum Speichern kurzlebiger, strukturierter oder unstrukturierter Daten. Sie kann entsprechend Ihren Anforderungen partitioniert und skaliert werden, ist jedoch in der Regel viel kleiner als Ihre Hauptdatenbank. Aus diesem Grund kann sich Ihre Cache-Datenbank im Arbeitsspeicher befinden, sodass Sie nicht mehr von einer Festplatte lesen müssen.

Hinweis: Wenn Sie jemals Wörterbücher in Python verwendet haben, folgt Redis der gleichen Struktur. Es ist ein Schlüsselwertspeicher, in dem Sie SET können und GET Daten wie ein Python dict .

Wenn eine Anfrage eingeht, überprüfen Sie zuerst die Cache-Datenbank, dann die Hauptdatenbank. Auf diese Weise können Sie verhindern, dass unnötige und sich wiederholende Anfragen den Server der Hauptdatenbank erreichen. Da eine Cache-Datenbank eine geringere Lesezeit hat, profitieren Sie auch von einer Leistungssteigerung!

Sie können pip verwenden, um die erforderliche Bibliothek zu installieren:

$ pip install redis

Betrachten Sie nun eine Anfrage, um den Namen des Benutzers von seiner ID abzurufen:

import redis
from datetime import timedelta

# In a real web application, configuration is obtained from settings or utils
r = redis.Redis()

# Assume this is a getter handling a request
def get_name(request, *args, **kwargs):
    id = request.get('id')
    if id in r:
        return r.get(id)  # Assume that we have an {id: name} store
    else:
        # Get data from the main DB here, assume we already did it
        name = 'Bob'
        # Set the value in the cache database, with an expiration time
        r.setex(id, timedelta(minutes=60), value=name)
        return name

Dieser Code prüft anhand der id, ob der Name in Redis steht Schlüssel. Wenn nicht, wird der Name mit einer Ablaufzeit versehen, die Sie verwenden, da der Cache nur von kurzer Dauer ist.

Was ist nun, wenn Ihr Gesprächspartner Sie fragt, was an diesem Code falsch ist? Ihre Antwort sollte sein, dass es keine Ausnahmebehandlung gibt! Datenbanken können viele Probleme haben, wie z. B. unterbrochene Verbindungen, daher ist es immer eine gute Idee, diese Ausnahmen abzufangen.




Fragen zu Entwurfsmustern und ETL-Konzepten

In großen Anwendungen verwenden Sie oft mehr als einen Datenbanktyp. Tatsächlich ist es möglich, PostgreSQL, MongoDB und Redis in nur einer Anwendung zu verwenden! Ein herausforderndes Problem ist der Umgang mit Zustandsänderungen zwischen Datenbanken, wodurch der Entwickler mit Konsistenzproblemen konfrontiert wird. Betrachten Sie das folgende Szenario:

  1. Ein Wert in Datenbank #1 wird aktualisiert.
  2. Derselbe Wert in Datenbank Nr. 2 wird beibehalten (nicht aktualisiert).
  3. Eine Abfrage wird auf Datenbank Nr. 2 ausgeführt.

Jetzt haben Sie ein inkonsistentes und veraltetes Ergebnis! Die von der zweiten Datenbank zurückgegebenen Ergebnisse spiegeln nicht den aktualisierten Wert in der ersten wider. Dies kann bei zwei beliebigen Datenbanken passieren, ist aber besonders häufig, wenn die Hauptdatenbank eine NoSQL-Datenbank ist und Informationen zu Abfragezwecken in SQL umgewandelt werden.

Datenbanken können Hintergrundarbeiter haben, um solche Probleme anzugehen. Diese Arbeiter extrahieren Daten aus einer Datenbank, transformieren es irgendwie und laden es in die Zieldatenbank. Wenn Sie von einer NoSQL-Datenbank zu einer SQL-Datenbank konvertieren, führt der ETL-Prozess (Extract, Transform, Load) die folgenden Schritte aus:

  1. Auszug: Es gibt einen MongoDB-Trigger, wenn ein Datensatz erstellt, aktualisiert usw. wird. Eine Callback-Funktion wird asynchron in einem separaten Thread aufgerufen.
  2. Transformation: Teile des Datensatzes werden extrahiert, normalisiert und in die richtige Datenstruktur (oder Zeile) eingefügt, um in SQL eingefügt zu werden.
  3. Laden: Die SQL-Datenbank wird stapelweise oder als einzelner Datensatz für Schreibvorgänge mit hohem Volumen aktualisiert.

Dieser Workflow ist in Finanz-, Spiel- und Berichtsanwendungen weit verbreitet. In diesen Fällen erfordert das sich ständig ändernde Schema eine NoSQL-Datenbank, aber Berichte, Analysen und Aggregationen erfordern eine SQL-Datenbank.


Q8:ETL-Herausforderungen

Es gibt mehrere herausfordernde Konzepte in ETL, darunter die folgenden:

  • Große Daten
  • Stateful-Probleme
  • Asynchrone Worker
  • Typanpassung

Die Liste geht weiter! Da die Schritte im ETL-Prozess jedoch klar definiert und logisch sind, kümmern sich die Daten- und Back-End-Ingenieure in der Regel mehr um Leistung und Verfügbarkeit als um die Implementierung.

Wenn Ihre Anwendung Tausende von Datensätzen pro Sekunde in MongoDB schreibt, muss Ihr ETL-Worker mit dem Transformieren, Laden und Bereitstellen der Daten an den Benutzer in der angeforderten Form Schritt halten. Geschwindigkeit und Latenz können zu einem Problem werden, daher sind diese Worker normalerweise in schnellen Sprachen geschrieben. Sie können kompilierten Code für den Transformationsschritt verwenden, um die Dinge zu beschleunigen, da dieser Teil normalerweise CPU-gebunden ist.

Hinweis: Mehrfachverarbeitung und Trennung von Arbeitern sind andere Lösungen, die Sie vielleicht in Betracht ziehen sollten.

Wenn Sie mit vielen CPU-intensiven Funktionen zu tun haben, sollten Sie sich vielleicht Numba ansehen. Diese Bibliothek kompiliert Funktionen, um sie bei der Ausführung schneller zu machen. Das Beste daran ist, dass dies einfach in Python implementiert werden kann, obwohl es einige Einschränkungen gibt, welche Funktionen in diesen kompilierten Funktionen verwendet werden können.



Q9:Designmuster in Big Data

Stellen Sie sich vor, Amazon muss ein Empfehlungssystem erstellen Benutzern geeignete Produkte vorzuschlagen. Das Data-Science-Team braucht Daten und davon jede Menge! Sie wenden sich an Sie, den Data Engineer, und bitten Sie, ein separates Staging-Datenbank-Warehouse zu erstellen. Dort werden sie die Daten bereinigen und transformieren.

Sie könnten schockiert sein, eine solche Anfrage zu erhalten. Wenn Sie über Terabytes an Daten verfügen, benötigen Sie mehrere Maschinen, um all diese Informationen zu verarbeiten. Eine Datenbankaggregationsfunktion kann eine sehr komplexe Operation sein. Wie können Sie relativ große Daten effizient abfragen, aggregieren und nutzen?

Apache hatte ursprünglich MapReduce eingeführt, das auf map, shuffle, Reduce folgt Arbeitsablauf. Die Idee ist, unterschiedliche Daten auf getrennten Maschinen, auch Cluster genannt, abzubilden. Anschließend können Sie die nach einem Schlüssel gruppierten Daten bearbeiten und die Daten schließlich in der letzten Phase aggregieren.

Dieser Workflow wird heute noch verwendet, ist aber in letzter Zeit zugunsten von Spark verblasst. Das Entwurfsmuster bildet jedoch die Grundlage der meisten Big-Data-Workflows und ist ein äußerst faszinierendes Konzept. Weitere Informationen zu MapReduce finden Sie bei IBM Analytics.



F10:Gemeinsame Aspekte des ETL-Prozesses und Big-Data-Workflows

Sie mögen denken, dass dies eine ziemlich seltsame Frage ist, aber es ist einfach eine Überprüfung Ihrer Informatikkenntnisse sowie Ihrer allgemeinen Designkenntnisse und -erfahrung.

Beide Workflows folgen dem Producer-Consumer Muster. Ein Worker (der Produzent) produziert Daten irgendeiner Art und gibt sie an eine Pipeline aus. Diese Pipeline kann viele Formen annehmen, einschließlich Netzwerknachrichten und Trigger. Nachdem der Produzent die Daten ausgegeben hat, konsumiert und nutzt der Konsument sie. Diese Worker arbeiten normalerweise asynchron und werden in separaten Prozessen ausgeführt.

Sie können den Producer mit den Extraktions- und Transformationsschritten des ETL-Prozesses vergleichen. Ebenso in Big Data der Mapper kann als Erzeuger angesehen werden, während der Reduzierer ist effektiv der Verbraucher. Diese Trennung von Anliegen ist äußerst wichtig und effektiv bei der Entwicklung und dem Architekturdesign von Anwendungen.




Schlussfolgerung

Herzliche Glückwünsche! Sie haben viel gelernt und mehrere Interviewfragen für Data Engineers beantwortet. Sie verstehen jetzt ein bisschen mehr über die vielen verschiedenen Aufgaben, die ein Datentechniker tragen kann, sowie über Ihre Verantwortlichkeiten in Bezug auf Datenbanken, Design und Workflow.

Ausgestattet mit diesem Wissen können Sie jetzt:

  • Verwenden Sie Python mit SQL, NoSQL und Cache-Datenbanken
  • Verwenden Sie Python in ETL- und Abfrageanwendungen
  • Planen Sie Projekte im Voraus und behalten Sie Design und Arbeitsablauf im Hinterkopf

Während die Fragen in Vorstellungsgesprächen vielfältig sein können, wurden Sie mit mehreren Themen konfrontiert und haben gelernt, in vielen verschiedenen Bereichen der Informatik über den Tellerrand hinauszuschauen. Jetzt bist du bereit für ein tolles Vorstellungsgespräch!