python跑批模拟、深度遍历、echarts时序图

记一次跑批优化

目录

  1. 工作背景;
  2. 报表层表依赖关系梳理;
  3. 调度系统跑批模拟器;
  4. 理论上的最短耗时;
  5. echarts输出时序图;

1. 工作背景

因今年的项目需将原有报表工具上的一些报表迁移至新的报表平台,因此涉及到这些报表的取数脚本梳理。厂商在梳理过程中发现一张报表取数时,会涉及到多个中间表,且中间表也是经过层层加工出来,梳理工作进展缓慢。
因此考虑在厂商继续梳理的同时,我这边也研究下如何快速提取表之间的依赖关系。进而在与负责调度系统的同事沟通过程中,发现一些无效的作业依赖关系,并因此想到能否通过删除无效依赖关系,调整作业编号,从而缩短报表跑批耗时。随之便有了接下来的表依赖关系提取、调度跑批模拟器、深度优先遍历、echarts绘制调度时序图等工作。

2. 报表层表依赖关系梳理

基本概念

  1. 跑批调度系统里有多个跑批任务,本次仅针对报表层任务;
  2. 每个任务里包含多个作业;
  3. 每个作业有一个作业编号(命名为“作业类型_编号”,形如XXXX_0012);
  4. 作业的内容就是通过sh去调用一段perl脚本,脚本里包含sql语句,去执行数据跑批。

表间依赖关系整理

需梳理的目标脚本,为报表层脚本文件夹里的约560个perl脚本。perl文件的文件名即为目标表名,perl文件里的sql语句中的以IALDB、IBLDB、ICLDB、IDLDB、IELDB这五种库实例名开头的表,即为目标表依赖的表名。
存在问题:有些表没有写库实例名称,或者是库实例名称后边有空格的,这部分数量不多,后期手工梳理。 最终输出表间依赖关系2243条。

调度系统作业依赖关系导出

调度系统里的作业信息,以及作业依赖关系直接从数据库里导出。导出的数据进过汇总排序,按被依赖的数量做降序,Top10的截图如下:
在这里插入图片描述
其中XXXX_0145号作业被依赖达64条。

无效依赖关系筛查

作业的依赖关系,比如A作业依赖于B作业,即为A的perl脚本依赖于B的perl脚本,即为A表需要用到B表的数据。但根据之前梳理出的表间关系做初筛,并结合以下ssh语句做复核,发现有92条作业依赖关系是无效的。以XXXX_0145为例,实际就1个作业依赖于0145,而非64个。

# ssh下查找包含某表名的pl文件
find 某路径下 -name "*.pl" | xargs grep -i "某tablename "

剔除了无效依赖关系后,按被依赖的数量做降序,Top10的截图如下:
在这里插入图片描述

依赖关系可视化呈现

删除无效依赖,可降低跑批任务的维护难度,在neo4j上做关系图可视化如下: 删除无效依赖前:262条关系
在这里插入图片描述
删除无效依赖后:170条关系,干净了不少。
在这里插入图片描述

3. 调度系统跑批模拟器

由于剔除了许多条依赖关系,不知该任务的跑批耗时是否有缩短,又不好在生产环境上进行验证,于是考虑在本地搞一个跑批的模拟器。
经与负责跑批调度系统的同事了解,调度系统的跑批规则如下:

  1. 20个线程同时跑;
  2. 按照作业编号order by的顺序取作业;
  3. 若取到的作业有依赖于其他作业,且其他作业尚未完成,则先跳过。

用python实现,设计如下:
在这里插入图片描述
代码如下:

# 数仓调度跑批模拟器

# %% import、文件路径
from concurrent.futures import ThreadPoolExecutor
import threading
import time
import datetime
import pandas as pd
import queue
import os
import webbrowser
from mylog import logc, dataf

jobInfo_filepath = u'./jobSrc/jobInfo_new.csv'
depInfo_filepath = u'./jobSrc/depInfo_new.csv'

dicjob = {
   }
executor = None
# 时间比例,缩小timescale倍
timescale = 100.0

