Je sais qu'il y a beaucoup de questions sur SO sur les erreurs de mémoire insuffisante sur Spark mais je n'ai pas trouvé de solution pour la mienne.
J'ai un workflow simple:
filter
jusqu'à un petit sous-ensemble de lignesselect
un petit sous-ensemble de colonnescollect
dans le nœud du pilote (donc je peux faire des opérations supplémentaires dans R
)Lorsque j'exécute ce qui précède, puis cache
la table à spark mémoire, il prend <2 Go - minuscule par rapport à la mémoire disponible pour mon cluster - alors j'obtiens une erreur OOM lorsque J'essaie de collect
les données sur mon nœud de pilote.
J'ai essayé de courir sur les configurations suivantes:
Pour chacun d'eux, j'ai joué avec de nombreuses configurations de executor.memory
, driver.memory
Et driver.maxResultSize
Pour couvrir la gamme complète des valeurs possibles dans ma mémoire disponible, mais je finis toujours avec une erreur de mémoire insuffisante à l'étape collect
; soit Java.lang.OutOfMemoryError: Java heap space
,Java.lang.OutOfMemoryError : GC overhead limit exceeded
Ou Error in invoke_method.spark_Shell_connection(spark_connection(jobj), : No status is returned.
(une erreur sparklyr
indiquant des problèmes de mémoire).
Sur la base de ma compréhension [limitée] de Spark, la mise en cache d'une table avant la collecte devrait forcer tous les calculs - c'est-à-dire que si la table se trouve joyeusement en mémoire après la mise en cache à <2 Go, alors je ne devrais pas avoir besoin de beaucoup plus de 2 Go de mémoire pour collecter dans le nœud du pilote.
Notez que les réponses à cette question ont quelques suggestions que je n'ai pas encore essayées, mais celles-ci sont susceptibles d'avoir un impact sur les performances (par exemple la sérialisation du RDD) donc nous aimerions éviter d'utiliser si possible.
Mes questions:
Je vous remercie
Edit: note en réponse au commentaire de @ Shaido ci-dessous, appeler cache
via Sparklyr "force le chargement des données en mémoire en exécutant un count(*)
over the table "[de la documentation Sparklyr] - c'est-à-dire que la table doit être en mémoire et que tous les calculs doivent être exécutés (je crois) avant d'appeler collect
.
Modifier: quelques observations supplémentaires depuis avoir suivi les suggestions ci-dessous:
driver.maxResultSize
Sur <1G, j'obtiens une erreur indiquant que la taille du RDD sérialisé est de 1030 Mo, supérieure à driver.maxResultSize.collect
, je constate que l'utilisation continue d'augmenter jusqu'à ce qu'elle atteigne ~ 90 Go, moment auquel l'erreur OOM se produit. Donc, pour une raison quelconque, la quantité de RAM utilisée pour effectuer l'opération collect
est ~ 100x supérieure à la taille de le RDD que j'essaye de collecter.Modifier: code ajouté ci-dessous, comme demandé dans les commentaires.
#__________________________________________________________________________________________________________________________________
# Set parameters used for filtering rows
#__________________________________________________________________________________________________________________________________
firstDate <- '2017-07-01'
maxDate <- '2017-08-31'
advertiserID <- '4529611'
advertiserID2 <- '4601141'
advertiserID3 <- '4601141'
library(dplyr)
library(stringr)
library(sparklyr)
#__________________________________________________________________________________________________________________________________
# Configure & connect to spark
#__________________________________________________________________________________________________________________________________
Sys.setenv("SPARK_MEM"="100g")
Sys.setenv(HADOOP_HOME="C:/Users/Jay.Ruffell/AppData/Local/rstudio/spark/Cache/spark-2.0.1-bin-hadoop2.7/tmp/hadoop")
config <- spark_config()
config$sparklyr.defaultPackages <- "org.Apache.hadoop:hadoop-aws:2.7.3" # used to connect to S3
Sys.setenv(AWS_ACCESS_KEY_ID="")
Sys.setenv(AWS_SECRET_ACCESS_KEY="") # setting these blank ensures that AWS uses the IAM roles associated with the cluster to define S3 permissions
# Specify memory parameters - have tried lots of different values here!
config$`sparklyr.Shell.driver-memory` <- '50g'
config$`sparklyr.Shell.executor-memory` <- '50g'
config$spark.driver.maxResultSize <- '50g'
sc <- spark_connect(master='local', config=config, version='2.0.1')
#__________________________________________________________________________________________________________________________________
# load data into spark from S3 ----
#__________________________________________________________________________________________________________________________________
#+++++++++++++++++++
# create spark table (not in memory yet) of all logfiles within logfiles path
#+++++++++++++++++++
spark_session(sc) %>%
invoke("read") %>%
invoke("format", "orc") %>%
invoke("load", 's3a://nz-omg-ann-aipl-data-lake/aip-connect-256537/orc-files/dcm-log-files/dt2-facts') %>%
invoke("createOrReplaceTempView", "alldatadf")
alldftbl <- tbl(sc, 'alldatadf') # create a reference to the sparkdf without loading into memory
#+++++++++++++++++++
# define variables used to filter table down to daterange
#+++++++++++++++++++
# Calculate firstDate & maxDate as unix timestamps
unixTime_firstDate <- as.numeric(as.POSIXct(firstDate))+1
unixTime_maxDate <- as.numeric(as.POSIXct(maxDate)) + 3600*24-1
# Convert daterange params into date_year, date_month & date_day values to pass to filter statement
dateRange <- as.character(seq(as.Date(firstDate), as.Date(maxDate), by=1))
years <- unique(substring(dateRange, first=1, last=4))
if(length(years)==1) years <- c(years, years)
year_y1 <- years[1]; year_y2 <- years[2]
months_y1 <- substring(dateRange[grepl(years[1], dateRange)], first=6, last=7)
minMonth_y1 <- min(months_y1)
maxMonth_y1 <- max(months_y1)
months_y2 <- substring(dateRange[grepl(years[2], dateRange)], first=6, last=7)
minMonth_y2 <- min(months_y2)
maxMonth_y2 <- max(months_y2)
# Repeat for 1 day prior to first date & one day after maxdate (because of the way logfile orc partitions are created, sometimes touchpoints can end up in the wrong folder by 1 day. So read in extra days, then filter by event time)
firstDateMinusOne <- as.Date(firstDate)-1
firstDateMinusOne_year <- substring(firstDateMinusOne, first=1, last=4)
firstDateMinusOne_month <- substring(firstDateMinusOne, first=6, last=7)
firstDateMinusOne_day <- substring(firstDateMinusOne, first=9, last=10)
maxDatePlusOne <- as.Date(maxDate)+1
maxDatePlusOne_year <- substring(maxDatePlusOne, first=1, last=4)
maxDatePlusOne_month <- substring(maxDatePlusOne, first=6, last=7)
maxDatePlusOne_day <- substring(maxDatePlusOne, first=9, last=10)
#+++++++++++++++++++
# Read in data, filter & select
#+++++++++++++++++++
# startTime <- proc.time()[3]
dftbl <- alldftbl %>% # create a reference to the sparkdf without loading into memory
# filter by month and year, using ORC partitions for extra speed
filter(((date_year==year_y1 & date_month>=minMonth_y1 & date_month<=maxMonth_y1) |
(date_year==year_y2 & date_month>=minMonth_y2 & date_month<=maxMonth_y2) |
(date_year==firstDateMinusOne_year & date_month==firstDateMinusOne_month & date_day==firstDateMinusOne_day) |
(date_year==maxDatePlusOne_year & date_month==maxDatePlusOne_month & date_day==maxDatePlusOne_day))) %>%
# filter to be within firstdate & maxdate. Note that event_time_char will be in UTC, so 12hrs behind.
filter(event_time>=(unixTime_firstDate*1000000) & event_time<(unixTime_maxDate*1000000)) %>%
# filter by advertiser ID
filter(((advertiser_id==advertiserID | advertiser_id==advertiserID2 | advertiser_id==advertiserID3) &
!is.na(advertiser_id)) |
((floodlight_configuration==advertiserID | floodlight_configuration==advertiserID2 |
floodlight_configuration==advertiserID3) & !is.na(floodlight_configuration)) & user_id!="0") %>%
# Define cols to keep
transmute(time=as.numeric(event_time/1000000),
user_id=as.character(user_id),
action_type=as.character(if(fact_type=='click') 'C' else if(fact_type=='impression') 'I' else if(fact_type=='activity') 'A' else NA),
lookup=concat_ws("_", campaign_id, ad_id, site_id_dcm, placement_id),
activity_lookup=as.character(activity_id),
sv1=as.character(segment_value_1),
other_data=as.character(other_data)) %>%
mutate(time_char=as.character(from_unixtime(time)))
# cache to memory
dftbl <- sdf_register(dftbl, "filtereddf")
tbl_cache(sc, "filtereddf")
#__________________________________________________________________________________________________________________________________
# Collect out of spark
#__________________________________________________________________________________________________________________________________
myDF <- collect(dftbl)
Lorsque vous dites collecter sur la trame de données, il se passe 2 choses,
Répondre:
Si vous cherchez à simplement charger les données dans la mémoire des exceutors, count () est également une action qui chargera les données dans la mémoire de l'exécuteur qui peut être utilisée par d'autres processus.
Si vous souhaitez extraire les données, essayez cela avec d'autres propriétés lors de la compression des données "--conf spark.driver.maxResultSize = 10g".
Comme mentionné ci-dessus, "cache" n'est pas une action, vérifiez RDD Persistence :
You can mark an RDD to be persisted using the persist() or cache() methods on it. The first time it is computed in an action, it will be kept in memory on the nodes.
Mais "collect" est une action, et tous les calculs (y compris "cache") seront lancés lorsque "collect" sera appelé.
Vous exécutez l'application en mode autonome, cela signifie que le chargement initial des données et tous les calculs seront effectués dans la même mémoire.
Le téléchargement de données et d'autres calculs sont utilisés dans la plupart des mémoires, pas pour "collecter".
Vous pouvez le vérifier en remplaçant "collect" par "count".