Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Comprendre et maîtriser la performance de vos applications Spark

rluta
April 17, 2019

Comprendre et maîtriser la performance de vos applications Spark

Support de présentation pour l'université Spark que j'ai animé à Devoxx France 2019

rluta

April 17, 2019
Tweet

More Decks by rluta

Other Decks in Technology

Transcript

  1. #DevoxxFR
    Comprendre et

    Maitriser la performance

    de vos applications Spark
    Raphaël Luta - @raphaelluta
    1

    View full-size slide

  2. #DevoxxFR
    « Vas-y fonce ! 

    On sait jamais sur un malentendu,
    ça peut marcher »
    2

    View full-size slide

  3. #DevoxxFR 4
    Tuning

    View full-size slide

  4. #DevoxxFR 4
    Measure
    Tuning

    View full-size slide

  5. #DevoxxFR 4
    Measure
    Model
    Tuning

    View full-size slide

  6. #DevoxxFR 4
    Measure
    Model
    Hypothesis
    Tuning

    View full-size slide

  7. #DevoxxFR 4
    Measure
    Model
    Hypothesis
    Adjustment
    Tuning

    View full-size slide

  8. #DevoxxFR
    Spark
    5

    View full-size slide

  9. #DevoxxFR
    Spark
    5
    Python
    Moteur
    Calcul
    Distribué
    Scala
    Java R

    View full-size slide

  10. #DevoxxFR
    Spark
    5
    SQL
    Dataset
    MLLib

    SparkML
    GraphX Streaming
    Python
    Moteur
    Calcul
    Distribué
    Scala
    Java R

    View full-size slide

  11. #DevoxxFR
    Spark
    5
    SQL
    Dataset
    MLLib

    SparkML
    GraphX Streaming
    Standalone YARN Mesos Kubernetes
    Python
    Moteur
    Calcul
    Distribué
    Scala
    Java R

    View full-size slide

  12. #DevoxxFR
    Dataframe
    Dataset
    RDD
    Mon profil Spark
    6
    Raphaël Luta

    DataDevSecOps

    Freelance
    0 %
    25 %
    50 %
    75 %
    100 %
    Batch Streaming ML GraphX
    0 %
    25 %
    50 %
    75 %
    100 %
    2012 2013 2014 2015 2016 2017 2018 2019
    Spark Pig/Hive Autre
    Scala
    SQL
    Python
    Java
    R
    YARN
    Standalone
    Mesos
    Kubernetes

    View full-size slide

  13. #DevoxxFR
    Programme
    7

    View full-size slide

  14. #DevoxxFR
    Programme
    7
    API

    View full-size slide

  15. #DevoxxFR
    Programme
    7
    API
    Modèle
    Exécution

    View full-size slide

  16. #DevoxxFR
    Programme
    7
    API
    Modèle
    Exécution
    Monitoring

    View full-size slide

  17. #DevoxxFR
    Programme
    7
    API
    Modèle
    Exécution
    Catalyst
    Monitoring

    View full-size slide

  18. #DevoxxFR
    Programme
    7
    API
    Modèle
    Exécution
    Catalyst
    Monitoring
    Formats
    Compression
    Parallélisme
    Mémoire
    S3
    UDF ???

    View full-size slide

  19. #DevoxxFR
    Programme
    9
    API
    Modèle
    Exécution
    Catalyst
    Monitoring

    View full-size slide

  20. #DevoxxFR
    RDD
    Dataframe
    Dataset
    10

    View full-size slide

  21. #DevoxxFR
    RDD API
    11
    RDD[T] <=> Seq[T]

    View full-size slide

  22. #DevoxxFR
    Transformations vs Actions
    12
    RDD[T] => RDD[U]
    map

    flatMap

    filter

    union
    distinct

    groupByKey

    reduceByKey

    join

    RDD[T] => _

    collect

    take

    count

    foreach

    reduce

    fold

    saveAsTextFile
    …

    Narrow
    Wide

    View full-size slide

  23. #DevoxxFR
    Exemple
    sc.textFile("wikipedia")
    .flatMap(line => line.split(" "))
    .map(word => (word, 1))
    .reduceByKey(_ + _)
    .saveAsTextFile("wordcount")
    13

    View full-size slide

  24. #DevoxxFR
    DATAFRAME API
    14
    Dataframe <=> Seq[Map[String,Any]]

    View full-size slide

  25. #DevoxxFR
    Exemple
    import spark.implicits._
    import org.apache.spark.sql.functions._
    spark.read.parquet("wikipedia")
    .select(explode(split(‘content)) as "word")
    .groupBy("word")

    .count
    .save.parquet("wordcount")
    15

    View full-size slide

  26. #DevoxxFR
    DATASET API
    16
    Dataset[T] <=> Seq[T]

    View full-size slide

  27. #DevoxxFR
    DATASET API
    16
    Dataset[T] <=> Seq[T]
    type Dataframe = Dataset[Row]

    View full-size slide

  28. #DevoxxFR
    Exemple
    case class WikiArticle(content: String)
    spark.read.parquet("wikipedia")
    .as[WikiArticle]
    .flatMap(_.content.split(" "))

    .groupByKey(_)

    .count
    .save.parquet("wordcount")
    17

    View full-size slide

  29. #DevoxxFR 18

    View full-size slide

  30. #DevoxxFR
    Programme
    19
    API
    Modèle
    Exécution
    Catalyst
    Monitoring

    View full-size slide

  31. #DevoxxFR 20 20

    View full-size slide

  32. #DevoxxFR 20 20
    Driver

    View full-size slide

  33. #DevoxxFR 20 20
    Executor
    Driver

    View full-size slide

  34. #DevoxxFR 20 20
    Executor
    Driver
    Task

    View full-size slide

  35. #DevoxxFR 20 20
    Executor
    Driver
    Task
    Stage

    View full-size slide

  36. #DevoxxFR 20 20
    Executor
    Driver
    Task
    Stage
    Input

    View full-size slide

  37. #DevoxxFR 20 20
    Executor
    Driver
    Task
    Stage
    Stage
    Input

    View full-size slide

  38. #DevoxxFR 20 20
    Executor
    Driver
    Task
    Stage
    Stage
    Shuffle
    Input

    View full-size slide

  39. #DevoxxFR 20 20
    Executor
    Driver
    Task
    Stage
    Stage
    Shuffle
    Input
    Output
    Job

    View full-size slide

  40. #DevoxxFR
    C'est quoi un job
    • 1 Action => 1 Job
    • Par défaut, 1 seul stage par Job
    • 1 transformation "wide" => 1 nouveau stage
    21

    View full-size slide

  41. #DevoxxFR
    Exemple découpage
    sc.textFile("wikipedia")
    .flatMap(line => line.split(" "))
    .map(word => (word, 1))
    .reduceByKey(_ + _)
    .saveAsTextFile("wordcount")
    22

    View full-size slide

  42. #DevoxxFR
    JOB 1
    Exemple découpage
    sc.textFile("wikipedia")
    .flatMap(line => line.split(" "))
    .map(word => (word, 1))
    .reduceByKey(_ + _)
    .saveAsTextFile("wordcount")
    22

    View full-size slide

  43. #DevoxxFR
    JOB 1
    Exemple découpage
    sc.textFile("wikipedia")
    .flatMap(line => line.split(" "))
    .map(word => (word, 1))
    .reduceByKey(_ + _)
    .saveAsTextFile("wordcount")
    22
    STAGE 1 STAGE 2

    View full-size slide

  44. #DevoxxFR
    Rôles du driver
    • Gérer le cycle de vie des exécuteurs
    • Gérer la répartition des données
    • Effectuer le découpage job, stage, task
    • Générer et affecter des tâches aux exécuteurs
    23

    View full-size slide

  45. #DevoxxFR
    Rôles de l’exécuteur
    • Executer les tâches demandées par le driver
    • Maintenir un cache de bloc de données
    • Gérer l’envoi et la reception des blocs de shuffle
    24

    View full-size slide

  46. #DevoxxFR
    Modèles d’execution
    • Local
    • YARN
    • Standalone
    • Mesos
    • Kubernetes
    25

    View full-size slide

  47. #DevoxxFR
    Spark-submit en action (YARN)
    26
    spark
    submit
    YARN RM
    Node
    Node
    spark-submit
    --master yarn
    --deploy-mode client
    --num-executors 2
    --executor-cores 4
    --executor-memory 8G
    --class MyClass
    mypackage.jar

    View full-size slide

  48. #DevoxxFR
    Spark-submit en action (YARN)
    26
    spark
    submit
    YARN RM
    Node
    Node
    spark-submit
    --master yarn
    --deploy-mode client
    --num-executors 2
    --executor-cores 4
    --executor-memory 8G
    --class MyClass
    mypackage.jar
    mypackage.jar

    View full-size slide

  49. #DevoxxFR
    Spark-submit en action (YARN)
    26
    spark
    submit
    YARN RM
    Node
    Node
    spark-submit
    --master yarn
    --deploy-mode client
    --num-executors 2
    --executor-cores 4
    --executor-memory 8G
    --class MyClass
    mypackage.jar
    mypackage.jar
    spark-defaults.conf
    core-site.xml
    hdfs-site.xml
    yarn-site.xml
    ...

    View full-size slide

  50. #DevoxxFR
    Spark-submit en action (YARN)
    26
    spark
    submit
    YARN RM
    Node
    Node
    spark-submit
    --master yarn
    --deploy-mode client
    --num-executors 2
    --executor-cores 4
    --executor-memory 8G
    --class MyClass
    mypackage.jar
    mypackage.jar
    spark-defaults.conf
    core-site.xml
    hdfs-site.xml
    yarn-site.xml
    ...

    View full-size slide

  51. #DevoxxFR
    Spark-submit en action (YARN)
    26
    spark
    submit
    YARN RM
    Node
    Node
    spark-submit
    --master yarn
    --deploy-mode client
    --num-executors 2
    --executor-cores 4
    --executor-memory 8G
    --class MyClass
    mypackage.jar
    mypackage.jar
    spark-defaults.conf
    core-site.xml
    hdfs-site.xml
    yarn-site.xml
    ...
    driver
    application
    master

    View full-size slide

  52. #DevoxxFR
    Spark-submit en action (YARN)
    26
    spark
    submit
    YARN RM
    Node
    Node
    spark-submit
    --master yarn
    --deploy-mode client
    --num-executors 2
    --executor-cores 4
    --executor-memory 8G
    --class MyClass
    mypackage.jar
    mypackage.jar
    spark-defaults.conf
    core-site.xml
    hdfs-site.xml
    yarn-site.xml
    ...
    driver
    application
    master

    View full-size slide

  53. #DevoxxFR
    Spark-submit en action (YARN)
    26
    spark
    submit
    YARN RM
    Node
    Node
    spark-submit
    --master yarn
    --deploy-mode client
    --num-executors 2
    --executor-cores 4
    --executor-memory 8G
    --class MyClass
    mypackage.jar
    mypackage.jar
    spark-defaults.conf
    core-site.xml
    hdfs-site.xml
    yarn-site.xml
    ...
    Executor 1
    Executor 2
    driver
    application
    master

    View full-size slide

  54. #DevoxxFR
    Spark-submit en action (YARN)
    26
    spark
    submit
    YARN RM
    Node
    Node
    spark-submit
    --master yarn
    --deploy-mode client
    --num-executors 2
    --executor-cores 4
    --executor-memory 8G
    --class MyClass
    mypackage.jar
    mypackage.jar
    spark-defaults.conf
    core-site.xml
    hdfs-site.xml
    yarn-site.xml
    ...
    Executor 1
    Executor 2
    driver
    application
    master

    View full-size slide

  55. #DevoxxFR
    spark-submit
    • Créer la configuration de l’application Spark adaptée
    au cluster choisi
    • Déploie les ressources vers le cluster
    • Lance le driver sur la machine locale (client mode)
    ou demande l’execution au cluster manager (cluster
    mode)
    27

    View full-size slide

  56. #DevoxxFR
    Tips: spark-submit sans jar local
    28

    View full-size slide

  57. #DevoxxFR
    Tips: spark-submit sans jar local
    spark-submit

    --master yarn

    --deploy-mode cluster

    --packages my.fab:spark-job:1.2.2

    --class my.fab.JobClass

    dummy
    28

    View full-size slide

  58. #DevoxxFR
    Tips: spark-submit sans jar local
    spark-submit

    --master yarn

    --deploy-mode cluster

    --packages my.fab:spark-job:1.2.2

    --class my.fab.JobClass

    dummy
    ...

    151 WARN [main] DependencyUtils: Local

    jar /Users/raphael/dummy does not exist,

    skipping.

    ...
    28

    View full-size slide

  59. #DevoxxFR 29

    View full-size slide

  60. #DevoxxFR
    Programme
    30
    API
    Modèle
    Exécution
    Catalyst
    Tungsten
    Monitoring

    View full-size slide

  61. #DevoxxFR
    Logging
    Event History
    Metrics
    31

    View full-size slide

  62. #DevoxxFR
    Logging
    Event History
    Metrics
    32

    View full-size slide

  63. #DevoxxFR
    Logging
    • Log4j ¯\_(ツ)_/¯
    • Local à chaque machine, puis
    consolidé sur HDFS ou S3 par YARN
    • En mode client, le log driver n’est
    pas consolidé !
    33

    View full-size slide

  64. #DevoxxFR
    Logging Tips
    • Modifier le template log4j.properties pour ajouter:
    • le nom du thread courant
    • un timestamp à la milliseconde
    Exemple
    %d{ISO8601} %p [%t] %c{1}: %m%n
    34

    View full-size slide

  65. #DevoxxFR
    Logging
    Event History
    Metrics
    35

    View full-size slide

  66. #DevoxxFR
    Spark Driver
    Spark History
    36
    Schedul
    er
    Executor

    1
    Executor

    2
    Executor

    3

    View full-size slide

  67. #DevoxxFR
    Spark Driver
    Web UI
    Spark History
    36
    Schedul
    er
    Executor

    1
    Executor

    2
    Executor

    3

    View full-size slide

  68. #DevoxxFR
    EventLog
    Listener
    Storage
    Event
    Log
    hdfs://spark-history/application_125321332_1
    Spark Driver
    Web UI
    Spark History
    36
    Schedul
    er
    Executor

    1
    Executor

    2
    Executor

    3

    View full-size slide

  69. #DevoxxFR
    EventLog
    Listener
    Storage
    Event
    Log
    hdfs://spark-history/application_125321332_1
    Spark Driver
    Web UI
    Spark History
    36
    Schedul
    er
    Executor

    1
    Executor

    2
    Executor

    3
    Spark
    History
    Server

    View full-size slide

  70. #DevoxxFR
    Infos dans Event Log
    37
    Configuration
    Quantité de données échangées
    Performance détaillée (wall-time et cpu-time) par Task
    Plans d’execution
    Métriques et accumulators
    ...

    View full-size slide

  71. #DevoxxFR
    Web UI ❤
    38

    View full-size slide

  72. #DevoxxFR
    Définir son SparkListener
    class DemoSparkListener extends SparkListener {
    override def onStageCompleted(event: SparkListenerStageCompleted) {
    val info = event.stageInfo
    if (info.completionTime.isDefined) {
    val duration = info.completionTime.get - info.submissionTime.get
    println(s"Stage ${info.name} succeeded: ${duration} ms")
    } else {
    println(s"Stage ${info.name} failed: ${info.failureReason.get}")
    }
    }
    }
    39
    https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.scheduler.SparkListener

    View full-size slide

  73. #DevoxxFR
    Ajouter un Listener
    Configuration:
    spark.extraListeners
    Code:
    val demoListener = new DemoSparkListener()
    sparkContext.addSparkListener(demoListener)
    ... do work ...
    sparkContext.removeSparkListener(demoListener)
    40

    View full-size slide

  74. #DevoxxFR
    Quelques Listeners utiles
    41
    Sparklint 

    https://github.com/groupon/sparklint
    Sparklens 

    https://github.com/qubole/sparklens
    SparkMeasure 

    https://github.com/LucaCanali/sparkMeasure

    View full-size slide

  75. #DevoxxFR
    Sparklint
    42

    View full-size slide

  76. #DevoxxFR
    Sparklens
    43

    View full-size slide

  77. #DevoxxFR
    Logging
    Event History
    Metrics
    44

    View full-size slide

  78. #DevoxxFR
    Metrics & Monitoring
    45
    Sources
    Jvm
    Scheduler
    HiveSQL
    Streaming
    ...
    Sinks Standard
    REST API

    JMX

    CSV

    Graphite

    Statsd
    Ganglia
    Sinks Communauté
    Prometheus
    Elasticsearch
    OpenTSDB
    InfluxDB
    ...

    View full-size slide

  79. #DevoxxFR
    Exemple configuration: metrics.properties
    driver.source.jvm.class=org.apache.spark.metrics.source.JvmSource

    executor.source.jvm.class=org.apache.spark.metrics.source.JvmSource
    *.sink.jmx.class=org.apache.spark.metrics.sink.JmxSink
    *.sink.elk.class=org.apache.spark.elk.metrics.sink.ElasticsearchSink
    *.sink.elk.host=localhost
    *.sink.elk.port=9200
    *.sink.elk.index=spark
    *.sink.elk.period=30
    *.sink.elk.unit=seconds

    46

    View full-size slide

  80. #DevoxxFR
    Metrics JVM
    47

    View full-size slide

  81. #DevoxxFR
    Metrics streaming
    48

    View full-size slide

  82. #DevoxxFR
    Metrics streaming
    49

    View full-size slide

  83. #DevoxxFR 50

    View full-size slide

  84. #DevoxxFR
    Programme
    51
    API
    Modèle
    Exécution
    Catalyst
    Monitoring

    View full-size slide

  85. #DevoxxFR
    C'est quoi ?
    52
    Catalyst:
    Moteur d'optimisation des plans d'exécution des requêtes
    Dataset & SQL
    Tungsten:
    Ensemble d'optimisation techniques notamment pour
    réduire la pression mémoire et augmenter l'efficacité CPU

    View full-size slide

  86. #DevoxxFR
    Itinéraire d'une requête SQL
    53
    Dataset
    SQL
    Parsed
    Plan
    Analyzed
    Plan
    Optimized

    Plan
    Physical

    Plan
    Task
    RDD
    DRIVER EXECUTOR

    View full-size slide

  87. #DevoxxFR
    La requête initiale
    54
    SQL
    select nature, count(*) from rna group by nature
    Dataset
    rna.groupBy('nature).count.show

    View full-size slide

  88. #DevoxxFR
    Parsed Plan
    GlobalLimit 21
    +- LocalLimit 21
    +- Project [cast(nature#19 as string) AS
    nature#7284, cast(count#7276L as string) AS count#7285]
    +- Aggregate [nature#19], [nature#19, count(1) AS
    count#7276L]
    +-
    Relation[id#10,id_ex#11,siret#12,rup_mi#13,gestion#14,d
    ate_creat#15,date_decla#16,date_publi#17,date_disso#18,
    nature#19,groupement#20,titre#21,titre_court#22,objet#2
    3,objet_social1#24,objet_social2#25,adrs_complement#26,
    adrs_numvoie#27,adrs_repetition#28,adrs_typevoie#29,adr
    s_libvoie#30,adrs_distrib#31,adrs_codeinsee#32,adrs_cod
    epostal#33,... 15 more fields] csv
    55

    View full-size slide

  89. #DevoxxFR
    Parsed -> Analyzed
    56
    Relation [...fields...] csv
    Aggregate [nature] [nature, count(1)]
    Project [nature, count]
    LocalLimit 21
    GlobalLimit 21

    View full-size slide

  90. #DevoxxFR
    Parsed -> Analyzed
    56
    Relation [...fields...] csv id, siret, rup_mi, gestion, nature, ...
    Aggregate [nature] [nature, count(1)]
    Project [nature, count]
    LocalLimit 21
    GlobalLimit 21

    View full-size slide

  91. #DevoxxFR
    Parsed -> Analyzed
    56
    Relation [...fields...] csv id, siret, rup_mi, gestion, nature, ...
    nature: String, count: Long
    Aggregate [nature] [nature, count(1)]
    Project [nature, count]
    LocalLimit 21
    GlobalLimit 21

    View full-size slide

  92. #DevoxxFR
    Parsed -> Analyzed
    56
    Relation [...fields...] csv id, siret, rup_mi, gestion, nature, ...
    nature: String, count: Long
    nature: String, count: String
    Aggregate [nature] [nature, count(1)]
    Project [nature, count]
    LocalLimit 21
    GlobalLimit 21

    View full-size slide

  93. #DevoxxFR
    Parsed -> Analyzed
    56
    Relation [...fields...] csv id, siret, rup_mi, gestion, nature, ...
    nature: String, count: Long
    nature: String, count: String
    nature: String, count: String
    Aggregate [nature] [nature, count(1)]
    Project [nature, count]
    LocalLimit 21
    GlobalLimit 21

    View full-size slide

  94. #DevoxxFR
    Parsed -> Analyzed
    56
    Relation [...fields...] csv id, siret, rup_mi, gestion, nature, ...
    nature: String, count: Long
    nature: String, count: String
    nature: String, count: String
    nature: String, count: String
    Aggregate [nature] [nature, count(1)]
    Project [nature, count]
    LocalLimit 21
    GlobalLimit 21

    View full-size slide

  95. #DevoxxFR
    Optimiseur Heuristique
    57
    Applique des règles heuristiques de transformation du graphe de
    dépendance pour accélérer les traitements :
    - PartitionPruning
    - PredicatePushdown
    - ProjectionPushdown
    ...
    + les règles de l'algèbre relationnel

    View full-size slide

  96. #DevoxxFR
    Partition Pruning
    58
    rna
    year=2018
    part-0001
    year=2016 year=2017 year=2019
    part-0001 part-0001 part-0001

    View full-size slide

  97. #DevoxxFR
    Partition Pruning
    58
    rna
    year=2018
    part-0001
    year=2016 year=2017 year=2019
    part-0001 part-0001 part-0001
    select nature, count(*) from rna where year=2018 group by nature

    View full-size slide

  98. #DevoxxFR
    Partition Pruning
    58
    rna
    year=2018
    part-0001
    select nature, count(*) from rna where year=2018 group by nature

    View full-size slide

  99. #DevoxxFR
    Pushdown Predicate
    59
    rna
    year=2018
    part-0001
    year=2016 year=2017 year=2019
    part-0001 part-0001 part-0001

    View full-size slide

  100. #DevoxxFR
    Pushdown Predicate
    59
    rna
    year=2018
    part-0001
    year=2016 year=2017 year=2019
    part-0001 part-0001 part-0001
    select nature, count(*) from rna where type='D' group by nature

    View full-size slide

  101. #DevoxxFR
    Pushdown Predicate
    59
    rna
    year=2018
    part-0001
    year=2016 year=2017 year=2019
    part-0001 part-0001 part-0001
    select nature, count(*) from rna where type='D' group by nature
    part-0001
    part-0001 part-0001
    part-0001
    part-0001
    part-0001
    part-0001
    part-0001

    View full-size slide

  102. #DevoxxFR
    year=2016 year=2017 year=2019
    part-0001 part-0001 part-0001
    part-0001
    Projection Pushdown
    60
    rna
    year=2018

    View full-size slide

  103. #DevoxxFR
    year=2016 year=2017 year=2019
    part-0001 part-0001 part-0001
    part-0001
    Projection Pushdown
    60
    rna
    year=2018
    select nature, count(*) from rna group by nature

    View full-size slide

  104. #DevoxxFR
    year=2016 year=2017 year=2019
    part-0001 part-0001 part-0001
    part-0001 part-0001
    part-0001
    part-0001
    part-0001 part-0001
    part-0001
    part-0001
    part-0001
    Projection Pushdown
    60
    rna
    year=2018
    select nature, count(*) from rna group by nature

    View full-size slide

  105. #DevoxxFR
    Optimized Plan
    GlobalLimit 21
    +- LocalLimit 21
    +- Aggregate [nature#19], [nature#19,
    cast(count(1) as string) AS count#7285]
    +- Project [nature#19]
    +-
    Relation[id#10,id_ex#11,siret#12,rup_mi#13,gestion#1
    4,date_creat#15,date_decla#16,date_publi#17,date_dis
    so#18,nature#19,groupement#20,titre#21,titre_court#2
    2,objet#23,objet_social1#24,objet_social2#25,adrs_co
    mplement#26,adrs_numvoie#27,adrs_repetition#28,adrs_
    typevoie#29,adrs_libvoie#30,adrs_distrib#31,adrs_cod
    einsee#32,adrs_codepostal#33,... 15 more fields] csv
    61

    View full-size slide

  106. #DevoxxFR
    Physical Plan
    CollectLimit 21
    +- *(2) HashAggregate(keys=[nature#19],
    functions=[count(1)], output=[nature#19,
    count#7285])
    +- Exchange hashpartitioning(nature#19, 200)
    +- *(1) HashAggregate(keys=[nature#19],
    functions=[partial_count(1)], output=[nature#19,
    count#7280L])
    +- *(1) FileScan csv [nature#19] Batched:
    false, Format: CSV, Location:
    InMemoryFileIndex[file:/Users/raphael/tmp/
    rna_waldec_20190301.csv], PartitionFilters: [],
    PushedFilters: [], ReadSchema: struct
    62

    View full-size slide

  107. #DevoxxFR
    Obtenir les plans
    63
    Web UI:
    Onglet SQL -> Détails
    SQL:
    EXPLAIN [EXTENDED]
    Dataset:
    rna.explain(true)

    View full-size slide

  108. #DevoxxFR
    Optimiseur par coût
    64
    Permet d'utiliser les statistiques des sources de données
    pour sélectionner le meilleur plan physique parmi tous les
    plans possibles.
    Principal impact sur les opérations "join"
    Optimized

    Plan
    Physical

    Plan Physical

    Plan
    Physical

    Plan
    Physical

    Plan
    Physical

    Plan
    Physical

    Plan
    Physical

    Plan
    Physical

    Plan
    Physical

    Plan

    View full-size slide

  109. #DevoxxFR
    Optimiser par cout
    65
    Désactivé par défaut
    spark.sql.cbo.enabled=true
    spark.sql.cbo.joinReorder.enabled=true
    Ne fonctionne que sur tables
    Nécessite un calcul préalable des statistiques
    ANALYZE TABLE mutable COMPUTE STATISTICS
    ANALYZE TABLE mutable COMPUTE STATISTICS FOR COLUMNS col1, col2

    View full-size slide

  110. #DevoxxFR
    Dernière étape... codegen !
    • Maximise l'efficacité de la structure
    InternalRow utilisée pour stocker les
    données en mémoire
    • Evite le dispatch de méthodes
    mégamorphiques
    66

    View full-size slide

  111. #DevoxxFR
    Génération de code
    67
    FileScan
    HashAggregate
    Exchange
    HashAggregate
    Limit
    Exchange
    Code
    source
    Task
    compilée
    WholeStage 1
    Janino
    expressions
    codegen
    Code
    source
    Task
    compilée
    WholeStage 2

    View full-size slide

  112. #DevoxxFR
    Voir le code...
    68
    Dataset
    df.queryExecution.debug.codegen
    SQL
    EXPLAIN CODEGEN

    View full-size slide

  113. #DevoxxFR
    RDD vs Dataset
    69
    RDD[MyClass] Dataset[Row] Dataset[MyClass]
    Analyse Compilation Runtime Runtime
    Optimisation Manuelle
    Heuristique

    CBO
    Heuristique

    CBO
    Mémoire Objets Java/Scala InternalRow
    InternalRow

    Objets Java/Scala
    Tasks operator initial Inlined codegen Inlined codegen

    View full-size slide

  114. #DevoxxFR 70

    View full-size slide

  115. #DevoxxFR
    Programme
    71
    API
    Modèle
    Exécution
    Catalyst
    Monitoring
    Formats
    Parallélisme
    Gestion mémoire
    AWS
    Joins & Skew
    Sizing Exécuteurs
    ???

    View full-size slide

  116. #DevoxxFR
    Histoire de parallélisme
    72

    View full-size slide

  117. #DevoxxFR
    Principes
    • Nombre de Tasks d'un Stage est égal au nombre de
    partitions RDD en fin de stage
    • Donc 3 cas possibles:
    • Input
    • Shuffle
    • Output
    73

    View full-size slide

  118. #DevoxxFR
    Parallélisme d'Input
    74
    1 partition par split ou par fichier
    Petites partitions concaténées pour être entre:
    spark.sql.files.openCostInBytes (4M par défaut)
    spark.sql.files.maxSplitBytes (128M par défaut)

    View full-size slide

  119. #DevoxxFR
    Shuffle
    75
    Un seul paramètre
    spark.sql.shuffle.partitions (200 par défaut)
    "Bientôt" un support de shuffle adaptatif:
    SPARK-9850

    View full-size slide

  120. #DevoxxFR
    Output
    76
    Nombre de fichiers écrits:
    • Partitions RDD (i.e nombre de Tasks)
    • Partitionnement de la table
    • Bucketing

    View full-size slide

  121. #DevoxxFR
    Exemple
    77
    200 Partitions sur le dernier stage
    1 colonne de partition avec 20 valeurs
    => jusqu'à 200 * 20 fichiers résultats**
    **multiplié par nombre de buckets si activé !

    View full-size slide

  122. #DevoxxFR
    Maîtriser le nombre de fichiers
    78
    Utiliser repartition pour regrouper les données en des groupes ayant
    un sens
    df.repartition('dpt)

    .write

    .partitionBy("dpt")

    .parquet("my_dataset")
    1 seul fichier de sortie par partition sur disque

    View full-size slide

  123. #DevoxxFR
    Comment paramétrer le
    driver et les exécuteurs ?
    79

    View full-size slide

  124. #DevoxxFR
    2 problèmes à résoudre
    80
    Déterminer le pool de ressource
    optimal CPU/Mem pour l’application
    Maximiser l’efficacité du découpage
    entre exécutors

    View full-size slide

  125. #DevoxxFR
    Allocation pool
    81
    Multi-tenant:
    Limité par répartition ressources cluster et queueing policy
    YARN
    Mono-tenant:
    Limité par parallélisme des données et des stages
    Allocation dynamique permet de simplifier cette gestion au
    prix d’une latence de job un peu plus élevée

    View full-size slide

  126. #DevoxxFR
    Allocation dynamique
    spark.dynamicAllocation.enabled = true
    spark.dynamicAllocation.minExecutors = 5

    spark.dynamicAllocation.initialExecutors = 10

    spark.dynamicAllocation.maxExecutors = 100
    spark.dynamicAllocation.executorIdleTimeout = 30s

    spark.dynamicAllocation.cachedExecutorIdleTimeout = 1800s
    spark.dynamicAllocation.executorAllocationRatio = 1
    spark.dynamicAllocation.schedulerBacklogTimeout = 1s

    spark.dynamicAllocation.sustainedSchedulerBacklogTimeout = 1s
    spark.shuffle.service.enabled=true
    82

    View full-size slide

  127. #DevoxxFR
    Sizing executeurs
    83
    Moins d’exécuteurs:
    - maximise les opportunités de partage de données intra-process
    - augmente les temps de full-GC
    - (HDFS) peut créer de la contention d’I/O
    Plus d’exécuteurs:
    - plus de shuffle
    - plus simple à allouer par le cluster manager

    View full-size slide

  128. #DevoxxFR
    Heuristique
    84
    Maximum 6 cores par exécuteur
    Affecter la taille mémoire pour maximiser l’utilisation du
    noeud. 2 à 4G/core est une bonne base
    Laisser des ressources à l'OS
    Prendre en compte le YARN memoryOverhead (10% heap par
    défaut)
    Pour valider, mesurez les différences de performance
    entre configurations !

    View full-size slide

  129. #DevoxxFR
    Exemple de sizing
    85
    m5.12xlarge - 48 cores - 192 GB RAM
    1 core pour OS, ~5 à 10% RAM pour OS/Cache disque

    -> YARN peut allouer au plus 47 cores et 180 G RAM
    On prend 5 cores / executor -> 9 executors soit 20 GB total/
    executor

    Yarn memoryOverhead = 10 à 15% donc on fixe heap a 18G
    —num-executors 9 —executor-cores 5 —executor-memory
    18G

    View full-size slide

  130. #DevoxxFR
    Optimiser les joins et le
    skew ?
    86

    View full-size slide

  131. #DevoxxFR
    2 types principaux types
    de join
    87

    View full-size slide

  132. #DevoxxFR
    2 types principaux types
    de join
    87
    SortMergeJoin
    Data 1 Data 2
    Sort[key] Sort[key]
    Merge
    Data 3

    View full-size slide

  133. #DevoxxFR
    2 types principaux types
    de join
    87
    SortMergeJoin
    Data 1 Data 2
    Sort[key] Sort[key]
    Merge
    Data 3
    HashJoin
    Data 1 Data 2
    Hash[key]
    Merge
    Data 3

    View full-size slide

  134. #DevoxxFR
    Cas d'utilisation
    88
    SortMergeJoin est le join par défaut mais coûteux dans le cas
    général
    1 shuffle + 2 sorts
    Si Spark détecte qu'un des 2 datasets est "petit", il va utiliser
    un BroadcastHashJoin à la place
    Si Spark sait que les 2 datasets sont déjà partitionnés de
    manière équivalentes (bucket), il va éviter le shuffle

    View full-size slide

  135. #DevoxxFR
    Bucketing
    89
    Activé uniquement pour les tables (donc via metastore), "pre
    shuffle persistant" d'un dataset.
    df.repartition(30,'year,'key)

    .write

    .partitionBy("year")

    .bucketBy(30,"key")

    .sortBy("key")

    .saveAsTable("my_table")
    Bucketing accélère group by, join et windowing sur les
    colonnes concernées

    View full-size slide

  136. #DevoxxFR
    Skew et ré-équilibrage de charge
    90
    3 stratégies pour gérer le skew (déséquilibre de données):
    - ajouter un sel pour mieux répartir les clés et agréger en 2
    étapes
    - avoir 2 traitements différents pour la clé dominante et le
    reste (typiquement NULL handling)
    - accepter le skew si le delta de temps n'est pas trop
    important

    View full-size slide

  137. #DevoxxFR
    Sel + Merge
    91

    View full-size slide

  138. #DevoxxFR
    Sel + Merge
    val df = df1.groupBy(‘id).agg(sum(‘value))
    91

    View full-size slide

  139. #DevoxxFR
    Sel + Merge
    val df = df1.groupBy(‘id).agg(sum(‘value))
    df1.withColumn("salt", (rand()*4) cast "int")

    .groupBy(‘id, ’salt).agg(sum(‘value) as "value")

    .groupBy(‘id).agg(sum(‘value))
    91

    View full-size slide

  140. #DevoxxFR
    Sel + Merge
    val df = df1.groupBy(‘id).agg(sum(‘value))
    df1.withColumn("salt", (rand()*4) cast "int")

    .groupBy(‘id, ’salt).agg(sum(‘value) as "value")

    .groupBy(‘id).agg(sum(‘value))
    df1.withColumn("salt", when(‘id isNull, (rand()*50)
    cast "int").otherwise(1))

    .groupBy(‘id, ’salt).agg(sum(‘value) as "value")

    .groupBy(‘id).agg(sum(‘value))
    91

    View full-size slide

  141. #DevoxxFR
    Gestion Mémoire et OOM
    92

    View full-size slide

  142. #DevoxxFR
    Modèle mémoire Exécuteur
    93
    JVM Native
    Reserved - 300M
    User Memory
    Execution
    Storage
    Mem
    JVM Heap
    Memory

    Offheap
    spark.executor.memory
    spark.executor.memoryOverhead
    spark.memory.fraction
    Unified Memory
    Total Heap
    spark.memory.storageFraction
    Storage
    Storage + Execution
    Unified memory

    View full-size slide

  143. #DevoxxFR
    Spilling
    94
    Spilling: Quand la zone execution + storage est trop petite
    pour tout accueillir, une partie de la mémoire est copiée sur
    disque et libérée
    Partie exécution n'est jamais spilled, toujours storage
    Si le storage est non vide, il ne peut pas être spilled en dessous
    du spark.memory.storageFraction
    Spark a besoin de maxPartitionSize * executor-cores en
    unified memory pour l'execution de toutes les tasks en
    parallèle

    View full-size slide

  144. #DevoxxFR
    Gérer la pression sur la Heap
    • Regarder les métriques !
    • Ajuster le spark.memory.fraction
    • Adapter le GC (plutôt G1GC ou ParallelGC)
    • Réduire l'utilisation des UDF et opérations Dataset
    • Diminuer le poids des structures de données
    95

    View full-size slide

  145. #DevoxxFR
    Le cas du memoryOverhead
    96
    WARN TaskSetManager: Lost task 49.2 in stage
    6.0 (TID xxx, xxx.xxx.xxx.compute.internal):
    ExecutorLostFailure (executor 16 exited caused by
    one of the running tasks) Reason: Container killed
    by YARN for exceeding memory limits. 10.4 GB of
    10.4 GB physical memory used. Consider boosting
    spark.yarn.executor.memoryOverhead.

    View full-size slide

  146. #DevoxxFR
    Le cas du memoryOverhead
    97
    - JVM Natives pools (Metaspace, Compiler,
    Threads stacks, DirectBuffers(nio), etc...)
    - Python process (or R)
    - Mémoire de libs JNI (xgboost, lightgbm, ...)

    View full-size slide

  147. #DevoxxFR
    Gérer le memoryOverhead
    98

    View full-size slide

  148. #DevoxxFR
    Gérer le memoryOverhead
    98
    Limiter les DirectBuffers

    spark.executor.extraJavaOptions=-XX:MaxDirectMemorySize=XXXM

    ou

    spark.shuffle.io.preferDirectBufs=false

    View full-size slide

  149. #DevoxxFR
    Gérer le memoryOverhead
    98
    Limiter les DirectBuffers

    spark.executor.extraJavaOptions=-XX:MaxDirectMemorySize=XXXM

    ou

    spark.shuffle.io.preferDirectBufs=false
    Limiter et déclarer la RAM pour le process Python

    spark.executor.pyspark.memory=XXXM

    View full-size slide

  150. #DevoxxFR
    Gérer le memoryOverhead
    98
    Limiter les DirectBuffers

    spark.executor.extraJavaOptions=-XX:MaxDirectMemorySize=XXXM

    ou

    spark.shuffle.io.preferDirectBufs=false
    Limiter et déclarer la RAM pour le process Python

    spark.executor.pyspark.memory=XXXM
    Augmenter le memory overhead

    spark.executor.memoryOverhead=15% heap

    View full-size slide

  151. #DevoxxFR
    Gérer le memoryOverhead
    98
    Limiter les DirectBuffers

    spark.executor.extraJavaOptions=-XX:MaxDirectMemorySize=XXXM

    ou

    spark.shuffle.io.preferDirectBufs=false
    Limiter et déclarer la RAM pour le process Python

    spark.executor.pyspark.memory=XXXM
    Augmenter le memory overhead

    spark.executor.memoryOverhead=15% heap

    View full-size slide

  152. #DevoxxFR
    Quel format utiliser pour
    stocker les données ?
    99

    View full-size slide

  153. #DevoxxFR
    Le bon format: un compromis
    100

    View full-size slide

  154. #DevoxxFR
    Le bon format: un compromis
    100
    Expressivité

    View full-size slide

  155. #DevoxxFR
    Le bon format: un compromis
    100
    Expressivité
    Evolutivité

    View full-size slide

  156. #DevoxxFR
    Le bon format: un compromis
    100
    Expressivité
    Evolutivité
    Compatibilité

    View full-size slide

  157. #DevoxxFR
    Le bon format: un compromis
    100
    Expressivité
    Evolutivité
    Compatibilité


    Performance

    View full-size slide

  158. #DevoxxFR
    Le bon format: un compromis
    100
    Expressivité
    Evolutivité
    Compatibilité


    Performance
    Efficacité


    View full-size slide

  159. #DevoxxFR
    Les familles
    101
    Formats texte Formats binaires

    View full-size slide

  160. #DevoxxFR
    Les familles
    101
    CSV

    - universel

    - compact

    - pas de schema

    - parsing
    compliqué

    Formats texte Formats binaires

    View full-size slide

  161. #DevoxxFR
    Les familles
    101
    CSV

    - universel

    - compact

    - pas de schema

    - parsing
    compliqué

    JSON

    - inefficace

    - schema limité
    Formats texte Formats binaires

    View full-size slide

  162. #DevoxxFR
    Les familles
    101
    CSV

    - universel

    - compact

    - pas de schema

    - parsing
    compliqué

    JSON

    - inefficace

    - schema limité
    XML

    - inefficace

    - OOM
    Formats texte Formats binaires

    View full-size slide

  163. #DevoxxFR
    Les familles
    101
    CSV

    - universel

    - compact

    - pas de schema

    - parsing
    compliqué

    JSON

    - inefficace

    - schema limité
    XML

    - inefficace

    - OOM
    Parquet

    - stockage colonne

    - optimisé Spark

    - écosystème riche

    - schéma évolutif
    Formats texte Formats binaires

    View full-size slide

  164. #DevoxxFR
    Les familles
    101
    CSV

    - universel

    - compact

    - pas de schema

    - parsing
    compliqué

    JSON

    - inefficace

    - schema limité
    XML

    - inefficace

    - OOM
    Parquet

    - stockage colonne

    - optimisé Spark

    - écosystème riche

    - schéma évolutif
    ORC

    - stockage colonne

    - optimisé Hive

    - indexes

    - ACID
    Formats texte Formats binaires

    View full-size slide

  165. #DevoxxFR
    Les familles
    101
    CSV

    - universel

    - compact

    - pas de schema

    - parsing
    compliqué

    JSON

    - inefficace

    - schema limité
    XML

    - inefficace

    - OOM
    Parquet

    - stockage colonne

    - optimisé Spark

    - écosystème riche

    - schéma évolutif
    ORC

    - stockage colonne

    - optimisé Hive

    - indexes

    - ACID
    Avro

    - stockage record

    - très utilisé Kafka

    - schéma riche

    - RPC
    Formats texte Formats binaires

    View full-size slide

  166. #DevoxxFR
    Les compressions
    102
    Taux

    Compression
    Débit 

    Compression
    Débit

    Décompression
    GZIP ☆☆☆☆ ☆ ☆☆
    SNAPPY ☆☆ ☆☆☆☆ ☆☆☆☆
    BZIP2 ☆☆☆☆☆ ☆ ☆
    LZ4 ☆ ☆☆☆☆☆ ☆☆☆☆☆
    ZSTD ☆☆☆ ☆☆☆ ☆☆☆
    Un fichier entièrement compressé est non-splittable sauf BZIP2

    View full-size slide

  167. #DevoxxFR
    Compression dans Spark
    103
    Spark peut lires les fichiers compressés directement mais ne
    supporte pas .zip ou .7z
    En interne, Spark utilise LZ4 par défaut pour la compression
    des shuffles, spills et broadcast.
    Pour améliorer les taux de compression:
    spark.io.compression.lz4.blockSize=128k ou 256k

    View full-size slide

  168. #DevoxxFR
    Recommandations
    104
    Parquet+snappy en priorité pour Spark
    Privilégiez les formats avec schéma pour la maintenance et
    l'évolutivité
    Evitez JSON et XML
    Attention aux livraisons de gros fichiers compressés !

    View full-size slide

  169. #DevoxxFR
    Spécificités cloud
    105

    View full-size slide

  170. #DevoxxFR
    Types d'instances
    106
    Les instances m.*, c.* ou r.* suffisent dans
    la plupart des cas
    Surveiller les IO et bande passante réseau
    Des SSDs locaux sont utiles pour la
    performance

    View full-size slide

  171. #DevoxxFR
    Stockage S3
    107
    Accès aux méta-données est très long
    Pas d'opérations de move native
    EMRFS peut introduire des bottlenecks
    supplémentaires (chiffrement, cohérence via
    dynamoDB)

    View full-size slide

  172. #DevoxxFR
    A suivre de près
    108
    http://iceberg.incubator.apache.org

    View full-size slide

  173. #DevoxxFR
    En résumé
    109

    View full-size slide

  174. #DevoxxFR 110
    Measure
    Model
    Hypothesis
    Adjustment
    Tuning

    View full-size slide

  175. #DevoxxFR
    Modèle d'exécution
    111

    View full-size slide

  176. #DevoxxFR
    Logging
    Event History
    Metrics
    112

    View full-size slide

  177. #DevoxxFR
    Catalyst
    113
    Dataset
    SQL
    Parsed
    Plan
    Analyzed
    Plan
    Optimized

    Plan
    Physical

    Plan
    Task
    RDD
    Physical

    Plan
    Physical

    Plan
    Physical

    Plan
    Physical

    Plan
    Physical

    Plan
    Meta-données
    Règles heuristiques
    Statistiques
    Codegen
    Catalog

    View full-size slide

  178. #DevoxxFR
    RDD vs Dataset
    114
    RDD[MyClass] Dataset[Row] Dataset[MyClass]
    Analyse Compilation Runtime Runtime
    Optimisation Manuelle
    Heuristique

    CBO
    Heuristique

    CBO
    Mémoire Objets Java/Scala InternalRow
    InternalRow

    Objets Java/Scala
    Tasks code initial Inlined codegen Inlined codegen

    View full-size slide

  179. #DevoxxFR
    Bonnes pratiques
    • Privilégiez les formats type Parquet ou ORC
    • Utilisez un Metastore
    • Privilégiez des exécuteurs multi-core
    115

    View full-size slide

  180. #DevoxxFR
    Bonnes pratiques de code
    • Pas de traitement sur le driver
    • Pensez utilisation ultérieure du dataset
    (partition by, sort by, bucket by)
    • Surveiller le nombre de fichiers d'output
    • Maximiser le parallélisme des traitements
    116

    View full-size slide

  181. #DevoxxFR
    Les limites
    • Shuffle est la limite principale de montée en
    charge et difficulté de paramétrage
    • Efficacité du cache Spark est limitée
    • Cost-based Optimizer pas encore universel
    117

    View full-size slide

  182. #DevoxxFR
    Pour optimiser Spark, il faut
    comprendre vos données 

    et son modèle d'exécution
    118

    View full-size slide

  183. #DevoxxFR 119
    Merci
    @raphaelluta
    raphael.luta at aptiwan.com
    Crédits Photos
    Pascal Mauerhofer
    Monica Leonardi
    Samuel Scrimshaw
    Pascal van de Vendel
    Premkumar Masilamani
    Maria E. Mayobre

    View full-size slide