Database
 sql >> Base de Dados >  >> RDS >> Database

Perguntas da entrevista do engenheiro de dados com Python


Ir a entrevistas pode ser um processo demorado e cansativo, e entrevistas técnicas podem ser ainda mais estressantes! Este tutorial tem como objetivo prepará-lo para algumas perguntas comuns que você encontrará durante sua entrevista de engenheiro de dados. Você aprenderá a responder perguntas sobre bancos de dados, Python e SQL.

Ao final deste tutorial, você será capaz de:
  • Entenda as perguntas comuns da entrevista do engenheiro de dados
  • Distinguir entre bancos de dados relacionais e não relacionais
  • Configurar bancos de dados usando Python
  • Usar Python para consultar dados

Download gratuito: Obtenha um capítulo de amostra do Python Tricks:The Book que mostra as melhores práticas do Python com exemplos simples que você pode aplicar instantaneamente para escrever um código mais bonito + Pythonico.

Tornar-se um engenheiro de dados


O papel da engenharia de dados pode ser vasto e variado. Você precisará ter um conhecimento prático de várias tecnologias e conceitos. Os engenheiros de dados são flexíveis em seu pensamento. Como resultado, eles podem ser proficientes em vários tópicos, como bancos de dados, desenvolvimento de software, DevOps e big data.

O que faz um engenheiro de dados?


Dado seu conjunto variado de habilidades, uma função de engenharia de dados pode abranger muitas descrições de trabalho diferentes. Um engenheiro de dados pode ser responsável pelo design de banco de dados, design de esquema e criação de várias soluções de banco de dados. Esse trabalho também pode envolver um Administrador de Banco de Dados.

Como engenheiro de dados , você pode atuar como uma ponte entre o banco de dados e as equipes de ciência de dados. Nesse caso, você também será responsável pela limpeza e preparação dos dados. Se big data estiver envolvido, é seu trabalho criar uma solução eficiente para esses dados. Esse trabalho pode se sobrepor à função de DevOps.

Você também precisará fazer consultas de dados eficientes para relatórios e análises. Pode ser necessário interagir com vários bancos de dados ou gravar procedimentos armazenados. Para muitas soluções, como sites ou serviços de alto tráfego, pode haver mais de um banco de dados presente. Nesses casos, o engenheiro de dados é responsável por configurar os bancos de dados, mantê-los e transferir dados entre eles.


Como o Python pode ajudar os engenheiros de dados?


Python é conhecido por ser o canivete suíço das linguagens de programação. É especialmente útil em ciência de dados, sistemas de back-end e scripts do lado do servidor. Isso porque o Python tem digitação forte, sintaxe simples e uma abundância de bibliotecas de terceiros para usar. Pandas, SciPy, Tensorflow, SQLAlchemy e NumPy são algumas das bibliotecas mais usadas na produção em diferentes setores.

Mais importante ainda, o Python diminui o tempo de desenvolvimento, o que significa menos despesas para as empresas. Para um engenheiro de dados, a maior parte da execução de código é vinculada ao banco de dados, não à CPU. Por isso, faz sentido capitalizar a simplicidade do Python, mesmo ao custo de um desempenho mais lento quando comparado a linguagens compiladas como C# e Java.



Responder a perguntas da entrevista do engenheiro de dados


Agora que você sabe em que pode consistir sua função, é hora de aprender a responder a algumas perguntas da entrevista do engenheiro de dados! Embora haja muito terreno a percorrer, você verá exemplos práticos de Python ao longo do tutorial para guiá-lo ao longo do caminho.


Perguntas sobre bancos de dados relacionais


Os bancos de dados são um dos componentes mais importantes em um sistema. Sem eles, não pode haver Estado nem história. Embora você possa não ter considerado o design do banco de dados como uma prioridade, saiba que isso pode ter um impacto significativo na rapidez com que sua página é carregada. Nos últimos anos, várias grandes corporações introduziram várias novas ferramentas e técnicas:
  • NoSQL
  • Bancos de dados de cache
  • Bancos de dados gráficos
  • Suporte NoSQL em bancos de dados SQL

Essas e outras técnicas foram inventadas para tentar aumentar a velocidade com que os bancos de dados processam as solicitações. Você provavelmente precisará falar sobre esses conceitos em sua entrevista de engenheiro de dados, então vamos analisar algumas perguntas!

