Hadoop集群数据分发——pyspark导出及python写入excel文件或csv文件及邮件附件发送

场 景

  《Shell语言调用SparkSQL抽取业务DB数据到hadoop集群》讲述了如何将业务库的数据etl到hadoop集群ods层,在hadoop集群上经过spark,hive控件处理dwd层,dm层以及app层后,很多需要还是需要将集群的数据再分发到集群外,比如数据导成excel,csv,数据回写到mysql,sql server等等,也找了很多大数据工具,感觉都不是很灵活,于是乎就自己用python写一些常用的,第一次这么全面的写ETL工具,真香!

安装pyspark

  windows或者shell环境下,包有点大,可能有网络不稳定的话就多试几次,总会成功的,如果还不成功,再百度下办法。

pip install pyspark

定义Hive的pyspark取数脚本init_spark.py

  首先第一步是利用pyspark去数据仓库Hive里面把数据取出来,存在dataframe上,注意 ding.main(n)是有异常的话钉钉报警,可以参考: 调度Job报错或异常触发钉钉报警(Python 3.x版)

#-*-coding:utf-8-*-
from pyspark.sql import HiveContext,SparkSession
from pyspark.sql.functions import col
import os
import sys
from ding_talk_warning_report_py.main import ding_talk_with_agency as ding

#传入的参数是hive的取数脚本,确保脚本准确
def exec_hive(hivesql):
    try:
        #初始化,如果要执行hive sql,需要在初始化中加入enableHiveSupport()
        spark = SparkSession.builder.master("yarn").appName("DistributeData").enableHiveSupport().getOrCreate()
        hive_context = HiveContext(spark)#初始化pyspark的环境变量
        spark_df = hive_context.sql(hivesql)#执行Hive sql结果赋值给spark dataframe
        return spark_df #返回一个spark的dataframe,dataframe可以理解为一张表
    except Exception as e:
        print(e)
        n = [0,94]
        ding.main(n) #报错的话钉钉报警,可以参考:调度Job报错或异常触发钉钉报警(Python 3.x版)
        raise e  //异常外抛,系统爆错出来

将spark dataframe的结果导入excel或者csv的export_file.py

  以下是将Hive的抽取结果写入excel或者csv,实际调用的时候可以写一个type参数来确定是调用导入excel的export_excel还是导入csv的export_csv,注意 ding.main(n)是有异常的话钉钉报警,可以参考: 调度Job报错或异常触发钉钉报警(Python 3.x版)

import init_spark
import os
import sys
import pandas as pd
from ding_talk_warning_report_py.main import ding_talk_with_agency as ding

#参数1 hive_sql:hive上的取数脚本
#参数2 excel_path:excel的输出路径,如果不写,默认输出当前路径
#参数3 excel_name:excel的文件名字
#参数4 sheet:excel的sheet名字
#参数5 header_list:excel的表头,允许为空,即不需要表头
def export_excel(hive_sql,excel_path,excel_name,sheet,header_list):
    try:
        #调用init_spark
        spark_df = init_spark.exec_hive(hive_sql)
        #转成padas dataframe,如果表头为空,则无表头处理
        if(not header_list):
            padas_df = pd.DataFrame(spark_df.collect())
        #if(header_list or isinstance(header_list,list)):
        else:
            padas_df = pd.DataFrame(spark_df.collect(),columns=header_list) 
        #获取当前目录
        cur_path = os.path.dirname(__file__)
        #设置Excel文件名
        path = '%s/%s'%(excel_path,excel_name)#如果excel输出路径为空,则默认输出到当前路径,否则输出到指定路径。
        padas_df.to_excel(path,sheet_name=sheet,encoding='utf-8',index=False, header=header_list)#将数据写入excel

    except Exception as e:
        print(e)
        n = [0,95]
        ding.main(n)
        raise e  //异常外抛,系统爆错出来

#参数1 hive_sql:hive上的取数脚本
#参数2 csv_path:csv的输出路径,如果不写,默认输出当前路径
#参数3 csv_name:csv的文件名字
#参数4 sep_name:csv的分隔符
#参数5 header_list:csv的表头,允许为空,即不需要表头
#参数5 compression_name='infer',不压缩

