当前位置:首页 > 统计知识

大数据调度脚本中的常见问题

作者:admin     时间:2020-08-10 08:15:12     来源:互联网    
# 场景说明

1. 针对大数据开发初学者,给初学者看的;学习收获:掌握DML语句开发出来之后,如何写调度程序

2. 知识储备要求:有一定的 sql 基础,尤其是 query 语句,了解 shell 脚本

3. 本文讲解面向平台是大数据计算平台,例如,hive、presto 等

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

- 日期处理

- 路径问题

- 变量替换

- 如何记录任务的耗时

- 后台跑数据问题

## 日期处理 ### 日期处理的场景

日期处理是关于如何获取日期以及格式化日期。举个最常见的例子,销售类报表是领导一定都要看的。假如每天十点之前要给领导递交昨天的销售数据假设和领导约定要在每天上午的十点。

之前,昨天的销售数据要让领导看到。这里有一个关键字——“昨天”,这个“昨天”圈定了销售行为发生在昨天。当天的日期为 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 工具的命令行。回到“给领导发销售报表”的需求,我们使用 date 命令下面给出如何使用命令行取出日期,然后将日期给 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

## 变量替换

在上面的内容里面,我们提到了变量的传递。我们使用的是 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 命令。类似下面

```shell

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

```

## 手动后台跑数据问题

做数据工作久了,我们就会知道,我们经常会接到一些临时性的取数需求。这种需要我们通常只会跑一次,这样就需要我们手动跑一下数据了。有的同学说了,在命令行里面输入:

Hive –hivevar start_date=xxx –hivevar end_date=xxx –f hive.sql

这样是可以的。但是,如果这个任务要跑比较长的时间(一个小时),那么我们只能在终端上看到输出一连串的日志信息,而不能在这个窗口干其他事情了。最糟糕的是客户端与服务器的网络断开了,我们正在跑着的任务也就停掉了。

如果我们要同时跑多个任务。我们怎末做呢?其实很简单。在 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

```

希望对大家有用。

# 总结

1. 日期问题。日期参数是任务的最基本的参数,自动跑批脚本中一定会日期的处理逻辑。我们讲了两种方式:date 命令和维表方式。

2. 路径问题,路径问题也是调度程序要处理的基本问题。这里讲解了,dirname + pwd + 字符串处理的方法来获得我们想要的路径。

3. 变量替换。我们主要使用的 mktemp + sed 命令的方式来实现的。

4. 记录任务耗时,这里我们主要使用的是 time 命令来实现。

5. 手动跑数据。在这一小节里面,我们讲了 & + wait 来跑临时取数需求的任务。 lsof + kill 来管理后台跑的后台任务。

希望对大家有用。

End.

爱统计网专栏作者:wang-possible