Fui a una entrevista de trabajo de ingeniero de datos. El entrevistador me hizo una pregunta. Me dio una situación y me pidió que diseñara el flujo de datos para ese sistema. Lo resolví pero a él no le gustó mi solución y fallé. Me gustaría saber si tienes mejores ideas sobre cómo resolver ese desafío.
La pregunta fue:
Nuestro sistema recibe cuatro flujos de datos. Los datos contienen una identificación del vehículo, velocidad y coordinaciones de geolocalización. Cada vihicle envía sus datos una vez por minuto. No hay conexión entre una secuencia específica a una carretera o vihicle específico o cualquier otra cosa. Hay una función que acepta coordinaciones y devuelve un nombre de sección de carretera. Necesitamos conocer la velocidad promedio por tramo de carretera por 5 minutos. Finalmente queremos escribir los resultados a Kafka.
Entonces mi solución fue:
Primero, escriba todos los datos en un grupo de Kafka, en un tema, dividido por los 5-6 primeros dígitos de la latitud concatenados con los 5-6 primeros dígitos de la longitud. Luego, lea los datos por Structured Streaming, agregue para cada fila el nombre de la sección de la carretera por las coordinaciones (hay un udf predefinido para eso), y luego combine los datos por el nombre de la sección de la carretera.
Como particiono los datos en Kafka por los primeros 5-6 dígitos de las coordinaciones, después de traducir las coordinaciones al nombre de la sección, no es necesario transferir muchos datos a la partición correcta y, por lo tanto, puedo aprovechar la operación colesce () eso no desencadena una barajadura completa.
Luego calculando la velocidad promedio por ejecutor.
Todo el proceso ocurrirá cada 5 minutos y escribiremos los datos en modo Añadir al sumidero Kafka final.
De nuevo, al entrevistador no le gustó mi solución. ¿Alguien podría sugerir cómo mejorarlo o una idea completamente diferente y mejor?
Respuestas:
Encontré esta pregunta muy interesante y pensé en intentarlo.
Como evalué más adelante, su intento en sí es bueno, excepto lo siguiente:
Si ya tiene un método para obtener la identificación / nombre de la sección de la carretera en función de la latitud y la longitud, ¿por qué no llamar primero a ese método y utilizar la identificación / nombre de la sección de la carretera para dividir los datos en primer lugar?
Y después de eso, todo es bastante fácil, por lo que la topología será
(Se puede encontrar una explicación más detallada en los comentarios en el código a continuación. Pregunte si algo no está claro)
He agregado el código al final de esta respuesta, tenga en cuenta que en lugar de la media, he usado la suma, ya que es más fácil de demostrar. Es posible hacer un promedio almacenando algunos datos adicionales.
He detallado la respuesta en los comentarios. A continuación se muestra un diagrama de topología generado a partir del código (gracias a https://zz85.github.io/kafka-streams-viz/ )
Topología:
fuente
El problema como tal parece simple y las soluciones ofrecidas ya tienen mucho sentido. Me pregunto si al entrevistador le preocupa el diseño y el rendimiento de la solución en la que se ha centrado o la precisión del resultado. Como otros se han centrado en el código, el diseño y el rendimiento, consideraré la precisión.
Solución de transmisión
A medida que los datos fluyen, podemos proporcionar una estimación aproximada de la velocidad promedio de una carretera. Esta estimación será útil para detectar la congestión, pero será errónea para determinar el límite de velocidad.
Solución por lotes
Esta estimación estará desactivada porque el tamaño de la muestra es pequeño. Necesitaremos un procesamiento por lotes de datos completos de mes / trimestre / año para determinar con mayor precisión el límite de velocidad.
Lea los datos de un año del lago de datos (o tema de Kafka)
Aplique UDF en las coordenadas para obtener el nombre de la calle y el nombre de la ciudad.
Calcule la velocidad promedio con una sintaxis como -
En base a este límite de velocidad más preciso, podemos predecir el tráfico lento en la aplicación de transmisión.
fuente
Veo algunos problemas con su estrategia de partición:
Cuando dice que va a particionar sus datos en función de los primeros 5-6 dígitos de lat de longitud, no podrá determinar de antemano el número de particiones kafka. Tendrá datos asimétricos, ya que en algunos tramos de carretera observará un volumen alto que otros.
Y su combinación de teclas no garantiza los mismos datos de la sección de carretera en la misma partición de todos modos y, por lo tanto, no puede estar seguro de que no se barajarán.
La información dada por la OMI no es suficiente para diseñar toda la tubería de datos. Porque al diseñar la tubería, la forma de particionar sus datos juega un papel importante. Debe preguntar más acerca de los datos que está recibiendo, como la cantidad de vehículos, el tamaño de los flujos de datos de entrada, ¿es fijo el número de flujos o puede aumentar en el futuro? ¿Los flujos de datos de entrada que está recibiendo son flujos kafka? ¿Cuántos datos recibes en 5 minutos?
mapValues
y enreduceByKey
lugar de groupBy. Consulte este .fuente
mapValues
y dereduceBy
hecho pertenece a RDD de bajo nivel, pero aún funcionará mejor en esta situación, ya que primero calculará el agregado por partición y luego barajará.Los principales problemas que veo con esta solución son:
Yo diría que la solución debe hacerlo: leer desde el flujo de Kafka -> UDF -> sección de carretera groupby -> promedio -> escribir en el flujo de Kafka.
fuente
Mi diseño dependería de
Si quiero escalar cualquier cantidad de conteos, el diseño se vería así
Preocupaciones cruzadas sobre este diseño:
Algunas mejoras prácticas posibles en este diseño:
fuente