Q1:Bancos de dados relacionais x não relacionais


Um banco de dados relacional é aquele em que os dados são armazenados na forma de uma tabela. Cada tabela tem um esquema , que são as colunas e os tipos que um registro deve ter. Cada esquema deve ter pelo menos uma chave primária que identifique exclusivamente esse registro. Em outras palavras, não há linhas duplicadas em seu banco de dados. Além disso, cada tabela pode ser relacionada a outras tabelas usando chaves estrangeiras.

Um aspecto importante dos bancos de dados relacionais é que uma mudança em um esquema deve ser aplicada a todos os registros. Isso às vezes pode causar quebras e grandes dores de cabeça durante as migrações. Bancos de dados não relacionais lidar com as coisas de uma maneira diferente. Eles são inerentemente sem esquema, o que significa que os registros podem ser salvos com esquemas diferentes e com uma estrutura aninhada diferente. Os registros ainda podem ter chaves primárias, mas uma alteração no esquema é feita entrada por entrada.

Você precisaria realizar um teste de comparação de velocidade com base no tipo de função que está sendo executada. Você pode escolher INSERT , UPDATE , DELETE , ou outra função. O design do esquema, os índices, o número de agregações e o número de registros também afetarão essa análise, portanto, você precisará testar completamente. Você aprenderá mais sobre como fazer isso mais tarde.

Os bancos de dados também diferem em escalabilidade . Um banco de dados não relacional pode ser menos complicado de distribuir. Isso porque uma coleção de registros relacionados pode ser facilmente armazenada em um nó específico. Por outro lado, bancos de dados relacionais exigem mais reflexão e geralmente fazem uso de um sistema mestre-escravo.


Um exemplo SQLite


Agora que você respondeu o que são bancos de dados relacionais, é hora de explorar um pouco do Python! SQLite é um banco de dados conveniente que você pode usar em sua máquina local. O banco de dados é um único arquivo, o que o torna ideal para fins de prototipagem. Primeiro, importe a biblioteca Python necessária e crie um novo banco de dados:
import sqlite3

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

Agora você está conectado a um banco de dados na memória e tem seu objeto de cursor pronto para ser usado.

Em seguida, você criará as três tabelas a seguir:
  1. Cliente: Essa tabela conterá uma chave primária, bem como o nome e sobrenome do cliente.
  2. Itens: Esta tabela conterá uma chave primária, o nome do item e o preço do item.
  3. Itens comprados :Esta tabela conterá um número de pedido, data e preço. Ele também se conectará às chaves primárias nas tabelas Itens e Clientes.

