Professional Documents
Culture Documents
link: http://scala-ide.org/
C:\Program Files\Java\jdk1.8.0_111\
package com.sundogsoftware.spark
import org.apache.spark._
import org.apache.spark.SparkContext._
import org.apache.log4j._
/** Count up how many of each star rating exists in the MovieLens 100K data set. */
object RatingsCounter {
// Create a SparkContext using every core of the local machine, named RatingsCounter
val sc = new SparkContext("local[*]", "RatingsCounter")
// Convert each line to a string, split it out by tabs, and extract the third field.
// (The file format is userID, movieID, rating, timestamp)
val ratings = lines.map(x => x.toString().split("\t")(2))
2
// Count up how many times each value (rating) occurs
val results = ratings.countByValue()
vidimo da u kodu imamo objekat u kome se nalazi cela naa aplikacija i ije je ime RatingsCounter
vidimo da imamo main funkciju unutar koje se sve nalazi
zatim imamo instrukciju u kojoj kaemo da samo pamtimo logove koji su ERROR
nakon toga postavljamo SparkContext, koji pokreemo na lokalnoj maini i koristimo svaki CPU i ime
tog JOB-a je RatingsCounter
u sledeoj liniji koda uitavamo podatke o rejtinzima filmova u neto to zovemo RDD
u sledeoj liniji koda parsiramo te podatke, tako to ih podelimo na osnovu tab karaktera i upamo
same rejtinge filmova
nakon toga koristimo f-ju CountByValue, koja nam automatski prebrojava za svaki rejting rezultat
nakon toga je sortiramo i tampamo rezultate
iz menija u Eclipse-u idemo na meni i odaberimo File -> New -> Scala project
nazovimo ovaj projekat LearningScala
u ovaj projekat Import-ujmo fajl LearningScala1.sc
ovo je Scala worksheet, kao notebook u Python-u
to je interaktivno, jer moete odmah videti rezultat nakon odreene linije koda
svaki put kada dodate neki kod i snimite, taj kod je evaluiran i interpretiran i to je prikazano
3
kada otvorimo fajl LearningScala1.sc, vidimo sledei kod
object LearningScala1 {
// VALUES are immutable constants. You can't change them once defined.
val hello: String = "Hola!" //> hello : String = Hola!
println(hello) //> Hola!
// Notice how Scala defines things backwards from other languages - you declare the
// name, then the type.
// One key objective of functional programming is to use immutable objects as often as possible.
// Try to use operations that transform immutable objects into a new immutable object.
// For example, we could have done the same thing like this:
val immutableHelloThere = hello + "There!" //> immutableHelloThere : String = Hola!There!
println(immutableHelloThere) //> Hola!There!
4
// String printing tricks
// Concatenating stuff with +:
println("Here is a mess: " + numberOne + truth + letterA + pi + bigNumber)
//> Here is a mess: 1truea3.141592651234567890
// printf style:
println(f"Pi is about $piSinglePrecision%.3f") //> Pi is about 3.142
println(f"Zero padding on the left: $numberOne%05d")
//> Zero padding on the left: 00001
// Substituting in variables:
println(s"I can use the s prefix to use variables like $numberOne $truth $letterA")
//> I can use the s prefix to use variables like 1 true a
// Substituting expressions (with curly brackets):
println(s"The s prefix isn't limited to variables; I can include any expression. Like ${1+2}")
//> The s prefix isn't limited to variables; I can include any expression.
Like
//| 3
// EXERCISE
// Write some code that takes the value of pi, doubles it, and then prints it within a string with
// three decimal places of precision to the right.
// Just write your code below here; any time you save the file it will automatically display the results!
5
immutable znai da ne mogu da se menjaju
u drugim jezicima se to zove konstantna promenljiva ili finalna promenljiva u zavisnosti od jezika o
kojem priamo
Scala ohrabruje upotrebu immutable promenljivih koliko god je to mogue
i one se u Scala oznaavaju sa val od value
sintaksa za deklarisanje promenljive je malo neobiajena, pogledajmo sada ovde:
o val hello: String = "Hola!"
val je dakle immutable konstanta, ta konstanta e biti tipa String i postaviemo je na vrednost Hola!
iako Scala vas ohrabruje da koristitite Immutable vrednosti, takoe moete koristiti Mutable vrednosti,
tada koristite var kljunu re umesto val
o var helloThere: String = hello
o helloThere = hello + " There!"
o println(helloThere)
var mogu da promenim
Scala moe implicitno da odredi tip
o val immutableHelloThere = hello + "There!"
6
...
izrazi (eng. expressions) u Scala-i funkcioniu malo drugaije ukoliko imamo blok koda, koji izgleda
ovako:
{val x = 10; x + 20}
zapoinjemo sa izrazom x = 10 i kasnije dodajemo 20 na to
u drugim jezicima to ne bi uradilo nita, zato to nita ne vraamo od tog izraza
ali u Scala-i to ima implicitnu vrednost, a to je da kompletan izraz ima vrednost poslednjeg izraza
dakle, vi moete da uzmete ceo ovaj izraz i da koristite i on ima vrednost 30
println({val x = 10; x + 20})
daje vrednost 30,
dakle u funkcionalnom programiranju izrazi (eng. expressions) mogu biti korieni kao same vrednosti
(eng. values)
object LearningScala3 {
// Functions
println(squareIt(2)) //> 4
println(cubeIt(2)) //> 8
}
vidimo f-ju transformInt, koja prihvata 2 parametra, gde kao prvi parametar prihvata Int, a kao drugi f-ju
koja transformie Int u drugi Int, kao rezultat vraa f-ju f kojoj je prosleen parametar x
7
07. Data Structures in Scala
torke (eng. tuples)
specijalni sluaj jeste kada torka (eng. tuple) moe da sadri 2 stvari, primer picardShip
8
object LearningScala4 {
// Data structures
// Lists
// Like a tuple, but it's an actual Collection object that has more functionality.
// It's a singly-linked list under the hood.
// head and tail give you the first item, and the remaining ones.
println(shipList.head) //> Enterprise
println(shipList.tail) //> List(Defiant, Voyager, Deep Space Nine)
// Let's apply a function literal to a list! map() can be used to apply any function to every item in a
collection.
val backwardShips = shipList.map( (ship: String) => {ship.reverse})
//> backwardShips : List[String] = List(esirpretnE, tnaifeD, regayoV,
eniN eca
//| pS peeD)
9
for (ship <- backwardShips) {println(ship)} //> esirpretnE
//| tnaifeD
//| regayoV
//| eniN ecapS peeD
// reduce() can be used to combine together all the items in a collection using some function.
val numberList = List(1, 2, 3, 4, 5) //> numberList : List[Int] = List(1, 2, 3, 4, 5)
val sum = numberList.reduce( (x: Int, y: Int) => x + y)
//> sum : Int = 15
println(sum) //> 15
// filter() can remove stuff you don't want. Here we'll introduce wildcard syntax while we're at it.
val iHateFives = numberList.filter( (x: Int) => x != 5)
//> iHateFives : List[Int] = List(1, 2, 3, 4)
val iHateThrees = numberList.filter(_ != 3) //> iHateThrees : List[Int] = List(1, 2, 4, 5)
// Note that Spark has its own map, reduce, and filter functions that can distribute these operations. But
they work the same way!
// Also, you understand MapReduce now :)
// Concatenating lists
val moreNumbers = List(6, 7, 8) //> moreNumbers : List[Int] = List(6, 7, 8)
val lotsOfNumbers = numberList ++ moreNumbers //> lotsOfNumbers : List[Int] = List(1, 2, 3, 4, 5,
6, 7, 8)
// More list fun
val reversed = numberList.reverse //> reversed : List[Int] = List(5, 4, 3, 2, 1)
val sorted = reversed.sorted //> sorted : List[Int] = List(1, 2, 3, 4, 5)
val lotsOfDuplicates = numberList ++ numberList //> lotsOfDuplicates : List[Int] = List(1, 2, 3, 4, 5, 1,
2, 3, 4, 5)
val distinctValues = lotsOfDuplicates.distinct //> distinctValues : List[Int] = List(1, 2, 3, 4, 5)
val maxValue = numberList.max //> maxValue : Int = 5
val total = numberList.sum //> total : Int = 15
val hasThree = iHateThrees.contains(3) //> hasThree : Boolean = false
// Maps
// Useful for key/value lookups on distinct keys
// Like dictionaries in other languages
val shipMap = Map("Kirk" -> "Enterprise", "Picard" -> "Enterprise-D", "Sisko" -> "Deep Space Nine",
"Janeway" -> "Voyager")
//> shipMap : scala.collection.immutable.Map[String,String] = Map(Kirk
-> Ente
//| rprise, Picard -> Enterprise-D, Sisko -> Deep Space Nine, Janeway ->
Voyage //| r)
println(shipMap("Janeway")) //> Voyager
11
Cluster Manager je odgovoran za distribuiranje posla koji je definisan driver script-om izmeu
viestrukih vorova
svaki vor koji je pokrenut na svakoj maini ima Executor proces, koji ima svoj ke i njegovu listu
zadataka (eng. task) i moe da podeli te podatke na vitestruke izvrioce (eng. executors), tako da
ukoliko imate veliki skup podatka (eng. data set), moe da uzme jedan mali deo toga i postavi svaki od
ovih delova na jedan od vorova na vaem klasteru za paralelno procesiranje i kada klaster menader
(eng. cluster manager) shvati kako da rekombinuje sve te podatke i prosledi ih sledeem koraku ukoliko
je potrebno
lepota toga je to se sve sa desne strane ove arhitekture deava automatski vei deo toga
sve to treba da brinete kao Spark programer je logika kako ete da procesirate ove podatke, Spark
Cluster Manager je odgovoran tada za kako da distribuirate na efikasan nain
Spark core radi sa osnova RDD-eva, transformie ih, skuplja njihove rezultate, zatim redukuje ih
(eng. reduce)
postoje neke biblioteke na vrhu Spark-a koje ine sloene operacije prostim
jedna od tih stvari je Spark Streaming to je tehnologija izgraena na vrhu koja moe da upravlja
malim delovima podataka kako oni dolaze u realnom vremenu, dakle vi moete da procesirate stream
podataka od mnogo Web servera ili npr. moda podatke od tone senzora od Internet of Things
aplikacija kako oni dolaze jednu sekundu u vremenu i koji nastavljaju da auriraju vae rezultate kako
idete u realnom vremenu i to je pokrenuto zauvek
Spark SQL omoguava jednostavan interfejs ka Sparku, moete otvoriti neto kao malu konekciju i
pokrenuti SQL upite nad masivnom koliinom podataka, to je veoma mona stvar ponekad
12
MLLib omoguava operacije mainskog uenja na masivnom skupu podatka
ukoliko je potrebno da uradite kao linearnu regresiju ili preporuivanje artikala prema ponaanju
korisnika, MLLib ima rutine koje to rade automatski i distribuiraju ih preko klastera
na kraju GraphX, ovde se radi o teoriji grafova, gde moete razmiljati o socijalnoj mrei gde imate vei
broj povezanih individua, koji su povezani meusobno na razliite naine, GraphX obezbeuje
framework za dobijanje informacija o atributima i svojstvima tog grafa, kao i generalni mehanizam
Pregel koji emo pogledati kasnije u kursu
Zato Scala ?
sam Spark je napisan u Scala-i
poslednji i najbolji Spark-ovi feature-i uvek prvo izlaze u Scala-i
dakle, Scala je najbolji izbor
daje najbolje performanse i pouzdanost i to je najjednostavniji kod u koji moete da gledate
hiveCtx = HiveContext(sc)
rows = hiveCtx.sql(SELECT name, age FROM
users)
dakle, to bi zapravo bilo seed parallelize
ovo je korisno u testne svrhe, ali nije namenjeno ozbiljnom radu
pored toga, moete uitati podatke iz nekog fajl sistema (eng. file system) i to moe da bude sa vaeg
lokalnog fajl sistema ili moe da bude iz distribuiranog fajl sistema, to je verovatnije u realnom
scenariju
13
u ovom primeru preuzimamo veliki tekstualni fajl sa hard diska, ali vi moete koristiti s3n ukoliko ste
skladitili na Amazon-u ili hdfs ukoliko ste koristili distribuiran hdfs sistem na Hadoop-u klasteru umesto
fajla
dakle, ukoliko imate veliki komad podataka koji je na nekom distribuiranom fajl sistemu, textFile e vas
pustititi da to uradite
takoe moete da integriete sa Hive-om, koji je drugi deo Hadoop ekosistema koji vas puta da
skladitite Big Data na nain koji je strukturisaniji
ukoliko imate Hive bazu podataka, vi moete da kreirate HiveContext sa datim SparkContext-om i
nakon toga koristite HiveContext SQL upite nad Hive podacima i to e vratiti RDD nad kojim moete da
sprovodite Spark operacije
postoji jo drugih izvora podataka (eng. data source) koji moete da integriete sa Spark-om
JDBC
Cassandra
HBase
Elasticsearch
JSON, CSV, sequence files, object files, razliiti kompresovani formati
14
o neka imamo hard-coder-iranu listu (1, 2, 3, 4)
o ono to ovde radimo, jeste da uzimamo Spark Context objekat sc i pozivamo parallelize nad
istim
o i ono ta ovaj kod radi jeste da proizvodi novi RDD i taj RDD sadri 4 reda 1, 2, 3, 4
o pogledajmo sada kako map radi: uzimamo rdd objekat i pozivamo map funkciju nad njim i kao
parametar funkcije map prosleujemo funkciju
o dakle, ova funkcija u zagradi map funkcije kae zapravo sledee prihvati kao ulaz vrednost x i
vrati x puta x
o dakle, prosleivanjem ove f-je koja kvadrira svaki red u RDD-u, squares RDD je transformisan i
sadri redove 1, 4, 9 i 16
o dakle, prosleujemo funkciju koja se primenjuje na svaki individualni red u vaem RDD-u i kreira
novi RDD kao rezultat
o dakle RDD se ne menja, ve se pravi novi RDD, dakle RDD je immutable
15
u prvoj liniji koda definiemo u kojem paketu ovaj kod ivi
o package com.sundogsoftware.spark
ovo je standardan nain imenovanja paketa tako da na kod ne bude u konfliktu sa bibliotekama drugih
ljudi
sledee emo import-ovati specifine biblioteka koje su nam potrebne za ove skripte
import-ovaemo sve to nam je potrebno iz spark biblioteke, specifino: SparkContext objekat, takoe
log4j paket koji radi sa log-ovima
o import org.apache.spark._
o import org.apache.spark.SparkContext._
o import org.apache.log4j._
kao to vidimo Logger koristimo samo da oznaimo Spark-u da loguje ukoliko doe do greaka
o Logger.getLogger("org").setLevel(Level.ERROR)
prva linija samog Spark koda je sledea
o val sc = new SparkContext("local[*]", "RatingsCounter")
SparkContext obejkat se koristi za kreiranje RDD-eva, tako da je to prva stvar koju je potrebno da
uradimo
dakle, kreiramo novi SparkContex objekat koji smo nazvali sc i to je immutable Spark Context objekat
ova zvezda u pravougaonim zagradama oznaava da emo kod pokreuti na lokalnoj maini, a ne u
klasteru, a sama zvezda oznaava da ,pemo da koristimo sva jezgra na naem CPU na maini, da
distribuiramo to lokalno ukoliko je to potrebno
ukoliko imate viejezgarni CPU ovaj kod moe da se distribuira izmeu jezgara
drugi parametar SparkContext objekat, je nita drugo do ime Spark Context-a
dalje, prvo to radimo jeste da uzimamo taj Spark Contex objekat i pozivamo tekstualni fajl nad njim sa
putanjom gde se nalazi taj tekstualni fajl sa podacima
u.data je fajl koji sadri aktuelne rejtinge podataka i importujemo jednu liniju po jednu u novi RDD koji
se zove lines
o val lines = sc.textFile("../ml-100k/u.data")
dakle, lines je dataset gde svaki red sadri jedan red ulaznih podataka
oni izgledaju na sledei nain:
196 242 3
881250949
186 302 3
891717742
22 377 1
878887116
16
o val ratings = lines.map(x => x.toString().split("\t")(2))
pozivamo funkciju map nad lines RDD-em, da bi smo ga transformisali u novi RDD koji se zove ratings,
koji parsira svaku liniju od ovih linija i samo izvlai polja koje su nam potrebne, a to je tree polje,
zapravo vrednost samog rejtinga
zapamtite da na krajnji rezultat je - koliko puta se svaki rejting pojavio, tako da ne moramo da brinemo
o korisnicima filova ili time stamp-ovima, sve o emu brinemo jesu ove vrednosti rejting-a
vidimo da pozivamo map funkciju da transformie taj ulazni RDD lines i prosleujemo ga ovoj funkciji u
zagradi da ga transformie, dakle ta linija koda znai sledee uzmi svaku pojedinanu liniju x i
konvertuj je u string, zatim je podeli u listu zasnovano na karakteru TAB (zato to je ovaj fajl TAB
delimited) i izvuci polje broj 2 iz te liste
dakle, zapoeli smo brojanje od nule tako da je polje 0 USER ID, polje 1 je MOVIE ID, a polje 2 je
RATING i to je ono o emu brinemo
dakle, ova funkcija se primenjuje na svaku liniju ulaznog fajla i mi vraamo na RDD koji sadri ocene
(rejtinge) i koji izgleda ovako
3
3
1
2
1
dakle, sada imamo ratings RDD gde svaki red je zapravo ocena (vrednost rejtinga) za svaki individualni
rejting
ok, ta dalje ?
krajnji rezultat koji elimo jeste da broj svake ocene (rejtinga) i koliko puta se dogodilo i Spark ima
korisnu funkciju koja se zove countByValue, koja e to odraditi za nas
zapamtite da je countByValue akcija i da kada je pozovete, to e uzrokovati Spark da ode i da srauna
direktni aciklini graf i da zapravo zapone da distribuira podatke ukoliko moe i ba kraju emo dobiti
ovo
3
3 (3, 2)
1 (1, 2)
2 (2, 1)
1
dakle, countByValue vraa, prihvata RDD, ali vraa Scala map objekat, gde zapravo mapa prikazuje
koliko puta su se odreene ocene desile
u ovom primeru zapoinjemo sa RDD-em koji zapoinje sa samim rejtinzima (ocenama), countByValue
nam vraa Scala map, sa kojo moemo da radimo ta poelimo i koja samo mapira vrednosti rejtinga
koliko puta su se dogodili (npr. ocena, tj. rejting 3 se pojavio 2 puta, rejting 1 se pojavio 2 puta, ...)
sada ono to je potrebno jeste da prikaemo rezultate na odgovarajui nain
prvo emo konvertovati nau mapu u sekvencu, jer je to neto to moemo da sortiramo
o val sortedResults = results.toSeq.sortBy(_._1)
i to emo sortirati po prvom polju
nakon toga moemo da uzmemo tu sortiranu sekvencu i pozovemo for each nad njom i prikaemo
svaki red i to je ono to tampa svaki pojedinani red rezultata u sortiranom redosledu i na finalni
rezultat su: ocena (rejting) 1 se pojavio 2 puta, rejting 2 se pojavio 1 put a rejting 3 se pojavio 2 puta
o sortedResults.foreach(println)
12
21
32
17
11. Spark internals
pogledajmo sada ta se deava ispod haube sa ovim primerom histograma ocena iz skup podataka o
ocenama filmova
dobra stvar da razumete onoga to se deava ispod haube ne samo iz akademskih razloga, ve da
kreirate i struktuirate Spark skripte koje mogu da se izvravaju najoptimalnije
pogledajmo sada to na primeru RatingsCounter.scala iz prethodne lekcije
ono to se zapravo dogaa kada pozovemo countByValue nad RDD-em, to je poslednji korak u naoj
skripti, to je zapravo akcija nad RDD-em, to uslovljava Spark da se vrati i zapravo skapira plan
izvrenja kako da doe do rezultata koji smo traili
dakle ono to se deava jeste da prati sve stvari koje smo ulanali zajedno iz razliitih RDD-eva i kako
su oni povezani izmeu sebe i na osnovu te informacije on kreira DAG direktni aciklini graf, u ovom
sluaju moe biti jedan veoma jednostavan, koji izgleda kao na slici
zapoinjemo sa textFile komandom putem koje importujemo gomilu sirovih podataka u RDD, nakon
toga map parsira informacije koje su nam vane to su zapravo same ocene filmova i na kraju
zovemo akciju countByValue da sumira sve razliite brojeve za svaki tip rejtinga (ocene), dakle to je
plan izvrenja
ono to je potrebno da shvatite jeste da sam map imate 1 na 1 vezu izmeu svakog ulaznog i izlaznog
reda RDD-eva, tako da sve moemo da drimo particionisano na isti nain u tom koraku, jer se to
veoma lako izvodi na distribuiran nain
ta se deava kada pozovemo countByValue ? moda je potrebno da promeamo i pomeamo malo
stvari ovde dakle ovde stvari postaju malo komplikovanije i ova operacije se zove shuffle (promeati)
operacija u Sparku i to moe izazvati u Sparku da mora da pogura gomilu podtaka na vaem klasteru i
to moe da bude veoma skupa operacija
u mnogo situacija kada optimizujete Spark job-ove, potrebno je da razmiljate kako da umanjite shuffle
operacije i kako da stvari ostanu paralelizovane kao kada mapiramo stvari
Dakle, jednom kada Spark ima plan izvrenja, i kada je potrebno da promea (shuffle) podatke, tada je
potrebno da razdvoji korake (stage-ove)
18
u tom trenutku Spark e rastaviti ovaj job na 2 stage-a: gde e se u prvom tekstualni fajl uitati i
mapiraemo da izvuemo podatke koji su potrebni i to sve moe biti pokrenuto paralelno
kada doemo do countByValue stvari su potrebne da se promeaju, i to je potreno da bude izvreno
kao korak 2
koraci (stage) se kreiraju zasnovano na delovima procesiranja koji mogu da budu uraeni paralelno bez
ponovnog premetanja (shuffle) podatka
kada pokrenete Spark job on e vam dati informaciju ukoliko je distribuiran, zatim prikazae koji korak
(eng. stage) je pokrenut i o emu priamo
jednom kada imamo korake (eng. stages) Spark e podeliti te korake u zadatke (eng. task)
to je zapravo gde stvari zapravo postaju distribuirane
zadatak (eng. task) zapravo razbija paralelizovane zadatke u diskretne delove koji mogu da budu
procesirani individualno i paralelno
dakle, zapoinjemo sa planom izvravanja, plan izvravanja se razbija na stage-ove (zasnovano na
osnovu stvari koje se mogu procesirati paralelno), i koraci (eng. stages) se razbijaju na zadatke koji su
distribuirani na individualnim vorovima na vaem klasteru
nakon toga Spark samo ide i radi
tada je red na Cluster Manager-a da preuzme podatke i sakupi ih tamo gde je potrebno da budu na
kraju i vaa driver script-a i maina se pokree i vi dobijate rezultate dakle, to je na visokom nivou
kako Spark radi interno
neke od drugih stvari koje moete uraditi su: SQL stil JOIN-ove ukoliko imate key-value parove
(razmislite o tome (key, value) RDD je poprilino kao NoSQL baza podatka, zar ne i moemo da
uradimo neke stvari kao sa bazom, obzirom da sada imamo malo strukturisanije podatke, moete da
uradite razliite JOIN-ove, razliite flavours,
ukoliko ete samo mapirati samo vrednosti u vaem key-value RDD-u i elite da kljueve (eng, keys)
ostavite samostalne, bie mnogo jednostavnje da koristite funkcje mapValues() i flatMapValues(),
umesto map() i flatMap()
dakle imamo redove informacija, koje su podeljenje zarezom (eng. comma) i sastoje se od: USER ID,
USER NAME, USER AGE, USER NUMBER OF FRIENDS
20
dakle ono to mi pokuavamo da uradimo jeste da pokuamo da naemo prosean broj prijatelja za
date godine
u ovom sluaju imamo Will a koji ima 33 godine i td...., takoe Jean-Luc ima 33 godine, tako da
nekako elimo da iskombinujemo ova 2 user-a i da vidimo koliki je njih prosean broj prijatelja
21