def export_csv(hivesql,csv_path,csv_name,sep_name,header_list,compression_name='infer'):
    try:
        #调用init_spark
        spark_df = init_spark.exec_hive(hivesql)
        #转成padas dataframe,如果表头为空,则无表头处理
        if(not header_list):
            print("have no header") 
            padas_df = pd.DataFrame(spark_df.collect())
        #if(header_list or isinstance(header_list,list)):
        else:
            print("have header") 
            padas_df = pd.DataFrame(spark_df.collect(),columns=header_list) 
        #设置csv的输出路径和文件名,如果不写,默认输出当前路径
        path = '%s/%s'%(csv_path,csv_name)
        #写入csv
        padas_df.to_csv(path,encoding='utf-8',index=False, header=header_list,sep=sep_name,compression=compression_name)
        #padas_df.to_csv(path,encoding='utf-8',index=False, header=True,compression=compression_name)

    except Exception as e:
        print(e)
        n = [0,96]
        ding.main(n)
        raise e  //异常外抛,系统爆错出来

将生成的excel或者csv成附件发邮件send_mail.py

  往往生成excel或者csv后,都需要以邮件附件的形式发送,具体python代码如下。

#-*-coding:utf-8-*-
import os
import configparser
import sys
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.header import Header 
from ding_talk_warning_report_py.main import ding_talk_with_agency as ding
from email.header import Header

# 发送邮件
#参数
#mail_msg 邮件正文内容
#mail_subject 邮件主题
#mail_to 收件人
#mail_cc 抄送人
#mail_bcc 密送人
#is_attachment='N' 是否附件,默认为否
#attch_path='' 附件路径
#attch_name='' 附件名称

def send_email(mail_msg,mail_subject,mail_to,mail_cc,mail_bcc,is_attachment='N',attch_path='',attch_name=''):
    config = configparser.ConfigParser()
    #获取当前目录
    file_path = os.path.dirname(__file__)
    #设置config文件的路径,这里是从配置文件中读取邮箱服务器的信息。
    ini_path = "%s/conf/myconfig.ini"%file_path
    config.read(ini_path)
    mail_host = config['mail_config']["mail_host"] #邮箱服务器IP
    mail_port = int(config['mail_config']["mail_port"]) #邮箱服务器端口
    mail_user = config['mail_config']["mail_user"] #邮箱服务器登录用户名
    mail_passwd = config['mail_config']["mail_passwd"] #邮箱服务器登录密码
    sender = config['mail_config']["sender"] #邮箱发件人

    try:
        # 三个参数:第一个为文本内容,第二个 plain 设置文本格式,第三个 utf-8 设置编码
        message = MIMEMultipart() 

        message['From'] = Header("dw_bi", 'utf-8') #如果要匿名发送,则可以用Header匿名发件人
        message['To'] = mail_to # 收件人邮箱昵称、收件人邮箱账号
        message['Cc'] = mail_cc #邮件抄送人
        message['Bcc'] = mail_bcc #邮件暗送人
        message['Subject'] = mail_subject #邮件主题
     
        #邮件正文内容
        message.attach(MIMEText(mail_msg,'html','utf-8'))
        
        if is_attachment == 'Y':
            # 添加附件(附件为Excel或者csv格式的文本) 
            excel_path = '%s/%s'%(attch_path,attch_name)
            att = MIMEApplication(open(excel_path, 'rb').read(),'utf-8') 
            att["Content-Type"] = 'application/octet-stream'
            att.add_header('Content-Disposition', 'attachment', filename=attch_name)
            message.attach(att)  

        server = smtplib.SMTP(mail_host,mail_port)  #发件人邮箱中的SMTP服务器,端口
        server.login(mail_user,mail_passwd) #发件人邮箱用户和密码
        # 第三个参数,msg 是字符串,表示邮件。我们知道邮件一般由标题,发信人,收件人,邮件内容,附件等构成,发送邮件的时候,要注意 msg 的格式。这个格式就是 smtp 协议中定义的格式
        server.sendmail(sender,message['To'].split(",") + message['Cc'].split(",")+ message['Bcc'].split(","),message.as_string()) 
        
    except smtplib.SMTPException as e:
        print(e)
        print("Error: 无法发送邮件")
        n = [0,20]
        ding.main(n)
        raise e  //异常外抛,系统爆错出来