Agora que você tem uma ideia de como serão suas tabelas, você pode ir em frente e criá-las:
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) )''')

Você passou uma consulta para cur.execute() para criar suas três tabelas.

A última etapa é preencher suas tabelas com dados:
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 

Agora que há alguns registros em cada tabela, você pode usar esses dados para responder a mais algumas perguntas da entrevista do engenheiro de dados.


Q2:Funções de agregação SQL


Funções de agregação são aqueles que realizam uma operação matemática em um conjunto de resultados. Alguns exemplos incluem AVG , COUNT , MIN , MAX e SUM . Muitas vezes, você precisará de GROUP BY e HAVING cláusulas para complementar essas agregações. Uma função de agregação útil é AVG , que você pode usar para calcular a média de um determinado conjunto de resultados:
>>>
>>> cur.execute('''SELECT itemid, AVG(price) FROM BoughtItem GROUP BY itemid''')
>>> print(cur.fetchall())
[(1, 10.2), (2, 11.73), (3, 189.995)]

Aqui, você recuperou o preço médio de cada um dos itens comprados em seu banco de dados. Você pode ver que o item com um itemid de 1 tem um preço médio de $ 10,20.

Para tornar a saída acima mais fácil de entender, você pode exibir o nome do item em vez do 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)]

Agora, você vê com mais facilidade que o item com preço médio de US$ 10,20 é o USB .

Outra agregação útil é SUM . Você pode usar esta função para exibir a quantia total de dinheiro que cada cliente gastou:
>>>
>>> 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)]

Em média, o cliente chamado Amy gastou cerca de US$ 180, enquanto Rob gastou apenas US$ 11,23!

Se o seu entrevistador gosta de bancos de dados, convém aprimorar as consultas aninhadas, os tipos de junção e as etapas que um banco de dados relacional executa para realizar sua consulta.


T3:Acelerando as consultas SQL


A velocidade depende de vários fatores, mas é afetada principalmente por quantos de cada um dos seguintes estão presentes:
  • Juntas
  • Agregações
  • Travessias
  • Registros

Quanto maior o número de junções, maior a complexidade e maior o número de travessias nas tabelas. Múltiplas junções são bastante caras para executar em vários milhares de registros envolvendo várias tabelas porque o banco de dados também precisa armazenar em cache o resultado intermediário! Neste ponto, você pode começar a pensar em como aumentar o tamanho da memória.

A velocidade também é afetada pela existência ou não de índices presentes no banco de dados. Os índices são extremamente importantes e permitem que você pesquise rapidamente em uma tabela e encontre uma correspondência para alguma coluna especificada na consulta.

Os índices classificam os registros ao custo de maior tempo de inserção, bem como algum armazenamento. Várias colunas podem ser combinadas para criar um único índice. Por exemplo, as colunas date e price podem ser combinados porque sua consulta depende de ambas as condições.


Q4:depuração de consultas SQL


A maioria dos bancos de dados inclui um EXPLAIN QUERY PLAN que descreve as etapas que o banco de dados executa para executar a consulta. Para SQLite, você pode habilitar essa funcionalidade adicionando EXPLAIN QUERY PLAN na frente de um SELECT demonstração:
>>>
>>> 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=?)')]

Esta consulta tenta listar o primeiro nome, título do item, preço original e preço comprado para todos os itens comprados.

Veja como é o próprio plano de consulta:
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=?)

Observe que a instrução fetch em seu código Python retorna apenas a explicação, mas não os resultados. Isso porque EXPLAIN QUERY PLAN não se destina a ser utilizado na produção.



Perguntas sobre bancos de dados não relacionais


Na seção anterior, você expôs as diferenças entre bancos de dados relacionais e não relacionais e usou SQLite com Python. Agora você vai se concentrar no NoSQL. Seu objetivo é destacar seus pontos fortes, diferenças e casos de uso.

Um exemplo do MongoDB


Você usará os mesmos dados de antes, mas desta vez seu banco de dados será o MongoDB. Esse banco de dados NoSQL é baseado em documentos e é muito bem dimensionado. Antes de mais nada, você precisará instalar a biblioteca Python necessária:
$ pip install pymongo

Você também pode querer instalar a Comunidade MongoDB Compass. Inclui um IDE local perfeito para visualizar o banco de dados. Com ele, você pode ver os registros criados, criar gatilhos e atuar como administrador visual do banco de dados.

Observação: Para executar o código nesta seção, você precisará de um servidor de banco de dados em execução. Para saber mais sobre como configurá-lo, confira Introdução ao MongoDB e Python.

Veja como você cria o banco de dados e insere alguns dados:
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)

Como você deve ter notado, o MongoDB armazena registros de dados em coleções , que são equivalentes a uma lista de dicionários em Python. Na prática, o MongoDB armazena documentos BSON.


Q5:consulta de dados com o MongoDB


Vamos tentar replicar o BoughtItem tabela primeiro, como você fez no SQL. Para fazer isso, você deve anexar um novo campo a um cliente. A documentação do MongoDB especifica que o operador de palavra-chave set pode ser usado para atualizar um registro sem ter que escrever todos os campos existentes:
# 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
                    }
                ]
            },
        }
    )

Observe como você adicionou campos adicionais ao customer sem definir explicitamente o esquema de antemão. Legal!

Na verdade, você pode atualizar outro cliente com um esquema ligeiramente alterado:
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

Semelhante ao SQL, os bancos de dados baseados em documentos também permitem que consultas e agregações sejam executadas. No entanto, a funcionalidade pode diferir sintaticamente e na execução subjacente. Na verdade, você deve ter notado que o MongoDB reserva o $ caractere para especificar algum comando ou agregação nos registros, como $group . Você pode aprender mais sobre esse comportamento nos documentos oficiais.

Você pode realizar consultas exatamente como fez no SQL. Para começar, você pode criar um índice:
>>>
>>> customers.create_index([("name", pymongo.DESCENDING)])

Isso é opcional, mas acelera as consultas que exigem pesquisas de nome.

Em seguida, você pode recuperar os nomes dos clientes classificados em ordem crescente:
>>>
>>> items = customers.find().sort("name", pymongo.ASCENDING)

Você também pode percorrer e imprimir os itens comprados:
>>>
>>> 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}]

Você pode até recuperar uma lista de nomes exclusivos no banco de dados:
>>>
>>> customers.distinct("firstname")
['Bob', 'Amy', 'Rob']

Agora que você conhece os nomes dos clientes em seu banco de dados, pode criar uma consulta para recuperar informações sobre eles:
>>>
>>> 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}]}

Aqui está a consulta SQL equivalente:
SELECT firstname, boughtitems FROM customers WHERE firstname LIKE ('Bob', 'Amy')

Observe que, embora a sintaxe possa diferir apenas um pouco, há uma diferença drástica na maneira como as consultas são executadas nos bastidores. Isso é esperado devido às diferentes estruturas de consulta e casos de uso entre os bancos de dados SQL e NoSQL.


Q6:NoSQL vs SQL


Se você tiver um esquema em constante mudança, como informações regulatórias financeiras, o NoSQL poderá modificar os registros e aninhar as informações relacionadas. Imagine o número de junções que você teria que fazer no SQL se tivesse oito ordens de aninhamento! No entanto, essa situação é mais comum do que você imagina.

Agora, e se você quiser gerar relatórios, extrair informações sobre esses dados financeiros e inferir conclusões? Nesse caso, você precisa executar consultas complexas e o SQL tende a ser mais rápido nesse aspecto.

Observação: Bancos de dados SQL, particularmente PostgreSQL, também lançaram um recurso que permite que dados JSON consultáveis ​​sejam inseridos como parte de um registro. Embora isso possa combinar o melhor dos dois mundos, a velocidade pode ser uma preocupação.

É mais rápido consultar dados não estruturados de um banco de dados NoSQL do que consultar campos JSON de uma coluna do tipo JSON no PostgreSQL. Você sempre pode fazer um teste de comparação de velocidade para obter uma resposta definitiva.

No entanto, esse recurso pode reduzir a necessidade de um banco de dados adicional. Às vezes, objetos em conserva ou serializados são armazenados em registros na forma de tipos binários e, em seguida, desserializados na leitura.

A velocidade não é a única métrica, no entanto. Você também vai querer levar em conta coisas como transações, atomicidade, durabilidade e escalabilidade. Transações são importantes em aplicações financeiras, e tais recursos têm precedência.

Como há uma grande variedade de bancos de dados, cada um com seus próprios recursos, é trabalho do engenheiro de dados tomar uma decisão informada sobre qual banco de dados usar em cada aplicativo. Para obter mais informações, você pode ler as propriedades ACID relacionadas às transações do banco de dados.

Você também pode ser perguntado sobre quais outros bancos de dados você conhece em sua entrevista de engenheiro de dados. Existem vários outros bancos de dados relevantes que são usados ​​por muitas empresas:
  • Pesquisa elástica é altamente eficiente na pesquisa de texto. Ele aproveita seu banco de dados baseado em documentos para criar uma poderosa ferramenta de pesquisa.
  • Newt DB combina ZODB e o recurso PostgreSQL JSONB para criar um banco de dados NoSQL compatível com Python.
  • InfluxDB é usado em aplicativos de série temporal para armazenar eventos.

A lista continua, mas isso ilustra como uma grande variedade de bancos de dados disponíveis atendem ao seu nicho de mercado.



Perguntas sobre bancos de dados de cache


Bancos de dados de cache armazenar dados acessados ​​com frequência. Eles vivem ao lado dos principais bancos de dados SQL e NoSQL. Seu objetivo é aliviar a carga e atender solicitações mais rapidamente.

Um exemplo do Redis


Você cobriu bancos de dados SQL e NoSQL para soluções de armazenamento de longo prazo, mas e quanto a um armazenamento mais rápido e imediato? Como um engenheiro de dados pode alterar a rapidez com que os dados são recuperados de um banco de dados?

Aplicativos da Web típicos recuperam dados comumente usados, como o perfil ou o nome de um usuário, com muita frequência. Se todos os dados estiverem contidos em um banco de dados, o número de ocorrências o servidor de banco de dados será exagerado e desnecessário. Como tal, é necessária uma solução de armazenamento mais rápida e imediata.

Embora isso reduza a carga do servidor, também cria duas dores de cabeça para o engenheiro de dados, a equipe de back-end e a equipe de DevOps. Primeiro, agora você precisará de algum banco de dados que tenha um tempo de leitura mais rápido que seu banco de dados SQL ou NoSQL principal. No entanto, o conteúdo de ambos os bancos de dados deve eventualmente corresponder. (Bem-vindo ao problema da consistência do estado entre bancos de dados! Aproveitar.)

A segunda dor de cabeça é que o DevOps agora precisa se preocupar com escalabilidade, redundância e assim por diante para o novo banco de dados de cache. Na próxima seção, você mergulhará em problemas como esses com a ajuda do Redis.


P7:Como usar bancos de dados de cache


Você pode ter obtido informações suficientes da introdução para responder a esta pergunta! Um banco de dados de cache é uma solução de armazenamento rápida usada para armazenar dados de curta duração, estruturados ou não estruturados. Ele pode ser particionado e dimensionado de acordo com suas necessidades, mas normalmente é muito menor em tamanho do que seu banco de dados principal. Por causa disso, seu banco de dados de cache pode residir na memória, permitindo que você ignore a necessidade de ler de um disco.

Observação: Se você já usou dicionários em Python, o Redis segue a mesma estrutura. É um armazenamento de valor-chave, onde você pode SET e GET dados como um Python dict .

Quando uma solicitação chega, você primeiro verifica o banco de dados de cache e, em seguida, o banco de dados principal. Dessa forma, você pode evitar que solicitações desnecessárias e repetitivas cheguem ao servidor do banco de dados principal. Como um banco de dados de cache tem um tempo de leitura menor, você também se beneficia de um aumento de desempenho!

Você pode usar pip para instalar a biblioteca necessária:
$ pip install redis

Agora, considere uma solicitação para obter o nome do usuário de seu ID:
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

Este código verifica se o nome está no Redis usando o id chave. Caso contrário, o nome será definido com um tempo de expiração, que você usa porque o cache é de curta duração.

Agora, e se o seu entrevistador perguntar o que há de errado com esse código? Sua resposta deve ser que não há tratamento de exceção! Bancos de dados podem ter muitos problemas, como conexões perdidas, então é sempre uma boa ideia tentar capturar essas exceções.



Perguntas sobre padrões de design e conceitos de ETL


Em aplicativos grandes, você geralmente usará mais de um tipo de banco de dados. Na verdade, é possível usar PostgreSQL, MongoDB e Redis em apenas um aplicativo! Um problema desafiador é lidar com mudanças de estado entre bancos de dados, o que expõe o desenvolvedor a problemas de consistência. Considere o seguinte cenário:
  1. Um valor no banco de dados nº 1 é atualizado.
  2. Esse mesmo valor no Banco de Dados #2 é mantido o mesmo (não atualizado).
  3. Uma consulta é executado no banco de dados nº 2.

Agora, você tem um resultado inconsistente e desatualizado! Os resultados retornados do segundo banco de dados não refletirão o valor atualizado no primeiro. Isso pode acontecer com quaisquer dois bancos de dados, mas é especialmente comum quando o banco de dados principal é um banco de dados NoSQL e as informações são transformadas em SQL para fins de consulta.

Os bancos de dados podem ter trabalhadores em segundo plano para resolver esses problemas. Esses trabalhadores extraem dados de um banco de dados, transforme de alguma forma e carregue no banco de dados de destino. Quando você está convertendo de um banco de dados NoSQL para um SQL, o processo Extrair, transformar, carregar (ETL) segue as seguintes etapas:
  1. Extrair: Há um gatilho do MongoDB sempre que um registro é criado, atualizado e assim por diante. Uma função de retorno de chamada é chamada de forma assíncrona em um encadeamento separado.
  2. Transformar: Partes do registro são extraídas, normalizadas e colocadas na estrutura de dados (ou linha) correta para serem inseridas no SQL.
  3. Carregar: O banco de dados SQL é atualizado em lotes ou como um único registro para gravações de alto volume.

Esse fluxo de trabalho é bastante comum em aplicativos financeiros, de jogos e de relatórios. Nesses casos, o esquema em constante mudança requer um banco de dados NoSQL, mas relatórios, análises e agregações exigem um banco de dados SQL.

Q8:Desafios de ETL


Existem vários conceitos desafiadores em ETL, incluindo o seguinte:
  • Grandes dados
  • Problemas com estado
  • Trabalhadores assíncronos
  • Correspondência de tipo

A lista continua! No entanto, como as etapas do processo de ETL são bem definidas e lógicas, os engenheiros de dados e back-end normalmente se preocupam mais com o desempenho e a disponibilidade do que com a implementação.

Se seu aplicativo estiver gravando milhares de registros por segundo no MongoDB, seu trabalhador de ETL precisa acompanhar a transformação, o carregamento e a entrega dos dados ao usuário no formulário solicitado. A velocidade e a latência podem se tornar um problema, portanto, esses trabalhadores geralmente são escritos em linguagens rápidas. Você pode usar código compilado para a etapa de transformação para acelerar as coisas, pois essa parte geralmente é vinculada à CPU.

Observação: Multiprocessamento e separação de trabalhadores são outras soluções que você pode considerar.

Se você está lidando com muitas funções com uso intensivo de CPU, convém conferir o Numba. Esta biblioteca compila funções para torná-las mais rápidas na execução. O melhor de tudo é que isso é facilmente implementado em Python, embora existam algumas limitações sobre quais funções podem ser usadas nessas funções compiladas.


Q9:Padrões de design em Big Data


Imagine que a Amazon precisa criar um sistema de recomendação para sugerir produtos adequados aos usuários. A equipe de ciência de dados precisa de dados e muito! Eles vão até você, o engenheiro de dados, e pedem que você crie um warehouse de banco de dados de teste separado. É aí que eles vão limpar e transformar os dados.

Você pode ficar chocado ao receber tal pedido. Quando você tiver terabytes de dados, precisará de várias máquinas para lidar com todas essas informações. Uma função de agregação de banco de dados pode ser uma operação muito complexa. Como você pode consultar, agregar e usar dados relativamente grandes de maneira eficiente?

O Apache introduziu inicialmente o MapReduce, que segue o map, shuffle, reduce fluxo de trabalho. A ideia é mapear dados diferentes em máquinas separadas, também chamadas de clusters. Em seguida, você pode realizar o trabalho nos dados, agrupados por uma chave e, por fim, agregar os dados na etapa final.

Esse fluxo de trabalho ainda é usado hoje, mas está desaparecendo recentemente em favor do Spark. O padrão de design, no entanto, forma a base da maioria dos fluxos de trabalho de big data e é um conceito altamente intrigante. Você pode ler mais sobre MapReduce em IBM Analytics.


Q10:Aspectos comuns do processo ETL e fluxos de trabalho de Big Data


Você pode pensar que esta é uma pergunta bastante estranha, mas é simplesmente uma verificação do seu conhecimento em ciência da computação, bem como seu conhecimento e experiência geral de design.

Ambos os fluxos de trabalho seguem o Produtor-Consumidor padronizar. Um trabalhador (o Produtor) produz dados de algum tipo e os envia para um pipeline. Esse pipeline pode assumir várias formas, incluindo mensagens de rede e gatilhos. Depois que o Produtor gera os dados, o Consumidor os consome e faz uso deles. Esses trabalhadores normalmente trabalham de maneira assíncrona e são executados em processos separados.

Você pode comparar o Produtor às etapas de extração e transformação do processo ETL. Da mesma forma, em big data, o mapeador pode ser visto como o Produtor, enquanto o redutor é efetivamente o Consumidor. Essa separação de interesses é extremamente importante e eficaz no desenvolvimento e projeto de arquitetura de aplicativos.



Conclusão


Parabéns! Você cobriu muito terreno e respondeu a várias perguntas da entrevista do engenheiro de dados. Agora você entende um pouco mais sobre os muitos chapéus diferentes que um engenheiro de dados pode usar, bem como quais são suas responsabilidades com relação a bancos de dados, design e fluxo de trabalho.

De posse desse conhecimento, agora você pode:
  • Use Python com SQL, NoSQL e bancos de dados de cache
  • Use Python em ETL e aplicativos de consulta
  • Planeje projetos com antecedência, mantendo o design e o fluxo de trabalho em mente

Embora as perguntas da entrevista possam ser variadas, você foi exposto a vários tópicos e aprendeu a pensar fora da caixa em muitas áreas diferentes da ciência da computação. Agora você está pronto para ter uma entrevista incrível!