我使用PySpark创建了一个管道,它基本上循环遍历查询列表,每个查询都使用JDBC连接器在MySQL数据库上运行,将结果存储在Spark DataFrame中,过滤其只有一个值的列,然后将其保存为Parquet文件。
由于我使用for
循环遍历查询列表,因此每个查询和列过滤过程都是按顺序完成的,所以我没有使用所有可用的CPU。
我想要完成的是每当有CPU可用时启动一个新进程(查询+过滤器+ Parquet持久性)。
注意:我每次都处理不同的输入(查询),这与询问here的内容不同,后者在同一输入中进行不同的处理。另外,我不想指定同时运行多少进程,相反,我想在第一个进程中使用所有可用的CPU,如果仍有可用资源,请启动一个新进程一。如果仍有可用资源,请启动另一个资源,依此类推......
这是我正在运行的脚本:
# Imports
from pyspark.sql import SparkSession
from pyspark.sql.functions import isnull, when, count, countDistinct
from time import time
# Spark session initialization
spark = SparkSession \
.builder \
.appName('Filtering Columns') \
.config('spark.driver.memory', '16g') \
.config('spark.executor.memory', '16g') \
.config('spark.driver.extraClassPath',
'/path/to/mysql-connector-java-5.1.38.jar') \
.getOrCreate()
# JDBC config
jdbc_config = {
'url': 'jdbc:mysql://my_db_ip_address',
'properties': {
'user': 'my_db_user',
'password': 'my_db_password'
}
}
# My queries... Didn't put the real queries here, but
# they have nothing in special
queries_to_run = [
{
'table_name': 'table1',
'query': '''
(some query) as tmp
'''
},
{
'table_name': 'table2',
'query': '''
(some query) as tmp
'''
},
{
'table_name': 'table3',
'query': '''
(some query) as tmp
'''
},
...
]
# The function I'm using to filter the columns
def drop_constant_columns(df):
cols_to_drop_map = df.select([
when(countDistinct(column_name) == 1, True).alias(column_name)
for column_name in df.columns
]).first().asDict()
cols_to_drop = [
col for col, should_drop in cols_to_drop_map.iteritems()
if should_drop
]
return df.drop(*cols_to_drop)
# Here's the code that loops through the queries and, for each
# one of them:
# 1) Query a MySQL db
# 2) Store the result in a Spark DF
# 3) Filter the constant columns
# 4) Save the filtered DF in a Parquet format
for query in queries_to_run:
print('Querying {}'.format(query['table_name']))
df = spark.read.jdbc(table=query['query'], **jdbc_config)
print('Filtering {}'.format(query['table_name']))
n_cols = len(df.columns)
start = time()
df = drop_constant_columns(df)
elapsed = time() - start
n_cols_filtered = n_cols - len(df.columns)
print('Filtered {} of {} columns in {:.2f} secs'.format(n_cols_filtered, n_cols, elapsed))
print('Persisting {}'.format(query['table_name']))
df.write.mode('overwrite').parquet('./{}_test.parquet'.format(query['table_name']))
我在Ubuntu 2.2.1
上使用PySpark 2.7.12
,Python 16.04
。
答案 0 :(得分:1)
基本上,您需要为Spark上下文设置FAIR调度模式,创建多个线程并在每个线程中执行spark操作,以实现接近100%的集群饱和度(假设您的作业受CPU限制)。
虽然您提到您不想对线程数设置限制,但我仍然建议您这样做。您可以在操作系统中创建有限数量的线程,它们都可以从驱动程序中获取宝贵的内存和CPU资源。例如。你无法创建一百万个线程,无论如何都必须使用某种排队方式(例如信号量和锁的组合)。
另一方面,当所有执行程序都忙于100%的时间并且调度程序没有接受任何新任务时,有一个收益递减点,并且许多Spark作业只是闲置,等待执行程序变为可用。 Spark调度在任务级别完成,即如果某个执行者正在运行某个作业的任务,则不会被抢占。
您可以通过实验计算出足够多的并发请求,从而为所有请求提供最佳的整体处理时间。