万事俱备后的主函数调用app_pushdown_file_to_mail.py

  主函数调用,为了规范,我把邮件组和这次的行为做成mysql配置表cfg_email,邮件组配置表如下:

CREATE TABLE `cfg_email` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT comment '自增id',
 `email_name` varchar(100) CHARACTER SET utf8 NOT NULL  comment '邮件名字',
 `email_owner` varchar(100) CHARACTER SET utf8 NOT NULL  comment '邮件主人名字',
 `group_id` int(11) NOT NULL  comment '邮件所属组id,可以是一个部门,也可以是一个系列的人',
 `group_name` varchar(100) CHARACTER SET utf8 NOT NULL  comment '邮件所属组名字,可以是一个部门,也可以是一个系列的人',
 `is_enable` int(11) DEFAULT NULL  comment '是否有效',
 `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
 `create_user` varchar(100) CHARACTER SET utf8 NOT NULL,
 `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 `update_user` varchar(100) CHARACTER SET utf8 NOT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `mail_key` (`email_name`,`group_id`,`is_enable`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

  此次整体的配置表cfg_app_pushdown_file_to_mail如下,主要涉及文件的类型,参数以及邮件的参数

CREATE TABLE `cfg_app_pushdown_file_to_mail` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `data_set_id` int(11) NOT NULL COMMENT '同一个数据集',
  `sequence_id` int(11) NOT NULL COMMENT '同一个数据集执行顺序',
  `sql_file` varchar(100) CHARACTER SET utf8 NOT NULL COMMENT '输入的sql文件名',
  `header_list` varchar(2000) CHARACTER SET utf8 NOT NULL COMMENT '输出文件的表头,不需要的话写空格',
  `file_type` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '输出文件类型,写csv或excel',
  `excel_sheet` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '如果输出文件为excel,则写sheet名,否则可为null',
  `csv_seq` varchar(20) CHARACTER SET utf8 DEFAULT NULL COMMENT '如果是csv文件,csv文件的列分隔符',
  `file_path` varchar(200) CHARACTER SET utf8 DEFAULT NULL COMMENT '输出数据文件路径,如果不填,则默认输出执行程序的当前路径',
  `file_name` varchar(200) CHARACTER SET utf8 NOT NULL COMMENT '输出文件名,不需要要带文件后缀',
  `is_send_mail` varchar(5) CHARACTER SET utf8 NOT NULL COMMENT '是否发送邮件,1是,0或其他否',
  `mail_to_groupid` int(11) DEFAULT NULL COMMENT '邮件收件组id,组id参考cfg_email表',
  `mail_cc_groupid` int(11) DEFAULT NULL COMMENT '邮件抄送人组id,组id参考cfg_email表',
  `mail_bcc_groupid` int(11) DEFAULT NULL COMMENT '邮件暗送组id,组id参考cfg_email表',
  `mail_subject` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '邮件主题',
  `is_enable` int(11) NOT NULL COMMENT '是否有效',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `create_user` varchar(100) CHARACTER SET utf8 NOT NULL,
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `update_user` varchar(100) CHARACTER SET utf8 NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

  对于这个mysql的认证配置信息,我们可以写在当前目录下的conf folder下的myconfig.ini下,即 “./conf/myconfig.ini”里面,其中mail_config是邮箱服务器的配置,mysql_dw_config是存储以上两张配置表的信息,内容如下:

[mail_config]
mail_host = 10.256.25.109  
mail_port = 45
mail_user = dw_bi
mail_passwd = 123456zxc
sender = dw_bi@iloveyou,cn

[mysql_dw_config]
host = 10.256.49.38
port = 3306
user = dw_user
passwd = 123iloveyou
db = dw_config
charset = utf8

  以上都准备好了后,才是真的万事具备了,开始准备python主函数的调用,具体如下

#-*-coding:utf-8-*-
import requests
import configparser
import json
import pymysql
from sqlalchemy import create_engine
import pandas as pd
from email.mime.text import MIMEText
import init_spark
from pyspark.sql import HiveContext,SparkSession
import os
import send_mail
import sys
import export_file
from ding_talk_warning_report_py.main import ding_talk_with_agency as ding

#获取mysql配置的登录连接信息,
def getcon():
    config = configparser.ConfigParser()
    file_path = os.path.dirname(__file__)
    ini_path = "%s/conf/myconfig.ini"%file_path
    config.read(ini_path)
    config_host = config['mysql_dw_config']["host"]
    config_port = int(config['mysql_dw_config']["port"])
    config_user = config['mysql_dw_config']["user"]
    config_passwd = config['mysql_dw_config']["passwd"]
    config_db = config['mysql_dw_config']["db"]
    config_charset = config['mysql_dw_config']["charset"]
    ## print(config_host+'\t'+config_port+'\t'+config_user+'\t'+config_passwd+'\t'+config_db+'\t'+config_charset)
    try:
        print(1)
        conn = pymysql.Connect(host=config_host, port=config_port, database=config_db, user=config_user,password=config_passwd, charset=config_charset)
        return conn
    except Exception as e:
        print(e)
        raise e  //异常外抛,系统爆错出来

def main(argv):
    event_day=argv[1] #指定时间日期,用于给生成的文件名加后缀
    myid= int(argv[2]) #指定id来选择配置表cfg_app_pushdown_file_to_mail的哪一条记录
    contends=argv[3] #传入的sql脚本,因为sql脚本可能带参数,所以这个sql是shell处理过的完全能在hive环境跑的通的脚本
   
       
    print("sql text:"+contends)

    dw_config=getcon() #通过getcon获取连接

    #将配置表cfg_app_pushdown_file_to_mail和邮件配置表组合cfg_email,得到所有的调用参数
    sqlcmd = """select
      cfm.id
     ,cfm.data_set_id
     , cfm.sequence_id
     , ifnull(cfm.sql_file,'') as sql_file
     , ifnull(cfm.header_list,'')  header_list
     , cfm.file_type
     , ifnull(cfm.excel_sheet,'') excel_sheet
     , ifnull(cfm.csv_seq,'') csv_seq
     , ifnull(cfm.file_path,'') file_path
     , ifnull(cfm.file_name,'') file_name
     , cfm.is_send_mail
     , ifnull(GROUP_CONCAT(DISTINCT cmt.email_name  SEPARATOR ','),'')  mail_to
     , ifnull(GROUP_CONCAT(DISTINCT cmcc.email_name  SEPARATOR ','),'')  mail_cc
     , ifnull(GROUP_CONCAT(DISTINCT cmbcc.email_name  SEPARATOR ','),'')  mail_bcc
     , ifnull(cfm.mail_subject,'') mail_subject
     , cfm.is_enable
     , cfm.create_time
     , cfm.create_user
     , cfm.update_time
     , cfm.update_user
from      cfg_app_pushdown_file_to_mail cfm
left join cfg_email cmt
       on cfm.mail_to_groupid=cmt.group_id
      and cmt.is_enable=1
left join cfg_email cmcc
       on cfm.mail_cc_groupid=cmcc.group_id
      and cmcc.is_enable=1
left join cfg_email cmbcc
       on cfm.mail_bcc_groupid=cmbcc.group_id
      and cmbcc.is_enable=1
where cfm.id=%d
group by cfm.id,cfm.data_set_id, cfm.sequence_id, cfm.sql_file, cfm.header_list , cfm.file_type, cfm.excel_sheet"""
    sql1 = sqlcmd % (myid)
    cfg_dataframe=pd.read_sql(sql1,dw_config) #将所有的参数放在dataframe里面
    ##print(cfg_dataframe)
    if cfg_dataframe.empty:
        print("no file to report")
    else:
        sql_file=cfg_dataframe['sql_file'].values[0]
        print("sql_file:"+sql_file)

        header_list=cfg_dataframe['header_list'].values[0]
        header_list=header_list.split(",")
        print("header_list:")
        print(header_list)

        file_type=cfg_dataframe['file_type'].values[0]
        print("file_type:"+file_type)

        excel_sheet=cfg_dataframe['excel_sheet'].values[0]
        print("excel_sheet:"+excel_sheet)

        csv_seq=cfg_dataframe['csv_seq'].values[0]
        print("csv_seq:"+csv_seq)

        file_path=cfg_dataframe['file_path'].values[0]
        print("file_path:"+file_path)

        file_name=cfg_dataframe['file_name'].values[0]
        print("file_name:"+file_name)

        is_send_mail=cfg_dataframe['is_send_mail'].values[0]
        print("is_send_mail:"+is_send_mail)

        mail_to=cfg_dataframe['mail_to'].values[0]
        print("mail_to:"+mail_to)

        mail_cc=cfg_dataframe['mail_cc'].values[0]
        print("mail_cc:"+mail_cc)

        mail_bcc=cfg_dataframe['mail_bcc'].values[0]
        print("mail_bcc:"+mail_bcc)

        mail_subject=cfg_dataframe['mail_subject'].values[0]
        print("mail_subject:"+mail_subject)
        

    
    #获取sql文件的内容
    #with open(sql_file) as file_object:
    #     contends = file_object.read()

    hive_sql = contends
    print("hive sql text:"+hive_sql)
    #生成一个list作为列名
    #header_list=['学号','姓名','性别','年龄'] #参数1
    #header_list=""
    if header_list=="":
       header_list=False 
    print("header_list:%s"%header_list)
    file_name = file_name+event_day #参数3
    #获取当前目录
    cur_path = os.path.dirname(__file__)

    #如果文件路径为空,则默认选当前文件夹,否则选指定的file_path
    if file_path=="":
        my_file_path = cur_path
    else:
        my_file_path=file_path
    
    #如果文件类型选excel
    if file_type=='excel': #参数5
        file_name=file_name+".xlsx" #文件名加xlsx后缀
        my_sheet=excel_sheet #参数4 #excel的sheet名
        #调用export_file.py中的export_excel函数,生成excel
        export_file.export_excel(hive_sql=hive_sql,excel_path=my_file_path,excel_name=file_name,sheet=my_sheet,header_list=header_list)
        print("the excel has create on %s"%file_name)
    
    #如果文件类型选csv
    if file_type=='csv':
       file_name=file_name+".csv"  #文件名加csv后缀
       my_sep_name=csv_seq #参数6 csv分隔符
       my_compression_name='infer' #选择不压缩
       # export_csv(hivesql,csv_path,csv_name,sep_name,compression_name='infer',header_list=[])
 
 #调用 export_file.export_csv来生成csv     export_file.export_csv(hivesql=hive_sql,csv_path=my_file_path,csv_name=file_name,sep_name=my_sep_name,header_list=header_list,compression_name=my_compression_name)
       print("the csv has create on %s" % file_name)
    
    #如果要发邮件附件
    if is_send_mail=="1":  #参数8
        print("send mail start!")
        mail_msg = '''
        <p>Dear all:</p>
        <p>&nbsp&nbsp附件中是'%s',请查收!</p>
        <p>&nbsp&nbsp此邮件为系统自动发送,请勿直接回复!如有任何疑问,可联系数仓相关人员:
        <p>&nbsp&nbsp数据仓库组  datawarehouse@iloveyou.cn;</p>
        '''%file_name

        #message = MIMEText(mail_msg, 'html', 'utf-8')
        #调用send_mail.py文件中的send_email函数,发邮件
        send_mail.send_email(mail_msg=mail_msg,mail_subject=mail_subject,mail_to=mail_to,mail_cc=mail_cc,mail_bcc=mail_bcc,is_attachment='Y',attch_path=my_file_path,attch_name=file_name)
        print("send mail successfully!")

if __name__ == "__main__":
    main(sys.argv)
    #main函数有三个参数
    #  argv[1] 指定时间日期,用于给生成的文件名加后缀
    # int(argv[2]) 指定id来选择配置表cfg_app_pushdown_file_to_mail的哪一条记录
    # argv[3] #传入的sql脚本,因为sql脚本可能带参数,所以这个sql是shell处理过的完全能在hive环境跑的通的脚本

每次例行调度的shell脚本call_job.sh

  主函数好了,但是不至于每天都让人去调用对吧,所以选择大家最喜欢的shell语言完成调度,这样就完成了自动化的部署,之前提到过,还有个重要的取数脚本要处理,而且取数脚本还及其可能是带动态化参数的,所以方便起见,我们把它放在当前的一个sql文件里面例子如下students.sql:

-- ['学号','姓名','性别','年龄']
select
   sno,
   sname,
   ssex,
   sage
from students
where ssex='m'
  and event_day=%s

自动化的shell语言脚本call_job.sh如下:

#!/usr/bin/env bash

mydate=$1  #指定的日期
myid=$2   #指定配置表cfg_app_pushdown_file_to_mail 的id
my_sql_file_argv=$3 #指定sql取数脚本的文件名

mydate=$(date -d"1 day ago ${mydate}" +%Y%m%d) #T-1的天数统计
event_week=$(date -d ${mydate}  +%V) #计算所属的周
event_week=$((10#$event_week))  #convert enentweek to Int type
event_hour=00
cur_dir=`pwd`

echo ${mydate}
echo ${event_week}
echo ${event_hour}

#解析文件内容和替换参数
my_sql_file=`cat ${my_sql_file_argv} | tr "\n" " " | tr "\r" " "`
my_sql_file=`printf "${my_sql_file}" "${mydate}"`

echo "mydate:${mydate}"
echo "myid:${myid}"
echo "my_sql_file:${my_sql_file}"


#source_path1 需要改动,检测原始数据是否存在
source_path1=/hive/warehouse/app/students/event_week=${event_week}/event_day=${mydate}/event_hour=${event_hour}/_SUCCESS

#判断依赖的Source_path路径下文件是否都存在
function testfile()
{
   local total_success_flag=0
   
   for((i=1;i<=1;i++)); #需要修改
   do
           mysource_path=`eval echo '$'"source_path$i"`
           hadoop fs -test -e $mysource_path
           hdfs_flag=$?
           ((total_success_flag+=hdfs_flag))
   done
   echo $total_success_flag
}

#统计依赖的source文件是否ready的个数
total_success_result=$(testfile)
echo "total not exists files: $total_success_result"
n=0
while (($total_success_result != 0))
do
  #如果文件不存在,睡一分钟
   sleep 1m
   ((n++))
  #尝试60次,即一小时,再没有结果就认为报错超时
   if [ $n -gt 60 ]; then 
       echo "等待1个小时,系统数据还没加载到hdfs"
       cd ${cur_dir}/../src/ding_talk_warning_report_py/main/
       python3 ding_talk_with_agency.py 99
       exit 7
   fi
   
   total_success_result=$(testfile)
   echo "total not exists files: $total_success_result"
done

#利用pyspark调用主函数app_pushdown_file_to_mail.py
spark-submit --master yarn --deploy-mode client --executor-memory 2G --executor-cores 1 --num-executors 4 --queue etl ./../src/app_pushdown_file_to_mail.py ${mydate} ${myid} "${my_sql_file}"


my_flag=$?    #检测上一句是否执行成功
echo "status:$my_flag"
if [ $my_flag -eq 0 ];then
   echo "send to monitor_report as a excel attach file has successed"
else #不成功则钉钉报警
  echo "send to monitor_report as a excel attach file has failed,deal with it hurry up!"
  cd ${cur_dir}/../src/ding_talk_warning_report_py/main/
  python3 ding_talk_with_agency.py 93
  exit 7
fi

小 结

  毕竟是第一次写etl小工具,写的还是有点粗糙的,还有很大的改善空间吧,但是感觉用起来还蛮舒服的,根据自己的要求随心所欲,同时也进行的配置表进阶,让以后这些Job都能在配置表上统一化管理,略能得瑟一下吧,从此之后什么什么ETL工具?渣渣!(手动滑稽),尽情期待Hadoop集群数据分发——pyspark导出Hive集群数据及python写入mysql或sql server

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

╭⌒若隐_RowYet——大数据

谢谢小哥哥,小姐姐的巨款

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值