Wprowadzenie do Big Data i Apache Spark

1,210 views

Published on

Jakub Nowacki, 16.04.2016

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,210
On SlideShare
0
From Embeds
0
Number of Embeds
8
Actions
Shares
0
Downloads
24
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Wprowadzenie do Big Data i Apache Spark

  1. 1. Wprowadzenie do Big Data i Apache Spark Jakub Nowacki
  2. 2. Agenda ● Wstęp do Big Data ● Wstęp do Hadoop ● Apache Spark ○ Core ○ Streaming ○ SQL ● Podsumowanie Źródło: http://www.business2community.com
  3. 3. Wstęp do Big Data
  4. 4. Czym jest Big Data? Big data is high-volume, high-velocity and high-variety information assets that demand cost-effective, innovative forms of information processing for enhanced insight and decision making. Gardner http://www.gartner.com/it- glossary/big-data/
  5. 5. Big Data vs HPC ● Dużo danych (> 1 TB) ● Mniej obliczeń ● Na tanim sprzęcie ● Odporny na usterki ● Mniej danych (< 1 TB) ● Dużo obliczeń ● Specjalizowane klastry i superkomputery ● Mało odporny na usterki
  6. 6. High-Performance Computing Można przetwarzać dużo danych ale jest to dość drogie przez sprzęt. Systemy optymalizowane do obliczeń. Duże i drogie rozproszone dyski i systemy plików. Dobra obsługa błędów sprzętu ale nie zadania.
  7. 7. Czy Hadoop to Big Data? Hadoop to jedno z narzędzi Big Data do przetwarzania wsadowego danych, głównie w postaci nieustrukturyzowanej. Hadoop niekiedy używany jest jako synonim rodziny systemów Big Data. Hadoop File System (HDFS) to tylko jeden rozproszony system plików.
  8. 8. Czy NoSQL to Big Data? NoSQL to tylko nierelacyjny model bazy danych; są bazy danych NoSQL dla Big Data jak i do innych zastosowań. Wymagają przynajmniej połowicznej struktury danych. Zastosowanie modelu nierelacyjnego pozwala na lepsze skalowanie, przynajmniej w teorii.
  9. 9. Podział obowiązków w Big Data Administracja systemami Big Data - architekci systemowi, administratorzy Programowanie systemów Big Data - architekci oprogramowania, programiści Analiza Big Data - stratedzy, analitycy, data scientists
  10. 10. Big Data - rozwiązanie na wszystko? Systemy Big Data nie odpowiadają magicznie na pytania - ludzie muszą je dobrze zadać. Nie ma jednego dobrego podejścia do Big Data - często różne problemy wymagają różnych rozwiązań. Systemy Big Data wymagają inwestycji - trzymanie i przetwarzanie dużej ilości danych wymaga pieniędzy
  11. 11. Jak sformułować problem Big Data? 1. Czy mamy problem Big Data? 2. Jakie są źródła danych? 3. Jaka jest szybkość tworzenia danych? 4. Co chcemy osiągnąć? 5. Czy jest jakaś wartość dodana?
  12. 12. Scenariusz 1 Mamy 1 TB historycznych plików firmowych z różnych działów. Wszystkie pliki są w formatach które możemy czytać. Należy przenieść pliki do nowego archiwum i je skatalogować. Działy mogą prosić o dostęp do poszczególnych plików; 1 transakcja z archiwum dziennie.
  13. 13. Scenariusz 2 Firma posiada wiele niezależnych serwerów aplikacji bez centralnego zarządzania. Zadaniem jest archiwizacja logów z różnych serwerów w centralnym i bezpiecznym repozytorium. Ponadto, statystyki aplikacji z logów mają być dobowo analizowane i przestawiane na wspólnym panelu.
  14. 14. Scenariusz 3 Oddziały firmy mają jednostki sprzedażowe, które wysyłają dzienne raporty w różnych formatach plików, bez jednolitego systemu. Ponadto jest dotychczas zgromadzone 10 TB takich plików. Zadaniem jest gromadzenie tych plików, wstępna standardowa analiza i możliwość kierowania zapytań o historyczne dane sprzedażowe produktów.
  15. 15. Scenariusz 4 Firma ma wiele kont Tweetera dla każdego oddziału, na których na bieżąco odpowiada się na pytania klientów. Ze względu na błędy pracowników, w szczytach ruchu zapytań zdarzały się pominięcia niektórych ważnych pytań. Należy napisać system który by agregował zapytania ze wszystkich kont i je odpowiednio delegował. Ponadto, należy monitorować i prorytetyzować ważne problemy i klientów.
  16. 16. Apache Hadoop
  17. 17. Czym jest Apache Hadoop? Apache Hadoop to jedna z pierwszych otwarto- źródłowych implementacji MapReduce. Powstaje od 2005 r. Napisana w Javie. Hadoop jako nazwa stał się obecnie synonimem rodziny aplikacji do pracy z Big Data.
  18. 18. Rodzina Hadoop Źródło: http://hortonworks.com/hdp/
  19. 19. Rodzina Hadoop - wersje (!) Źródło: http://hortonworks.com/hdp/whats-new/
  20. 20. Dystrybucje Hadoop Dystrybucje Hadoop dają zintegrowane środowisko instalacji i uruchomienia. Dystrybucje dbają o wersje poszczególnych elementów i ich współdziałanie. Najbardziej znane firmy to: Cloudera, Hortonworks, MapR.
  21. 21. Komponenty Apache Hadoop Obecnie Apache Hadoop składa się z 4 części: ● biblioteki bazowe Hadoop ● HaDoop File System (HDFS) ● Yet Another Resource Negotiatior (YARN) ● Hadoop MapReduce HDFS YARN MR ...
  22. 22. Hadoop File System Rozproszony system plików stworzony specjalnie dla Hadoop i zadań z dużą ilością danych. System plików pisany był z myślą o przeciętnym sprzęcie. Relatywną nowością jest lokalność danych, tzn dane znajdują się na tych samych maszynach co obliczenia lub bardzo blisko. HDFS jest dedykowanym i domyślnym systemem plików dla Hadoop ale nie jest przez niego wymagany.
  23. 23. Architekture HDFS NameNode DataNode DataNode DataNode Trzyma wszystkie metadane związane z blokami danych Fizyczny zbiornik na bloki danych w lokalnej przestrzeni dyskowej Bloki mają duży rozmiar (typowo 64 lub 128 MB) i są replikowane
  24. 24. Uprawnienia i atrybuty plików HDFS HDFS oparty jest na modelu POSIX więc ma podobny system uprawnień i atrybutów plików jak systemy *nix. HDFS w zasadzie nie wspiera uruchamiania plików, więc argument x jest ignorowany. Użytkownik i grupa jest brana z procesu który uruchamia komendę. % hdfs dfs -ls . Found 2 items drwxr-xr-x - jakub supergroup 0 2014-11-05 10:30 /user/jakub/a_dir -rw-r--r-- 1 jakub supergroup 234 2014-11-05 10:34 /user/jakub/data.txt
  25. 25. Interfejsy HDFS HDFS posiada następujące interfejsy: Java (natywny), HTTP, C, FUSE. Interfejs HTTP występuje jako tylko do odczytu HFTP i nowy do odczytu i zapisu WebHDFS. Filesystem in Userspace (FUSE) jest standardową integracją systemu plików dla systemów *nix.
  26. 26. Apache Spark
  27. 27. Apache Spark Stworzony jako ogólny silnik do przetwarzania Big Data. Oparty na modelu Resilient Distributed Datasets (RDDs). Możliwość przetwarzania zarówno w pamięci jak i z dysku; wiele testów wskazuje, że jest szybszy od Hadoop. Natywnie wspiera API w Javie, Scali i Pythonie. Możliwe uruchomienie w YARN, Mesos lub jako osobny klaster.
  28. 28. Historia 2009: Pierwsza Spark wersja napisana jako cześć doktoratu Matei Zaharia w UC Berkeley AMPLab. 2010: Kod źródłowy został upubliczniony w na licencji BSD. 2013: Spark staje się projektem w inkunatorze Apache i zmienia licencje na Apache 2. 2014: Spark staje się oficjalnym projektem Apache; firma Databricks używa Spark do pobicia rekordu świata w sortowaniu. Źródło: https://en.wikipedia.org/wiki/Apache_Spark
  29. 29. Spark a Hadoop Hadoop ● szeroki projekt z wieloma elementami ● tylko map i reduce ● każdy krok zapisywany na dysku ● główne API tylko w Javie ● tylko przetwarzanie wsadowe Spark ● tylko przetwarzanie danych ● RDD i wiele transformat ● przetwarzanie głównie w pamięci ● Java, Python, Scala, R ● przetwarzanie wsadowe i strumieniowe
  30. 30. Historia MapReduce Historycznie pierwszą publikacją opisującą model MapReduce jest MapReduce: Simplified Data Processing on Large Clusters, Jeffrey Dean and Sanjay Ghemawat. Algorytm inspirowany funkcjami map i reduce dość powszechnych w programowaniu funkcyjnym, przez to autorstwo przypisywane pracownikom Google jest dyskusyjne. Apache Hadoop jest jedną z pierwszych i obecnie najpopularniejszą implementacją, ale nie jedyną.
  31. 31. Przepływ danych w MapRedure Mapper 1 Mapper 2 Mapper 3 Reducer 1 Reducer 2 Split 1 Split 2 Split 3 Data Part 1 Part 2 Input Map Shuffle Reduce Output
  32. 32. Działanie MapReduce Input Map Shuffle Reduce Output 123199901 567200805 645200811 989199933 452199904 224200822 (0,123199901) (10,567200806) (20,645200811) (50,989199933) (66,452199904) (72,224200822) (1999,1) (2008,6) (2008,11) (1999,33) (1999,4) (2008,22) (1999,[1,4, 33]) (2008,[6,11,22]) (1999,38) (2008,39) 1999,38 2008,39 cat in.txt map.sh sort reduce.sh out.txt
  33. 33. Resilient Distributed Datasets Model opublikowany w Zaharia et al., Resilient Distributed Datasets: A Fault-tolerant Abstraction for In-memory Cluster Computing, NSDI 2012. RDD ma poniższe założenia: ● RDD jest częścią rozproszonego zbioru tylko do odczytu załadowanego do lokalnej pamięci. ● RDD może być tylko stworzony przez operację ładowania zbioru z stabilnego nośnika lub operację na innym RDD; te operacje nazywane są transformacjami takimi jak np. map, filter czy join. Operacje w RDD tworzą skierowany graf acykliczny. Źródło: Zaharia et al., Resilient Distributed Datasets...
  34. 34. Przetwarzanie w pamięci lub z dysku Trzy podstawowe tryby przetrzymywania RDD: ● tylko w pamięci (MEMORY_ONLY) ● w pamięci i na dysku (MEMORY_AND_DISK) ● tylko na dysku (DISK_ONLY) Regresja logistyczna na Hadoop i Spark
  35. 35. Architektura
  36. 36. Warianty uruchomienia ● Wbudowany manager oparty o Akka, lokalnie jak i na klastrze ● Apache Mesos, zaawansowany manager klastra ● Apache Hadoop YARN, manager klastra Hadoop
  37. 37. Komponenty Spark
  38. 38. Spark Core
  39. 39. Java vs Scala vs Python (vs R) Spark napisany jest w Scali i jego komponenty zarządzające działają w JVM ale wszystkie języki uruchamiane są natywnie dla danego języka. Szybkość z reguły zależna od języka. API nie zmienia się znacząco pomiędzy językami poza drobnymi zmianami związanymi z modelem i typami w języku.
  40. 40. Spark - przykład (Java 7) JavaRDD<String> lines = spark.textFile("hdfs://..."); JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String, String>() { public Iterable<String> call(String s) { return Arrays.asList(s.split(" ")); } }); JavaPairRDD<String, Integer> pairs = words.mapToPair( new PairFunction<String, String, Integer>() { public Tuple2<String, Integer> call(String s) { return new Tuple2<String, Integer>(s, 1); } }); JavaPairRDD<String, Integer> counts = pairs.reduceByKey(new Function2<Integer, Integer>() { public Integer call(Integer a, Integer b) { return a + b; } }); counts.saveAsTextFile("hdfs://...");
  41. 41. Spark - przykład (Java 8) JavaRDD<String> lines = spark.textFile("hdfs://..."); JavaRDD<String> words = lines.flatMap(line -> Arrays.asList(line.split(" "))); JavaPairRDD<String, Integer> counts = words.mapToPair(w -> new Tuple2<String, Integer>(w, 1)) .reduceByKey((x, y) -> x + y); counts.saveAsTextFile("hdfs://...");
  42. 42. Spark - przykład (Scala) val lines = spark.textFile("hdfs://...") val counts = lines.flatMap(line => line.split(" ")) .map(word => (word, 1)) .reduceByKey(_ + _) counts.saveAsTextFile("hdfs://...")
  43. 43. Tworzenie zadania Zadanie w Spark konfigurowane jest z użyciem obiektu konfiguracji. Obiekt ten przekazywany jest do kontekstu Spark (Spark Context) tworząc zadanie. Wszystkie parametry zadania przechowywane są w obiekcie kontekstu Spark. SparkConf conf = new SparkConf().setAppName(appName).setMaster(master); JavaSparkContext sc = new JavaSparkContext(conf);
  44. 44. Uruchomienie zadania Zadania na klaster wysyłane są w postaci JAR (Scala i Java) lub plików Pythona, ZIP i EGG (Python). Przykład komendy uruchamiającej po lewej. Opcje master: ● local/local[k]/local[*] - uruchomienie lokalne z jednym, k i maksymalną liczbą wątków ● spark://HOST:PORT - klaster Spark (Akka) ● mesos://HOST:PORT - klaster Mesos ● yarn-client/yarn-cluster - uruchomienie na YARN w trybie klienta i klastra; klaster jest skonfigurowany jak wskazane w plikach w HADOOP_CONF_DIR lub YARN_CONF_DIR # Java/Scala ./bin/spark-submit --class <main-class> --master <master-url> --deploy-mode <deploy-mode> --conf <key>=<value> ... # other options <application-jar> [application-arguments] # Python ./bin/spark-submit --master <master-url> ... # other options <application-py> [application-arguments]
  45. 45. Rozproszone dane Spark może otrzymywać dane z wielu źródeł: ● rozproszonych kolekcji ● plików tekstowych i innych formatach w tym wszystkich formatach Hadoop ● baz danych ● itp. // Collections List<Integer> data = Arrays.asList(1, 2, 3, 4, 5); JavaRDD<Integer> distData = sc.parallelize(data); // Files JavaRDD<String> distFile = sc.textFile("data.txt"); JavaRDD<String> distFile = textFile("/my/dir"); JavaRDD<String> distFile = textFile("/my/dir/*.gz") JavaPairRDD<LongWritable,LongWritable> seqFile = sc.sequenceFile("data.seq", LongWritable.class, LongWritable.class);
  46. 46. Operacje na RDD Na RDD można wykonać wiele operacji, które podzielone są na: ● transformacje - tworzące nowy zbiór danych (RDD) ● akcje - zwracające wynik do głównego kontekstu Prezentowane operacje są tylko najważniejszymi; pełną listę operacji zawiera dokumentacja Spark: http://spark. apache.org/docs/latest/
  47. 47. Transformacje na RDD map(func) - na każdym elemencie wykonywana jest funkcja func filter(func) - każdy element jest wybierany przez funkcję func flatMap(func) - na każdym elemencie wykonywana jest funkcja func zwracająca kolekcje union(dataset) - suma zbiorów obecnego RDD z dataset intersection(dataset) - część wspólna obecnego RDD z dataset
  48. 48. Transformacje na RDD distinct() - elementy unikalne zbioru RDD groupByKey() - grupuje wartości po kluczach reduceByKey(func) - redukuje wartości po kluczach parami na podstawie zadanej funkcji sortByKey([ascending]) - sortuje RDD join(dataset) - wykonuje operacje łączenia z dataset cogroup(dataset) - łączenie z grupowaniem
  49. 49. Transformacje na RDD cartesian(dataset) - wykonuje iloczyn kartezjański z dataset, zwraca wszystkie pary pipe(command) - wykonaj komendę shell na każdym elemencie RDD coalesce(numPartitions) - zmniejsz ilość partycji RDD repartition(numPartitions) - zmień ilość partycji RDD dzieląc dane losowo
  50. 50. Akcje na RDD reduce(func) - agregacja wartości RDD za pomocą funkcji func collect() - zamienia RDD na kolekcje count() - zwraca ilość elementów RDD first() - zwraca pierwszy element RDD take(n) - zwraca n elementów RDD takeSample(withReplacement, n) - zwraca próbkę danych o rozmiarze n, z lub bez powtórzeń takeOrdered(n) - zwraca posortowane n elementów
  51. 51. Akcje na RDD saveAsTextFile(path) - zapisuje plik w postaci tekstowej w ścieżce path saveAsSequenceFile(path) - zapisuje plik w postaci sekwensyjnej w ścieżce path countByKey() - liczy ilość elementów z tym samym kluczem foreach(func) - funkcja func zawierająca zmienną agregującą wykonywana na każdym elemencie RDD
  52. 52. Współdzielone zmienne Spark ma dwa typy zmiennych współdzielonych. Zmienne nadawane (broadcast) są to zmienne tylko do odczytu, które mogą zawierać wartości często używane: Broadcast<int[]> broadcastVar = sc.broadcast(new int[] {1, 2, 3}); broadcastVar.value(); // returns [1, 2, 3]
  53. 53. Współdzielone zmienne Akumulatory (accumulators) to specjalne zmienne służące do agregowania wartości: Accumulator<Integer> accum = sc.accumulator(0); sc.parallelize(Arrays.asList(1, 2, 3, 4)). foreach(x -> accum.add(x)); // ... // 10/09/29 18:41:08 INFO SparkContext: Tasks finished in 0.317106 s accum.value(); // returns 10
  54. 54. Utrzymywanie danych w pamięci Spark ma możliwość utrzymywania danych w pamięci operacyjnej lub dyskowej. Funkcja ta służy do zachowanie i ponownego użycia uprzednio wykonywanej operacji. persist(level) - utrzymuje dane na wybranym poziomie (storage level), tj w pamięci, na dysku itp cache() - utrzymuje dane wyłącznie w pamięci operacyjnej
  55. 55. Spark Streaming
  56. 56. Spark Streaming Rozszerzenie Spark generujące mikro-zadania wsadowe (micro- batches), pozwalające na analizę strumieni danych. Używa tego samego modelu zrównoleglenia danych i samego Spark do analizy. Zapewnia analizę dokładnie raz (exactly once) elementu strumienia danych.
  57. 57. Spark Streaming - przykład (Java 8) SparkConf sparkConf = new SparkConf().setAppName("JavaNetworkWordCount"); JavaStreamingContext ssc = new JavaStreamingContext(sparkConf, Durations.seconds (1)); JavaReceiverInputDStream<String> lines = ssc.socketTextStream( args[0], Integer.parseInt(args[1]), StorageLevels.MEMORY_AND_DISK_SER); JavaDStream<String> words = lines.flatMap(line -> Arrays.asList(line.split(" "))); JavaPairDStream<String, Integer> counts = words.mapToPair(w -> new Tuple2<String, Integer>(w, 1)) .reduceByKey((x, y) -> x + y); counts.print(); ssc.start(); ssc.awaitTermination();
  58. 58. Discretized Streams (DStreams)
  59. 59. Wejścia DStreams Proste źródła dostępne z kontekstu: ● strumienie plikowe ● gniazdka (sockets) ● aktorzy Akka Źródła złożone dostępne jako biblioteki, m.in.: ● Kafka ● Flume ● Kinesis ● Twitter
  60. 60. Transformacje Większość transformacji RDD jest wspierana w DStreams, ale nie jest to zbór pełny. Dostępne nowe funkcje np: ● stany ● checkpointy ● funkcje na oknach danych
  61. 61. Checkpointing Jako, że strumień jest w teorii nieskończony należy zapisywać stan na dysk. Dwa typy danych są zapisywane: metadane strumienia (konfiguracja itp.) oraz dane. Dane zapisywane są z funkcji zapisu stanu lub operacji na oknach. SparkConf sparkConf = new SparkConf().setAppName("JavaNetworkWordCount"); JavaStreamingContext ssc = new JavaStreamingContext(sparkConf, Durations.seconds(1)); ssc.checkpoint("checkpoint_dir");
  62. 62. Funkcje na oknach danych
  63. 63. Zapis DStream Istnieją wbudowane opcje zapisu: ● print() - wyświetla pierwsze 10 elementów na standardowym wyjściu ● saveAsTextFiles(prefix, [suffix]) - zapis do pliku tekstowego ● saveAsHadoopFiles(prefix, [suffix]) - zapis do formatu wspieranego przez Hadoop ● foreachRDD(func) - wykonuje operację zapisu zdefiniowaną w funkcji func
  64. 64. Spark SQL
  65. 65. Spark SQL Zadania Spark są generowane na podstawie zapytań SQL. Dane mogą być ładowane z wielu źródeł. Podobny do Hive i może korzystać z jego komponentów oraz przetwarzać istniejące zapytania. Można także wywoływać zapytania przez standardowe interfejsy JDBC/ODBC. Dobra integracja z narzędziami BI.
  66. 66. Podstawowy przykład (Java) // SparkContexst sc = ... SQLContext sqlCtx = new SQLContext(sc); DataFrame people = sqlCtx.read().parquet("people.parquet"); people.registerTempTable("peope"); DataFrame results = sqlCtx.sql("SELECT * FROM people"); List<String> names = results.javaRDD().map(row -> row.getString(0)).collect(); DataFrame departments = sqlCtx.read().json("departments.json"); departments.registerTempTable("departments"); DataFrame joinResult = sqlCtx.sql("SELECT * FROM people JOIN departments");
  67. 67. Data frame Wprowadzone w Spark 1.3 ale ze zmianami w API Spark 1.4. Data frame jest jak inne RDD ale ma dodatkową strukturę danych jak tabela. Podobna do data frame z R lub Python Pandas. Można wykonywać na niej operacje jak na innym RDD oraz inne operacje typowe dla tabeli, np select. Można definiować schemat danych. // SparkContexst sc = ... SQLContext sqlCtx = new SQLContext(sc); DataFrame df = sqlContext.read().json ("people.json"); df.show(); // age name // null Michael // 30 Andy // 19 Justin df.printSchema(); // root // |-- age: long (nullable = true) // |-- name: string (nullable = true) df.select("name").show(); // name // Michael // Andy // Justin
  68. 68. Integracja z Hive Spark SQL nie ma wbudowanych komponentów Hive ale się z nimi integruje. Można korzystać z Hcatalog w Spark lub uruchamiać zadania Hive na Sparku jako silniku uruchomieniowym. HiveQL jest wspierany ale może nie zawierać najnowszych funkcji. // SparkContexst sc = ... HiveContext sqlCtx = new HiveContext(sc); DataFrame results = sqlCtx.sql( "SELECT * FROM people"); List<String> names = results.javaRDD(). map( row -> row.getString(0));
  69. 69. Pobieranie danych z JDBC Istnieje możliwość pobierania danych z arbitralnego źródła kompatybilnego z JDBC. Zaleca się używania tego rozwiązania zamiast starszego JdbcRDD, bo nowe operuje na data frames. Map<String, String> options = new HashMap<String, String>(); options.put("url", "jdbc:postgresql:dbserver"); options.put("dbtable", "schema.tablename"); DataFrame jdbcDF = sqlContext.read() .format("jdbc") .options(options) .load();
  70. 70. Spark SQL Thrift server Spark SQL posiada serwer Thrift ODBC/JDBC zgodny z HiveServer2. Pozwala on na połączenie się za pomocą wszystkich sterowników JDBC/ODBC do HiveServer2 z Spark SQL. W efekcie Spark SQL może zastępować MapReduce lub Tez w Hive. ./sbin/start-thriftserver.sh --hiveconf hive.server2.thrift.port=<port> --hiveconf hive.server2.thrift.bind.host=<lost> --master <master-uri> ... ./bin/beeline beeline> !connect jdbc:hive2: //localhost:10000
  71. 71. Co dalej? https://www.gitbook. com/book/databricks/databricks-spark-reference- applications/details http://spark.apache.org/docs/latest/
  72. 72. Chcesz wiedzieć więcej? Prowadzimy szkolenia w grupach indywidualne w grupach 4-8 osobowych: ● mamy więcej czasu: szkolenia 2-5 dniowe ● pozwalają na bardziej indywidualną pracę z uczestnikami ● możliwość dopasowania programu do grupy ● rozwiązujemy i odpowiadamy na indywidualne pytania
  73. 73. Szkolenia dedykowane dla Ciebie W ofercie szkoleń Big Data i nie tylko, mamy szkolenia dla architektów, analityków i programistów. Oferujemy całościowe szkolenia z ekosystemu Hadoop, jak i szkolenia z poszczególnych elementów. Szkolenia z baz NoSQL: HBase, Cassandra czy MongoDB. Szkolenia z analizy danych (data science). … i wiele innych. http://www.sages.com.pl/

×