今天下午调试了一个Shell脚本,简直是刷新了自己的认知,总体来说,这是一种难得的学习状态:当你精疲力竭找不到出口时,会去尝试各种可能,甚至是不可能的方法,而一旦找准了方向,找到了问题的症结,竟然发现是那些简单的可以笑掉大牙的小问题,不过问题解决之后那种收获还是很有意思的,无论如何,这个过程都值得自己总结,避免后续犯更lower的小错误。
脚本的内容是一个数据流转相关的需求,背景是一个分布式环境,数据是按照天为单位存储的,需要把这些数据转储做统计分析,源端为MySQL的分布式集群,目标端是Greenplum.
为了减少彼此的依赖,我们经过讨论是使用了csv文件的方式进行数据同步,目前的粒度为T+1,即今天统计的是昨天的数据,整个流程涉及几个环节:
1.源端MySQL导出csv文件
2.csv文件流转到ETL服务器
3.Greenplum端加载csv文件
4.完成统计分析
目前MySQL分布式集群是16个分片,4个物理节点,需要把这16个分片的数据导出。
从逻辑层面来看,是16个分片,从服务器维度来看,是4台服务器,而对于统计分析来说,是1个数据源,所以粒度可大可小,处理方式也是截然不同。
我们把导出和流转的过程再细化一下:
1)如果当前目录的CSV文件已存在,则生成错误记录,跳出导出过程
2)如果当前目录的CSV文件不存在,则导出CSV文件
3)查看导出日志,是否存在错误,如果存在则终止数据流转至ETL服务器
4)如果没有错误,则使用scp或者rsync的方式同步文件至ETL服务器
5)移动当前的导出文件至归档目录
结果碰到了魔性的一些场景,我手工执行脚本,整个流程很正常,但是在crontab中执行了一部分,也没有任何报错。
我把crontab里面的命令复制出来,手工执行,依然可以正常执行,但是在crontab中执行了一部分,没有任何报错。
所以这几个现象让我感到很郁闷,看起来是多么简单的一个需求,竟然这么纠结。
我试了几种方式来排查这个问题,首先第1,2步通过输出日志验证是没有问题的。
所以我们的焦点就集中在了第3点和第4点。
如果文件导出存在错误,就不做文件传输,在脚本中加了一些明细日志,可以很肯定的说,这个逻辑也没有问题。
那问题的重点就自然到了第4点,如果导出正常,则传输文件至ETL服务器。
这个脚本的部分内容类似下面的形式:
file_name_prefix=${tab_name}_${date_tag}_${id}
scp -P 20022 ${file_name_prefix}_xxxx.csv mysql@xxxx:/data/grw_data
文件的格式类似于 test_data_20190805_10_xxxx.csv,需要通过scp把文件传输到ETL服务器。
在scp的部分我做了不下20次调试,最后肯定就是这个scp的命令部分存在问题,让我纠结的,还不知道是什么原因导致的,我们做了如下的排除工作。
排除了scp的用户的一些配置差异
排除了日志的输出格式的处理差异
排除了scp命令在循环中的语法差异
排除了scp命令的限制,改用rsync的方式问题依然存在
排除了crontab任务属主的潜在配置差异
。。。。
最后发现scp -P 20022 ${file_name_prefix}_xxxx.csv mysql@xxxx:/data/grw_data这个命令里面的文件是没有路径的,也就意味着这是相对路径,我改为绝对路径之后整个问题就很顺畅了。
这个问题让我调试的有些心力憔悴,明白了缘由,一鼓作气把脚本的其他逻辑都补充好了。
至此我对scp命令不会存在阴影了,也不会怀疑人生了,而反推这个过程,我依旧不能肯定自己能够快速定位到这个问题,所做的就是遵守一些基本的规范。