dataf_template = "{
   {value:[{0},{1},{2},{3},types[{4}].name],itemStyle:{
   {normal:{
   {color:types[{5}].color}}}}}},"
# 显示x-range图开关
IS_OPEN_ECHARTS = True

# %%
###############
# timescale=100
###############
# 调试数据:20190908的调度数据---92min
# 原始作业编号,删除无效依赖,用时:
# 2019-09-24 17:21:07,292 - console[line:243] - INFO: used 65.566222s
# 修改24个作业编号后,用时:
# 2019-09-24 18:24:18,931 - console[line:251] - INFO: used 47.367517s

# 调试数据:20190913的调度数据---98m12s
# 原始作业编号,删除无效依赖,用时:
# 2019-09-24 17:23:12,103 - console[line:243] - INFO: used 68.279253s
# 修改24个作业编号后,用时:
# 2019-09-24 18:26:02,326 - console[line:253] - INFO: used 54.73252s

# 调试数据:20190922的调度数据---101m45s
# 原始作业编号,删除无效依赖,用时:
# 2019-09-24 17:24:56,260 - console[line:243] - INFO: used 68.034214s、
# 修改24个作业编号后,用时:
# 2019-09-24 18:27:53,594 - console[line:254] - INFO: used 54.080691s
# 2019-09-24 18:29:38,886 - console[line:255] - INFO: used 54.150868s


###############
# timescale=10
###############
# 调试数据:20190908的调度数据---92min
# 原始作业编号,删除无效依赖,用时:
# 2019-09-24 17:39:05,565 - console[line:235] - INFO: used 654.113571s
# 修改24个作业编号后,用时:


# 调试数据:20190913的调度数据---98m12s
# 原始作业编号,删除无效依赖,用时:
# 2019-09-24 17:51:41,276 - console[line:244] - INFO: used 685.985023s
# 修改24个作业编号后,用时:

# 调试数据:20190922的调度数据---101m45s
# 原始作业编号,删除无效依赖,用时:
# 2019-09-24 18:18:14,520 - console[line:246] - INFO: used 678.700894s
# 修改24个作业编号后,用时:


# %% 定义作业类


class BatJob(object):

    def __init__(self, name, time, typeindex):
        # 作业名称
        self.__name = name
        # 作业耗时
        self.__time = time
        # 作业依赖(list)
        self.__deps = []
        # 作业状态 S_WAIT,S_RUN,S_FINISH
        self.__status = 'S_WAIT'
        # 作业类别
        # { name: '无父类有子类(起始节点)', color: '#75d874', typeindex:0 },
        # { name: '无父类无子类(单节点)', color: '#7b9ce1', typeindex:1 },
        # { name: '有父类无子类(末端节点)', color: '#000000', typeindex:2 },
        # { name: '有父类有子类(中间节点)', color: '#bd6d6c', typeindex:3 }
        self.__typeindex = typeindex

    def addDep(self, dep):
        self.__deps.append(dep)

    def goPut(self):
        self.__status = 'S_PUT'

    def goRun(self):
        self.__status = 'S_RUN'

    # 作业完成时,修改状态
    def goDone(self):
        self.__status = 'S_FINISH'

    def __str__(self):
        return "\n".join(item for item in (
            '【name】' + self.__name,
            '【time】' + str(self.__time),
            '【status】' + str(self.__status),
            '【deps】' + str(self.__deps),
            '【typeindex】' + str(self.__typeindex)
        ))

    def getDeps(self):
        return self.__deps

    def getStatus(self):
        return self.__status

    def getTime(self):
        return self.__time/timescale

    def getTypeindex(self):
        return self.__typeindex


# %% 导入作业信息、依赖信息
def loadInfo():
    jobInfo = pd.read_csv(jobInfo_filepath)
    depInfo = pd.read_csv(depInfo_filepath)

    dic = {
   }
    jobnameList = []
    for i in range(len(jobInfo)):
        jobname = jobInfo.jobname[i]
        jobtime = 1 if jobInfo.jobtime_s[i] == 0 else jobInfo.jobtime_s[i]
        typeindex = jobInfo.typeindex[i]
        dic[jobname] = BatJob
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

马克布克打字机

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值