任务调度中常用 shell 命令

场景说明

  1. 给初学者看的。目的是让他们知道再 DML 语句开发出来之后,如何写调度程序。
  2. 面向的人群:有一定的 sql 基础,尤其是 query 语句。
  3. 我们面向的平台是大数据平台,例如,hive
  4. 了解 shell 脚本

单个任务下,常见的几个问题

  • 日期处理
  • 路径问题
  • 变量替换
  • 如何记录任务的耗时
  • 后台跑数据问题

日期处理

日期处理的场景

日期处理是关于如何获取日期以及格式化日期。举个最常见的例子,销售类报表是领导都要看的。假设和领导约定要在每天上午的十点
之前,昨天的销售数据要让领导看到。这里有一个关键字——“昨天”,这个“昨天”圈定了销售行为发生在昨天。当天的日期为 T,
那么昨天的日期就是 T+1(也有叫 T-1)的,这个 T 是变量,每天都会变。下面的这段 query 语句,假设就是没有要执行的 query 语句。


select shop_name as '商店名称' , sale_amt as '商店销售额'
  from dm_sale_daily
 where sale_date = '2020-01-28'

回到原来最初的问题,我们如何完成给领导发销售数据的需求呢?最简单的,我们可以每天 6 点起床,
然后打开电脑,修改日期条件后,执行 query 获得数据再发给领导(真的是有这样干的)。
本人不太赞同这种“人工智能”的做法。如果我们用一种办法能自动的替换掉日期条件,还能自动执行
query ,那不就完美了。

hive 命令行工具就提高这样的功能。使用方法如下:

hive --hivevar var_name=var_value -f xxxx.sql
例如:

hive --hivevar sale_date='2020-01-28' -f xxxx.sql

有的同学就会说,这个参数还是常量啊,莫急啊,最终的形态是这样的:

#!/bin/bash -x

sale_date=`date -d "-1 day" +%Y-%m%-d`

hive --hivevar sale_date='${sale_date}' -f xxxx.sql

xxxx.sql 内的内容是这样的:

select shop_name as '商店名称' , sale_amt as '商店销售额'
  from dm_sale_daily
 where sale_date = '${sale_date}'

$sale_date 是 shell 中调用变量的符号。
${sale_date} 这个是占位符,hive 命令行工具会自动将 ${sale_date} 变量替换成 2020-01-28。
如果我使用的某种方式将日期值赋值给 变量sale_date ,就完美了。我既然这样说,我一定是有办法的。

日期处理方法

接下来讲讲,我们如何使用 shell 脚本来做获得各种日期以及日期的格式化。

获取日期的方式大体有两种:

  • 使用 shell 中 的 date 命令来获取 ,
  • 使用数仓的日期维表来获得

先来看看常见的日期:

  • 昨日日期 -> date -d “-1 day” +%Y-%m-%d
  • 上周同天 -> date -d “-7 day” +%Y-%m-%d
  • 本周第一天日期
    week_day=`date -d "${sale_date}" +%w `
    if [ ${week_day} -eq 0 ]; then
       week_day=7
    fi
    ((week_day--))
    monday=`date -d"${sale_date} -$week_day day" +%Y-%m-%d `
    
  • 本周最后一天日期
    week_day=`date -d "${sale_date}" +%w `
    if [ ${week_day} -eq 0 ]; then
       week_day=7
    fi
    ((week_day=7-week_day))
    sunday=`date -d"${sale_date} +$week_day day" +%Y-%m-%d
    
  • 本月第一天日期 -> date -d "${sale_date} " +%Y-%m-01
  • 本月最后一天日期
    next_month_first_day=`date -d "${sale_date} +1 month" +%Y-%m-%d`
    this_month_first_day=`date -d "${next_month_first_day} -1 day" +%Y-%m-%d`
    
  • 上月同天日期
    last_month_same_day=`date -d "${sale_date} -1 month" +%Y-%m-%d`
    

简单得说一下 date 的用法: date -d ‘日期参数 [-+] {数字} 单位’ “格式化字符串”
假设,我们需要 2020-01-01 往前第七天的日期,date -d ‘2020-01-01 -7 day’ ‘+%Y-%m-%d’
从上面的命令来看,“日期参数”只要是日期就行,而且 2020-01-01、20200101、2020/01/01 这样的格式都是可以的。
+- 指示了日期的加减,+7 day 是从“日期”参数开始加七天。

“格式化字符串”用来设置字符串的格式。我们使用的格式有:

  • +%Y-%m-%d -> YYYY-mm-dd ,例如,2020-01-01
  • +%Y%m%d -> YYYYmmdd ,例如,2020-01-01

上面这两种格式就够用了,如何还有需要可以在 Linux 中输入 man date 来查看 date 格式化详细信息。

接下来我们讲讲,在数仓中的日期维表,日期维表是数据库或者 hive 一个表。此表以阳历日期为主键
其他字段包含此主键对应的节假日、阳历日期、周第一天、周最后一天、月第一天、月最后一天等等我们能
想到的各种日期信息。通常情况下,我们是提前将日期维表计算出来的。然后从表中查询出来,再将日期参数
传递给 hive 工具的命令行。下面给出如何使用命令行取出日期

-- hive
dates=`hive -e "select all kinds of day from dim_date where date_id = '20200101'"`
-- mysql
dates=`mysql -u user_name -p pwd -h server_ip -e "select all kinds of day from dim_date where date_id = '20200101'"`

总之都是使用 shell 中的命令嵌套。

路径问题

一般的情况下,我们会有一下常用的配置信息放到一个文件中。例如,报表数据会放到 mysql、Hbase 中,而且都是同一个 mysql
实例或者 Hbase 集群。其中链接信息(包括服务器ip地址/账号/密码)都是一致,这时候我们希望将这些信息放到变量中,
然后加载一次直接使用。我们可以的将这些链接信息放到根目录下的文件中。下面是一个项目的项目目录结构:

  • report_project
    • .env(文件)
    • sale_report(文件夹)
      • run_report.sh
    • budget_report(文件夹)
    • warehouse_report(文件夹)

我们要在 run_report.sh 中获取 .env 中的内容。

#!/bin/bash -x
declare -r current_dir=$(cd `dirname $0` ; pwd`)

