Naar sollicitatiegesprekken gaan kan een tijdrovend en vermoeiend proces zijn, en technische sollicitatiegesprekken kunnen zelfs nog meer stress opleveren! Deze tutorial is bedoeld om je voor te bereiden op enkele veelgestelde vragen die je tegenkomt tijdens je interview met data-engineers. Je leert hoe je vragen over databases, Python en SQL kunt beantwoorden.
Aan het einde van deze tutorial kun je:
- Begrijp veelvoorkomende interviewvragen voor data-engineers
- Onderscheid maken tussen relationele en niet-relationele databases
- Stel databases in met Python
- Gebruik Python voor het opvragen van gegevens
Gratis download: Ontvang een voorbeeldhoofdstuk uit Python Tricks:The Book dat je de beste praktijken van Python laat zien met eenvoudige voorbeelden die je direct kunt toepassen om mooiere + Pythonic-code te schrijven.
Een data-ingenieur worden
De rol van data-engineering kan enorm en gevarieerd zijn. Je moet praktische kennis hebben van meerdere technologieën en concepten. Data engineers zijn flexibel in hun denken. Als gevolg hiervan kunnen ze bekwaam zijn in meerdere onderwerpen, zoals databases, softwareontwikkeling, DevOps en big data.
Wat doet een data-engineer?
Gezien de gevarieerde vaardigheden kan een rol in data-engineering veel verschillende functiebeschrijvingen omvatten. Een data-engineer kan verantwoordelijk zijn voor database-ontwerp, schema-ontwerp en het maken van meerdere database-oplossingen. Bij dit werk kan ook een databasebeheerder betrokken zijn.
Als data-engineer , zou je kunnen fungeren als een brug tussen de database en de data science-teams. In dat geval ben je ook verantwoordelijk voor het opschonen en voorbereiden van gegevens. Gaat het om big data, dan is het jouw taak om voor die data een efficiënte oplossing te bedenken. Dit werk kan overlappen met de DevOps-rol.
U moet ook efficiënte gegevensquery's maken voor rapportage en analyse. Mogelijk moet u met meerdere databases communiceren of opgeslagen procedures schrijven. Voor veel oplossingen, zoals websites of services met veel verkeer, kan er meer dan één database aanwezig zijn. In deze gevallen is de data-engineer verantwoordelijk voor het opzetten van de databases, het onderhouden ervan en het uitwisselen van gegevens tussen de databases.
Hoe kan Python data-ingenieurs helpen?
Python staat bekend als het Zwitserse zakmes van programmeertalen. Het is vooral handig in datawetenschap, backend-systemen en server-side scripting. Dat komt omdat Python sterke typen, eenvoudige syntaxis en een overvloed aan bibliotheken van derden heeft om te gebruiken. Panda's, SciPy, Tensorflow, SQLAlchemy en NumPy zijn enkele van de meest gebruikte bibliotheken in productie in verschillende sectoren.
Het belangrijkste is dat Python de ontwikkeltijd verkort, wat minder kosten voor bedrijven betekent. Voor een data-engineer is de meeste uitvoering van code database-gebonden, niet CPU-gebonden. Daarom is het logisch om te profiteren van de eenvoud van Python, zelfs ten koste van tragere prestaties in vergelijking met gecompileerde talen zoals C# en Java.
Sollicitatievragen voor Data Engineer beantwoorden
Nu je weet wat je rol zou kunnen zijn, is het tijd om te leren hoe je een aantal interviewvragen voor data-engineers kunt beantwoorden! Hoewel er veel te bespreken valt, zie je in de hele tutorial praktische Python-voorbeelden om je op weg te helpen.
Vragen over relationele databases
Databases zijn een van de meest cruciale componenten in een systeem. Zonder hen kan er geen staat en geen geschiedenis zijn. Hoewel u databaseontwerp misschien niet als een prioriteit beschouwde, weet u dat dit een aanzienlijke invloed kan hebben op hoe snel uw pagina wordt geladen. In de afgelopen jaren hebben verschillende grote bedrijven verschillende nieuwe tools en technieken geïntroduceerd:
- NoSQL
- Cache-databases
- Grafiekdatabases
- NoSQL-ondersteuning in SQL-databases
Deze en andere technieken zijn uitgevonden om te proberen de snelheid te verhogen waarmee databases verzoeken verwerken. U zult waarschijnlijk over deze concepten moeten praten in uw interview met data-ingenieurs, dus laten we enkele vragen bespreken!
Q1:relationele versus niet-relationele databases
Een relationele database is er een waar gegevens worden opgeslagen in de vorm van een tabel. Elke tabel heeft een schema , wat de kolommen en typen zijn die een record moet hebben. Elk schema moet ten minste één primaire sleutel hebben die die record op unieke wijze identificeert. Met andere woorden, er zijn geen dubbele rijen in uw database. Bovendien kan elke tabel worden gerelateerd aan andere tabellen met behulp van externe sleutels.
Een belangrijk aspect van relationele databases is dat een wijziging in een schema op alle records moet worden toegepast. Dit kan soms leiden tot breuken en grote kopzorgen tijdens migraties. Niet-relationele databases dingen op een andere manier aanpakken. Ze zijn inherent schemaloos, wat betekent dat records kunnen worden opgeslagen met verschillende schema's en met een andere, geneste structuur. Records kunnen nog steeds primaire sleutels hebben, maar een wijziging in het schema gebeurt per item.
U moet een snelheidsvergelijkingstest uitvoeren op basis van het type functie dat wordt uitgevoerd. U kunt kiezen voor INSERT
, UPDATE
, DELETE
, of een andere functie. Schema-ontwerp, indices, het aantal aggregaties en het aantal records zijn ook van invloed op deze analyse, dus u moet grondig testen. U zult later meer leren over hoe u dit kunt doen.
Databases verschillen ook in schaalbaarheid . Een niet-relationele database kan minder lastig zijn om te verspreiden. Dat komt omdat een verzameling gerelateerde records eenvoudig op een bepaald knooppunt kan worden opgeslagen. Aan de andere kant vereisen relationele databases meer denkwerk en maken ze meestal gebruik van een master-slave-systeem.
Een SQLite-voorbeeld
Nu je hebt beantwoord wat relationele databases zijn, is het tijd om in Python te duiken! SQLite is een handige database die u op uw lokale computer kunt gebruiken. De database is een enkel bestand, waardoor het ideaal is voor prototyping-doeleinden. Importeer eerst de vereiste Python-bibliotheek en maak een nieuwe database aan:
import sqlite3
db = sqlite3.connect(':memory:') # Using an in-memory database
cur = db.cursor()
U bent nu verbonden met een database in het geheugen en heeft uw cursorobject klaar voor gebruik.
Vervolgens maakt u de volgende drie tabellen:
- Klant: Deze tabel bevat een primaire sleutel en de voor- en achternaam van de klant.
- Artikelen: Deze tabel bevat een primaire sleutel, de itemnaam en de itemprijs.
- Gekochte artikelen :Deze tabel bevat een bestelnummer, datum en prijs. Het maakt ook verbinding met de primaire sleutels in de tabellen Artikelen en Klant.
Nu u een idee heeft van hoe uw tabellen eruit zullen zien, kunt u ze gaan maken:
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) )''')
Je hebt een zoekopdracht doorgegeven aan cur.execute()
om uw drie tabellen te maken.
De laatste stap is om uw tabellen met gegevens te vullen:
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
Nu er in elke tabel een paar records staan, kunt u deze gegevens gebruiken om nog enkele interviewvragen voor data-engineers te beantwoorden.
Q2:SQL-aggregatiefuncties
Aggregatiefuncties zijn degenen die een wiskundige bewerking uitvoeren op een resultaatset. Enkele voorbeelden zijn AVG
, COUNT
, MIN
, MAX
, en SUM
. Vaak heb je GROUP BY
. nodig en HAVING
clausules om deze aggregaties aan te vullen. Een handige aggregatiefunctie is AVG
, die u kunt gebruiken om het gemiddelde van een bepaalde resultatenset te berekenen:
>>> cur.execute('''SELECT itemid, AVG(price) FROM BoughtItem GROUP BY itemid''')
>>> print(cur.fetchall())
[(1, 10.2), (2, 11.73), (3, 189.995)]
Hier heeft u de gemiddelde prijs opgehaald voor elk van de gekochte artikelen in uw database. Je kunt zien dat het item met een itemid
van 1
heeft een gemiddelde prijs van $10,20.
Om de bovenstaande uitvoer begrijpelijker te maken, kunt u de itemnaam weergeven in plaats van de itemid
:
>>> 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)]
Nu zie je gemakkelijker dat het item met een gemiddelde prijs van $10,20 de USB
. is .
Een andere nuttige aggregatie is SUM
. U kunt deze functie gebruiken om het totale bedrag weer te geven dat elke klant heeft uitgegeven:
>>> 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)]
Gemiddeld gaf de klant genaamd Amy ongeveer $ 180 uit, terwijl Rob slechts $ 11,23 uitgaf!
Als uw interviewer van databases houdt, wilt u misschien wat meer informatie over geneste zoekopdrachten, join-typen en de stappen die een relationele database neemt om uw zoekopdracht uit te voeren.
Q3:SQL-query's versnellen
Snelheid hangt af van verschillende factoren, maar wordt vooral beïnvloed door hoeveel van elk van de volgende factoren aanwezig zijn:
- Doe mee
- Aggregaties
- Overgangen
- Records
Hoe groter het aantal joins, hoe hoger de complexiteit en hoe groter het aantal traversals in tabellen. Meerdere joins zijn vrij duur om uit te voeren op enkele duizenden records met meerdere tabellen, omdat de database ook het tussenresultaat in de cache moet opslaan! Op dit punt begint u misschien na te denken over hoe u uw geheugen kunt vergroten.
Snelheid wordt ook beïnvloed door het al dan niet zijn van indexen aanwezig in de databank. Indexen zijn uiterst belangrijk en stellen u in staat snel door een tabel te zoeken en een overeenkomst te vinden voor een kolom die in de zoekopdracht is gespecificeerd.
Indexen sorteren de records ten koste van een hogere invoegtijd en enige opslag. Meerdere kolommen kunnen worden gecombineerd om een enkele index te maken. Bijvoorbeeld de kolommen date
en price
kan worden gecombineerd omdat uw zoekopdracht afhankelijk is van beide voorwaarden.
Q4:SQL-query's debuggen
De meeste databases bevatten een EXPLAIN QUERY PLAN
die de stappen beschrijft die de database neemt om de query uit te voeren. Voor SQLite kunt u deze functionaliteit inschakelen door EXPLAIN QUERY PLAN
toe te voegen voor een SELECT
verklaring:
>>> 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=?)')]
Deze zoekopdracht probeert de voornaam, de titel van het item, de oorspronkelijke prijs en de gekochte prijs weer te geven voor alle gekochte items.
Zo ziet het queryplan er zelf uit:
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=?)
Merk op dat de fetch-instructie in uw Python-code alleen de uitleg retourneert, maar niet de resultaten. Dat komt omdat EXPLAIN QUERY PLAN
is niet bedoeld om in productie te worden gebruikt.
Vragen over niet-relationele databases
In de vorige sectie heb je de verschillen tussen relationele en niet-relationele databases uiteengezet en SQLite met Python gebruikt. Nu ga je je concentreren op NoSQL. Je doel is om de sterke punten, verschillen en gebruiksscenario's ervan te benadrukken.
Een MongoDB-voorbeeld
U gebruikt dezelfde gegevens als voorheen, maar deze keer is uw database MongoDB. Deze NoSQL-database is op documenten gebaseerd en schaalt zeer goed. Allereerst moet u de vereiste Python-bibliotheek installeren:
$ pip install pymongo
Misschien wilt u ook de MongoDB Compass Community installeren. Het bevat een lokale IDE die perfect is voor het visualiseren van de database. Hiermee kunt u de gemaakte records zien, triggers maken en fungeren als visuele beheerder voor de database.
Opmerking: Om de code in dit gedeelte uit te voeren, hebt u een actieve databaseserver nodig. Raadpleeg Inleiding tot MongoDB en Python voor meer informatie over het instellen ervan.
Zo maakt u de database aan en voegt u wat gegevens in:
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)
Zoals je misschien hebt gemerkt, slaat MongoDB gegevensrecords op in verzamelingen , die het equivalent zijn van een lijst met woordenboeken in Python. In de praktijk slaat MongoDB BSON-documenten op.
Q5:gegevens opvragen met MongoDB
Laten we proberen het BoughtItem
te repliceren tabel eerst, zoals u deed in SQL. Hiervoor moet u een nieuw veld toevoegen aan een klant. MongoDB's documentatie specificeert dat de sleutelwoordoperator set kan worden gebruikt om een record bij te werken zonder alle bestaande velden te hoeven schrijven:
# 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
}
]
},
}
)
Merk op hoe u extra velden heeft toegevoegd aan de customer
zonder het schema vooraf expliciet te definiëren. Handig!
U kunt zelfs een andere klant updaten met een licht gewijzigd schema:
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
Net als bij SQL kunnen op documenten gebaseerde databases ook query's en aggregaties uitvoeren. De functionaliteit kan echter zowel syntactisch als in de onderliggende uitvoering verschillen. Het is je misschien opgevallen dat MongoDB de $
. reserveert teken om een commando of aggregatie op de records op te geven, zoals $group
. U kunt meer over dit gedrag te weten komen in de officiële documenten.
U kunt query's uitvoeren net zoals u deed in SQL. Om te beginnen kunt u een index maken:
>>>>>> customers.create_index([("name", pymongo.DESCENDING)])
Dit is optioneel, maar het versnelt zoekopdrachten waarvoor naamopzoekingen nodig zijn.
Vervolgens kunt u de klantnamen in oplopende volgorde sorteren:
>>>>>> items = customers.find().sort("name", pymongo.ASCENDING)
U kunt de gekochte artikelen ook doorlopen en afdrukken:
>>>>>> 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}]
U kunt zelfs een lijst met unieke namen in de database ophalen:
>>>>>> customers.distinct("firstname")
['Bob', 'Amy', 'Rob']
Nu u de namen van de klanten in uw database weet, kunt u een query maken om informatie over hen op te halen:
>>>>>> 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 is de equivalente SQL-query:
SELECT firstname, boughtitems FROM customers WHERE firstname LIKE ('Bob', 'Amy')
Merk op dat hoewel de syntaxis slechts in geringe mate kan verschillen, er een drastisch verschil is in de manier waarop query's onder de motorkap worden uitgevoerd. Dit is te verwachten vanwege de verschillende querystructuren en gebruiksscenario's tussen SQL- en NoSQL-databases.
Q6:NoSQL versus SQL
Als u een voortdurend veranderend schema heeft, zoals informatie over financiële regelgeving, kan NoSQL de records wijzigen en gerelateerde informatie nesten. Stel je het aantal joins voor dat je in SQL zou moeten doen als je acht orden van nesting had! Deze situatie komt echter vaker voor dan je zou denken.
Wat als u nu rapporten wilt maken, informatie over die financiële gegevens wilt extraheren en conclusies wilt trekken? In dit geval moet u complexe query's uitvoeren en SQL is in dit opzicht meestal sneller.
Opmerking: SQL-databases, met name PostgreSQL, hebben ook een functie uitgebracht waarmee opvraagbare JSON-gegevens kunnen worden ingevoegd als onderdeel van een record. Hoewel dit het beste van twee werelden kan combineren, kan snelheid een punt van zorg zijn.
Het is sneller om ongestructureerde gegevens uit een NoSQL-database op te vragen dan om JSON-velden op te vragen uit een JSON-type kolom in PostgreSQL. Je kunt altijd een snelheidsvergelijkingstest doen voor een definitief antwoord.
Desalniettemin kan deze functie de behoefte aan een extra database verminderen. Soms worden gebeitste of geserialiseerde objecten in records opgeslagen in de vorm van binaire typen en vervolgens gedeserialiseerd bij het lezen.
Snelheid is echter niet de enige maatstaf. U wilt ook rekening houden met zaken als transacties, atomiciteit, duurzaamheid en schaalbaarheid. Transacties zijn belangrijk in financiële toepassingen, en dergelijke functies hebben voorrang.
Aangezien er een breed scala aan databases is, elk met zijn eigen functies, is het de taak van de data-engineer om een weloverwogen beslissing te nemen over welke database in elke toepassing moet worden gebruikt. Voor meer informatie kunt u de ACID-eigenschappen met betrekking tot databasetransacties lezen.
Mogelijk wordt u ook gevraagd welke andere databases u kent in uw interview met data-engineers. Er zijn verschillende andere relevante databases die door veel bedrijven worden gebruikt:
- Elastisch zoeken is zeer efficiënt bij het zoeken naar tekst. Het maakt gebruik van zijn op documenten gebaseerde database om een krachtige zoekfunctie te creëren.
- Newt DB combineert ZODB en de PostgreSQL JSONB-functie om een Python-vriendelijke NoSQL-database te creëren.
- InfluxDB wordt gebruikt in tijdreekstoepassingen om gebeurtenissen op te slaan.
De lijst gaat maar door, maar dit illustreert hoe een grote verscheidenheid aan beschikbare databases allemaal inspelen op hun niche-industrie.
Vragen over cachedatabases
Cachedatabases veelgebruikte gegevens bewaren. Ze leven naast de belangrijkste SQL- en NoSQL-databases. Hun doel is om de belasting te verlichten en verzoeken sneller te behandelen.
Een Redis-voorbeeld
U hebt SQL- en NoSQL-databases voor langetermijnopslagoplossingen behandeld, maar hoe zit het met snellere, directere opslag? Hoe kan een data-engineer veranderen hoe snel gegevens uit een database worden opgehaald?
Typische webapplicaties halen vaak veelgebruikte gegevens op, zoals het profiel of de naam van een gebruiker. Als alle gegevens zich in één database bevinden, dan is het aantal hits de databaseserver krijgt, wordt overdreven en onnodig. Daarom is een snellere, directere opslagoplossing nodig.
Hoewel dit de serverbelasting vermindert, veroorzaakt het ook twee kopzorgen voor de data-engineer, het backend-team en het DevOps-team. Ten eerste heb je nu een database nodig die een snellere leestijd heeft dan je belangrijkste SQL- of NoSQL-database. De inhoud van beide databases moet uiteindelijk echter overeenkomen. (Welkom bij het probleem van consistentie van staten tussen databases! Veel plezier.)
De tweede hoofdpijn is dat DevOps zich nu zorgen moet maken over schaalbaarheid, redundantie, enzovoort voor de nieuwe cachedatabase. In het volgende gedeelte ga je met behulp van Redis dieper in op dit soort problemen.
Q7:Cachedatabases gebruiken
Wellicht heb je uit de inleiding genoeg informatie gekregen om deze vraag te beantwoorden! Een cachedatabase is een snelle opslagoplossing die wordt gebruikt om kortstondige, gestructureerde of ongestructureerde gegevens op te slaan. Het kan worden gepartitioneerd en geschaald volgens uw behoeften, maar het is meestal veel kleiner dan uw hoofddatabase. Hierdoor kan uw cachedatabase zich in het geheugen bevinden, zodat u de noodzaak om van een schijf te lezen kunt omzeilen.
Opmerking: Als je ooit woordenboeken in Python hebt gebruikt, volgt Redis dezelfde structuur. Het is een sleutel-waarde winkel, waar u SET
. kunt en GET
gegevens net als een Python dict
.
Als er een verzoek binnenkomt, controleer je eerst de cachedatabase en vervolgens de hoofddatabase. Op deze manier kunt u voorkomen dat onnodige en herhaalde verzoeken de server van de hoofddatabase bereiken. Aangezien een cachedatabase een lagere leestijd heeft, profiteert u ook van een prestatieverhoging!
U kunt pip gebruiken om de vereiste bibliotheek te installeren:
$ pip install redis
Overweeg nu een verzoek om de naam van de gebruiker uit hun ID te halen:
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
Deze code controleert of de naam in Redis staat met behulp van de id
sleutel. Zo niet, dan is de naam ingesteld met een vervaltijd, die u gebruikt omdat de cache van korte duur is.
Wat als uw interviewer u vraagt wat er mis is met deze code? Uw antwoord zou moeten zijn dat er geen uitzonderingsbehandeling is! Databases kunnen veel problemen hebben, zoals verbroken verbindingen, dus het is altijd een goed idee om te proberen die uitzonderingen op te vangen.
Vragen over ontwerppatronen en ETL-concepten
In grote applicaties gebruikt u vaak meer dan één type database. Het is zelfs mogelijk om PostgreSQL, MongoDB en Redis allemaal binnen slechts één applicatie te gebruiken! Een uitdagend probleem is het omgaan met statusveranderingen tussen databases, wat de ontwikkelaar blootstelt aan consistentieproblemen. Overweeg het volgende scenario:
- Een waarde in Database #1 is bijgewerkt.
- Dezelfde waarde in Database #2 wordt hetzelfde gehouden (niet bijgewerkt).
- Een vraag wordt uitgevoerd op Database #2.
Nu heb je een inconsistent en verouderd resultaat! De resultaten die uit de tweede database worden geretourneerd, komen niet overeen met de bijgewerkte waarde in de eerste. Dit kan met twee willekeurige databases gebeuren, maar het komt vooral vaak voor wanneer de hoofddatabase een NoSQL-database is en informatie wordt omgezet in SQL voor querydoeleinden.
Databases kunnen achtergrondmedewerkers hebben om dergelijke problemen aan te pakken. Deze werkers extracten gegevens uit één database, transformeren het op de een of andere manier, en laad het in de doeldatabase. Wanneer u converteert van een NoSQL-database naar een SQL-database, voert het proces Extract, Transform, Load (ETL) de volgende stappen uit:
- Extract: Er is een MongoDB-trigger wanneer een record wordt gemaakt, bijgewerkt, enzovoort. Een callback-functie wordt asynchroon aangeroepen op een aparte thread.
- Transformeren: Delen van het record worden geëxtraheerd, genormaliseerd en in de juiste gegevensstructuur (of rij) geplaatst om in SQL te worden ingevoegd.
- Laden: De SQL-database wordt in batches bijgewerkt, of als een enkel record voor schrijfbewerkingen met een hoog volume.
Deze workflow is vrij gebruikelijk in financiële, gaming- en rapportagetoepassingen. In deze gevallen vereist het voortdurend veranderende schema een NoSQL-database, maar voor rapportage, analyse en aggregaties is een SQL-database vereist.
Q8:ETL-uitdagingen
Er zijn verschillende uitdagende concepten in ETL, waaronder de volgende:
- Big data
- Statistische problemen
- Asynchrone werkers
- Type-overeenkomst
De lijst gaat verder! Omdat de stappen in het ETL-proces echter goed gedefinieerd en logisch zijn, zullen de data- en backend-engineers zich doorgaans meer zorgen maken over de prestaties en beschikbaarheid dan over de implementatie.
Als uw toepassing duizenden records per seconde naar MongoDB schrijft, moet uw ETL-medewerker de gegevens in de gevraagde vorm transformeren, laden en aan de gebruiker leveren. Snelheid en latentie kunnen een probleem worden, dus deze werknemers zijn meestal in snelle talen geschreven. Je kunt gecompileerde code gebruiken voor de transformatiestap om dingen te versnellen, aangezien dit deel meestal CPU-gebonden is.
Opmerking: Meervoudige verwerking en scheiding van werknemers zijn andere oplossingen die u zou kunnen overwegen.
Als je te maken hebt met veel CPU-intensieve functies, wil je misschien Numba eens bekijken. Deze bibliotheek compileert functies om ze sneller uit te voeren. Het beste van alles is dat dit eenvoudig kan worden geïmplementeerd in Python, hoewel er enkele beperkingen zijn aan de functies die in deze gecompileerde functies kunnen worden gebruikt.
V9:Ontwerppatronen in Big Data
Stel je voor dat Amazon een aanbevelingssysteem moet maken om gebruikers geschikte producten voor te stellen. Het data science-team heeft data nodig en veel! Ze gaan naar jou, de data engineer, en vragen je om een apart staging database warehouse te maken. Daar zullen ze de gegevens opschonen en transformeren.
Je zou geschokt kunnen zijn als je zo'n verzoek ontvangt. Als je terabytes aan gegevens hebt, heb je meerdere machines nodig om al die informatie te verwerken. Een database-aggregatiefunctie kan een zeer complexe operatie zijn. Hoe kunt u op een efficiënte manier gegevens opvragen, aggregeren en gebruiken?
Apache had aanvankelijk MapReduce geïntroduceerd, dat de map, shuffle, reduce . volgt werkstroom. Het idee is om verschillende data op aparte machines in kaart te brengen, ook wel clusters genoemd. Vervolgens kunt u aan de gegevens werken, gegroepeerd op een sleutel, en ten slotte de gegevens samenvoegen in de laatste fase.
Deze workflow wordt nog steeds gebruikt, maar is de laatste tijd aan het vervagen ten gunste van Spark. Het ontwerppatroon vormt echter de basis van de meeste big data-workflows en is een zeer intrigerend concept. U kunt meer lezen over MapReduce bij IBM Analytics.
Q10:Gemeenschappelijke aspecten van het ETL-proces en big data-workflows
U vindt dit misschien een nogal vreemde vraag, maar het is gewoon een controle van uw computerwetenschappelijke kennis, evenals uw algemene ontwerpkennis en -ervaring.
Beide workflows volgen de Producer-Consumer patroon. Een werknemer (de Producent) produceert een of andere soort gegevens en voert deze uit naar een pijplijn. Deze pijplijn kan vele vormen aannemen, waaronder netwerkberichten en triggers. Nadat de Producent de gegevens heeft uitgevoerd, consumeert en maakt de Consument er gebruik van. Deze werknemers werken doorgaans op een asynchrone manier en worden uitgevoerd in afzonderlijke processen.
U kunt de Producer vergelijken met de extractie- en transformatiestappen van het ETL-proces. Evenzo, in big data, de mapper kan worden gezien als de Producer, terwijl de reducer is in feite de Consument. Deze scheiding van zorgen is uiterst belangrijk en effectief bij de ontwikkeling en het architectuurontwerp van applicaties.
Conclusie
Gefeliciteerd! Je hebt veel kennis opgedaan en verschillende interviewvragen voor data-engineers beantwoord. U begrijpt nu wat meer over de vele verschillende petten die een data-engineer kan dragen, en wat uw verantwoordelijkheden zijn met betrekking tot databases, ontwerp en workflow.
Gewapend met deze kennis, kun je nu:
- Gebruik Python met SQL-, NoSQL- en cachedatabases
- Gebruik Python in ETL- en query-applicaties
- Plan projecten van tevoren, rekening houdend met ontwerp en workflow
Hoewel interviewvragen kunnen variëren, ben je blootgesteld aan meerdere onderwerpen en heb je geleerd buiten de gebaande paden te denken op veel verschillende gebieden van de informatica. Nu ben je klaar voor een geweldig interview!