Poszedłem na rozmowę o pracę inżyniera danych. Ankieter zadał mi pytanie. Dał mi pewną sytuację i poprosił mnie o zaprojektowanie przepływu danych dla tego systemu. Rozwiązałem to, ale nie podobało mi się moje rozwiązanie i nie udało mi się. Chciałbym wiedzieć, czy masz lepsze pomysły na rozwiązanie tego wyzwania.
Pytanie brzmiało:
Nasz system odbiera cztery strumienie danych. Dane zawierają identyfikator pojazdu, prędkość i koordynacje geolokalizacyjne. Każdy pojazd wysyła swoje dane raz na minutę. Nie ma połączenia między określonym strumieniem a określoną drogą, pojazdem lub czymkolwiek innym. Istnieje funkcja, która akceptuje koordynacje i zwraca nazwę odcinka drogi. Musimy znać średnią prędkość na odcinku drogi na 5 minut. Wreszcie chcemy zapisać wyniki do Kafki.
Więc moim rozwiązaniem było:
Najpierw zapisujemy wszystkie dane w klastrze Kafka, w jednym temacie, podzielonym przez 5-6 pierwszych cyfr szerokości geograficznej połączonych z 5-6 pierwszymi cyframi długości geograficznej. Następnie odczytaj dane według Structured Streaming, dodając dla każdego wiersza nazwę odcinka drogi przez koordynacje (jest do tego predefiniowany udf), a następnie dzieląc dane według nazwy odcinka drogi.
Ponieważ dzielę dane w Kafce według 5-6 pierwszych cyfr koordynacji, po przetłumaczeniu koordynacji na nazwę sekcji nie ma potrzeby przesyłania dużej ilości danych do poprawnej partycji i dlatego mogę skorzystać z operacji colesce () nie powoduje to pełnego losowania.
Następnie obliczamy średnią prędkość na executora.
Cały proces będzie się odbywał co 5 minut, a my zapisamy dane w trybie Append do ostatecznego zlewu Kafka.
Więc znowu, ankieterowi nie spodobało się moje rozwiązanie. Czy ktoś mógłby zasugerować, jak to ulepszyć lub zupełnie inny i lepszy pomysł?
Odpowiedzi:
Uznałem to pytanie za bardzo interesujące i pomyślałem o podjęciu próby.
Jak oceniłem dalej, twoja próba jest dobra, z wyjątkiem następujących:
Jeśli masz już metodę uzyskania identyfikatora / nazwy odcinka drogi na podstawie szerokości i długości geograficznej, dlaczego nie wywołać tej metody w pierwszej kolejności i użyć identyfikatora / nazwy odcinka drogi do podziału danych na pierwszym miejscu?
A potem wszystko jest dość łatwe, więc topologia będzie
(Bardziej szczegółowe wyjaśnienia można znaleźć w komentarzach w kodzie poniżej. Zapytaj, czy coś jest niejasne)
Dodałem kod na końcu tej odpowiedzi, pamiętaj, że zamiast średniej użyłem sumy, ponieważ łatwiej jest to zademonstrować. Można zrobić średnią, przechowując dodatkowe dane.
Szczegółowo opisałem odpowiedź w komentarzach. Poniżej znajduje się schemat topologii wygenerowany z kodu (dzięki https://zz85.github.io/kafka-streams-viz/ )
Topologia:
źródło
Problem jako taki wydaje się prosty, a oferowane rozwiązania mają już wiele sensu. Zastanawiam się, czy ankieter był zaniepokojony projektem i wydajnością rozwiązania, na którym się skupiłeś, czy też dokładnością wyniku. Ponieważ inni skupili się na kodzie, projekcie i wydajności, będę się liczył na dokładność.
Rozwiązanie do przesyłania strumieniowego
Gdy napływają dane, możemy z grubsza oszacować średnią prędkość drogi. Oszacowanie to będzie pomocne w wykrywaniu zatorów, ale będzie wyłączone przy określaniu ograniczenia prędkości.
Rozwiązanie wsadowe
To oszacowanie będzie wyłączone, ponieważ wielkość próby jest niewielka. Będziemy potrzebować przetwarzania wsadowego dla danych z całego miesiąca / kwartału / roku, aby dokładniej określić ograniczenie prędkości.
Czytaj dane z lata z jeziora danych (lub tematu Kafki)
Zastosuj UDF do współrzędnych, aby uzyskać nazwę ulicy i nazwę miasta.
Oblicz średnią prędkość za pomocą składni takiej jak -
Na podstawie tego dokładniejszego ograniczenia prędkości możemy przewidzieć powolny ruch w aplikacji do przesyłania strumieniowego.
źródło
Widzę kilka problemów z twoją strategią partycjonowania:
Kiedy mówisz, że zamierzasz podzielić dane na podstawie pierwszych 5-6 cyfr długości, nie będziesz w stanie określić liczby partycji kafka wcześniej. Będziesz miał wypaczone dane, ponieważ na niektórych odcinkach dróg zaobserwujesz dużą głośność niż na innych.
Twoja kombinacja klawiszy i tak nie gwarantuje tych samych danych odcinków drogi w tej samej partycji, dlatego nie możesz być pewien, że nie będzie tasowania.
Podane przez IMO informacje nie są wystarczające do zaprojektowania całego potoku danych. Ponieważ podczas projektowania potoku ważną rolę odgrywa sposób podziału danych na partycje. Powinieneś dowiedzieć się więcej o otrzymywanych danych, takich jak liczba pojazdów, wielkość strumieni danych wejściowych, czy liczba strumieni jest stała, czy może ona wzrosnąć w przyszłości? Czy otrzymywane strumienie danych wejściowych są strumieniami kafka? Ile danych otrzymujesz w 5 minut?
mapValues
ireduceByKey
zamiast groupBy. Zobacz to .źródło
mapValues
ireduceBy
rzeczywiście należy do RDD niskiego poziomu, ale nadal będzie działał lepiej w tej sytuacji, ponieważ najpierw oblicza agregację na partycję, a następnie tasuje.Główne problemy, które widzę przy tym rozwiązaniu, to:
Powiedziałbym, że rozwiązanie musi zrobić: odczytać ze strumienia Kafka -> UDF -> zgrupować odcinek drogi -> średnio -> zapisać do strumienia Kafka.
źródło
Mój projekt będzie zależał od
Jeśli chcę skalować dla dowolnej liczby obliczeń, projekt wyglądałby tak
Obawy dotyczące tego projektu -
Możliwe praktyczne ulepszenia tego projektu -
źródło