project_path=${current_dir%%report_project*}/report_project

-- 将 .env 里面的变量引入到当前环境中。
source $project_path/.env

declare -r current_dir=$(cd dirname $0 ; pwd`) 这个命令获取 run_report.sh 所在路径。其中 `` $()
两个是嵌套符号,dirname 命令是获取文档所在的相对路径,$0 是系统变量,它的值是当前 shell 文件(也就是 run_project.sh)
的名字。cd 进入文档所在路径,然后使用 pwd 获取文档所在路径。declare -r 是定义不可变变量。
另外,我们配置文件最后是同意的放到一个文档中,这样好统一管理。

如果有所有的项目都适用的变量,我们可以放到 home 目录下面,然后需要的时候,在文件里面 source ~/.bash_profile

如何我们要封装一个命令,配置在 bash_profile 里面,可以用下面的命令:
CURDIR=$(cd $(dirname ${BASH_SOURCE[0]}); pwd )

变量替换

在上面的内容里面,我们提到了变量的传递。我们使用的是 hive 提供给我们的功能。如果命令行没有没有提供给我们类似的功能,
例如 presto 计算平台。我们就需要自己来处理了。我这里给出的方案如下:

# 使用 mktemp -t 新建的一个临时文档,其中t选项指定在系统默认的临时文件的路径新建文件,一般是 /tmp/
# XXXX 是随机的变量,mktemp 命令会使用随机字符传替换
tmpfile=`mktemp -t sql_XXXX.sql`
cp sql_file.sql $tmpfile

sed -i "s/\${start}/${start_date}/g ; s/\${end}/${end}/g" $tmpfile
presto -f $tmpfile

如何记录任务的耗时

在 shell 中有必要记录每个任务执行耗时,这样可以找到慢任务来优化任务。
我们可以使用 shell 的 time 命令。类似下面


/usr/bin/time -f 'duration: %E' command

后台跑数据问题

在 shell 中,只要将 & 加快命令的最后面,我们就可以将任务放到后台执行了。例如:

hive -f xx.sql &

如果有多个任务可以并行跑,也可以使用 & 来处理。如下所示:

hive -f a.sql &
hive -f b.sql &

如果 c.sql 需要 a.sql b.sql 两个跑完后才能跑。我们可以写成下面:

hive -f a.sql &
hive -f b.sql &

wait

hive -f c.sql


wait 是等待 a.sql b.sql 执行完以后,c.sql 才能执行。

在后台跑有个问题,a.sql b.sql 两个任务的任务都会打印到命令行上,这样以来就没有办法分辨出哪个写日志是 a.sql 的,
哪些是 b.sql 的。另外,日志也不能保存。为了解决下面的问题,我们可以写成下面:

hive -f a.sql &> a.log &
hive -f b.sql &> b.log &

wait

hive -f c.sql &> c.log

这里涉及到了 shell 中的重定向知识点。https://zhuanlan.zhihu.com/p/47765176 可以到这上面找到自行学习即可

&> 是简写,也可以简写成 >&。

hive -f a.sql &> a.log & 等价于 hive -f a.sql > a.log 2>&1

这样我们就把任务放到后台跑了。但是还有一个问题,如果我们要是退出登陆,那么我们在后台跑着的任务都会停掉的。
这时候,我们就需要 nohup 命令来帮忙了,nohup 命令可以让后台进程不受退出登陆的影响。

所以一个完整的 hive 任务调用命令为:

nohup hive -f a.sql &> a.log &

最后如何查看后台任务的跑完没有,可以使用 lsof 这个命令。lsof 这个命令可以查看占用文件的进程。

lsof a.log

如果 lsof 返回的是空白行,则 a.log 没有进程占用了,也就是我们的任务跑完了。

接下来,如果我们发现任务中有个小错误,我们需要停下当前的任务,我们可以使用 kill -9 将进程杀掉即可。
我这里给出一个的批量删除的命令。

lsof a.log | awk '{print $2}' | xargs kill -9

最后给大家贡献一个批量查看多个任务是否执行完的命令组合:

find 任务的输出日志所在的路径 -name '日志的名字' | xargs lsof | awk '{if(NR>1){print $9}}' | uniq -c

希望对大家有用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值