shell脚本实践 (二):百万数据文件数据使用shell脚本高效入数据库

前言
该脚本直接采用ftp登录下载文件到本地;读取数据放到存储成临时文件,
通过load data local infile 批量导入临时文件到本地文件到mysql库中;
优点是在于不占用我们服务器的jvm内存,效率高,提高服务器的吞吐率等等;并且方便我们线上维护
只需要起定时任务去执行即可,并且可以交给在分布式任务管理器进行管理,例如xxljob等

前提

  1. 脚本所在linux 需要安装mysql客户端
  2. 文件服务器需要支持ftp下载文件

功能脚本实现

#!/bin/bash

#ftp下载文件到本地并直接入mysql库

#-----------------功能配置 --------------------------
# ftp连接配置
ftp_host="192.168.1.102"
ftp_port=21
ftp_user"admin"
ftp_pwd="123456"

# mysql配置
mysql_host="192.168.1.102"
mysql_port=3306
mysql_user="root"
mysql_pwd=""
mysql_db="test"


#FTP文件路径配置
ftp_file_path=/tmp/ftp/flie

#结果文件的文件夹路径
local_file_path=/tmp/ftp/flie

#本地和ftp的user文件夹名称
user=user


#-------------------------------------------
# MYSQL中导入
#在mysql中执行SHOW VARIABLES LIKE '%local%'; 如为OFF则设置下面参数
#SET GLOBAL local_infile=1
#
#解决mysql数据导入导出提示secure-file-priv option问题
#SHOW VARIABLES LIKE "secure_file_priv";
#找到/etc/my.cnf 在mysqlid下面添加secure_file_priv=''
#-------------------------------------------
#功能实现

echo 'the drop_down statistics'

#创建时间
gmt_create=$(date +"%Y-%m-%d %H:%M:%S")

#1、新建结果文件文件夹,覆盖之前的文件
mkdir -p ${local_file_path}
rm -rf ${local_file_path}/${user}
mkdir -p  ${local_file_path}/${user}

# 解析数据到数据库中
# 创建连接变量
mysql_conn="mysql -N --default-character-set=utf8 -h${mysql_host} -P${mysql_port} -u${mysql_user} -p${mysql_pwd} ${mysql_db}"

echo " "
echo "--------------------------- "
echo " "
#---------------数据入库-------------------------------------
echo "statistics data enter mysql start "

echo "download from ftp file start"

ftp -n	<< EOF   
	 open ${ftp_host} ${ftp_port}
	 user ${ftp_user} ${ftp_pwd}
	 lcd ${local_file_path}/${user}
	 cd  ${local_file_path}/${user}
	 binary
	 prompt
	 mget user_*.avh 
	bye
EOF
                                      #EOF只是一个分界符标志 也可以使用EOM,!等
                                                                           
echo "download from  file successfully"

echo "url file enter mysql"

#判断目录文件是否下载成功 在入库
if [ "$(ls -A ${local_file_path}/${user}/)" ]; then

	#清理原有数据数据
	$mysql_conn -e "delete from t_user "
	
	#遍历文件入库
	for file in ${local_file_path}/${user}/user_*.avh
	do
		#创建url txt文件
		tmpfile=${file##*/}
		tmpfilename=${tmpfile%.*}
		touch ${local_file_path}/${user}/${tmpfilename}.txt
		chmod 711 ${local_file_path}/${user}/${tmpfilename}.txt
	    while read name pword age des
		do
			echo "$name	$pword	$age	$des	$gmt_create	$gmt_create"  >> ${local_file_path}/${user}/${tmpfilename}.txt		
		done < ${file}
		
		echo "${local_file_path}/${user}/${tmpfilename}.txt >> t_user"
		$mysql_conn --local-infile=1 -e "LOAD DATA LOCAL INFILE '${local_file_path}/${user}/${tmpfilename}.txt'  INTO TABLE t_user	character set 'utf8' (id,name,pwd,age,des,gmt_create,gmt_modified)"
	done    
else
    echo "file is Empty"
fi


echo "statistics data enter mysql end"

数据库使用mycat中间键

从ftp下载成功入mysql入库的一个脚本就这样实现的,如果使用了mycat中间键,该脚本也适用,但注意分片的问题。我这里是按id进行分片的,也需要注意max_allowed_packet问题

登录ftp下载文件

#ftp的参数的各个含义
 -v  Verbose选项强制ftp显示来自远程服务器的所有响应,并报告数据传输-
fer统计。
-n  限制ftp在初始连接时尝试自动登录。如果自动登录i
已启用,ftp
将检查用户主目录中的.netrc(见下文)文件中描述帐户的条目
在远程机器上。如果不存在条目,ftp将提示输入远程计算机登录名(默认值)
是本地计算机上的用户标识),并在必要时提示输入密码和帐户
要登录的。
-i 在多个文件传输期间关闭交互式提示。

ftp -n	<< EOF   
	 open ${ftp_host} ${ftp_port}
	 user ${ftp_user} ${ftp_pwd}
	 lcd ${local_file_path}/${user}
	 cd  ${local_file_path}/${user}
	 binary   # 下载二进制的文件
	 prompt   # 取消ftp交互
	 mget user_*.avh    #获取多个文件 
	bye
EOF

lfp登录下载文件
使用方式
(1)lftp username:[email protected] 回车
(2)lftp [email protected] 回车 #默认端口为21 ,回车后输入密码
(3)lftp 127.0.0.0 回车 ##回车后 login <用户|URL> [<密码>] 登录
(4)lftp 回车 -->open 127.0.0.0–>login 登录
Lftp是一个基于命令行的文件传输软件(也被称为FTP客户端),由Alexander Lukyanov开发并以GNU GPL协议许可发行。除了FTP协议外,它还支持FTPS,HTTP,HTTPS,HFTP,FISH,以及SFTP等协议。这个程序还支持FXP,允许数据绕过客户端直接在两个FTP服务器之间传输。

读取本地文件到临时文件,并入库

#遍历文件入库
	for file in ${local_file_path}/${user}/user_*.avh
	do
		#创建url txt文件
		tmpfile=${file##*/}
		tmpfilename=${tmpfile%.*}
		touch ${local_file_path}/${user}/${tmpfilename}.txt
		chmod 711 ${local_file_path}/${user}/${tmpfilename}.txt
	    while read name pword age des
		do
			echo "$name	$pword	$age	$des	$gmt_create	$gmt_create"  >> ${local_file_path}/${user}/${tmpfilename}.txt		
		done < ${file}
		
		echo "${local_file_path}/${user}/${tmpfilename}.txt >> t_user"
		$mysql_conn --local-infile=1 -e "LOAD DATA LOCAL INFILE '${local_file_path}/${user}/${tmpfilename}.txt'  INTO TABLE t_user	character set 'utf8' (name,pwd,age,des,gmt_create,gmt_modified)"
	done    

采用LOAD DATA LOCAL INFILE 进行文件入库,大批量插入数据,针对大数据量的情况,速度快。

数据量比较小,你可以采用循环插入的方式

$mysql_conn  -e "insert into t_user(name,pwd,age,des,gmt_create,gmt_modified) values($name,$pwd,$age,$des,$gmt_create,$gmt_modified)"

拼装批量插入

需要将文件中的数据读取出来 采用 mapfile 命令 读取每行,并拆分出来 批量插入

LOAD DATA LOCAL INFILE优点

MySQL使用LOAD DATA LOCAL INFILE从文件中导入数据比insert语句要快,MySQL文档上说要快20倍左右。 而自己测试了下,确实感觉在两百万更往上数据20倍完全更多

LOAD DATA LOCAL INFILE 实践时的问题

shell脚本实践 (二):百万数据文件数据使用shell脚本高效入数据库_第1张图片
错误解决
mysql的设置参数中max_allowed_packet过会导致上面的错误
可以查看max_allowed_packet;
show variables like ‘max_allowed_packet’;

永久性解决方案:
修改方法1(配置文件持久化修改):
vim /etc/my.cnf
[mysqld]
max_allowed_packet = 100M

注意:修改配置文件以后,需要重启mysql服务才能生效。

下载的文件量太大解决方案,在shell脚本中进行分文件在入库

#USER临时文件拆分数据条数
SPLIT_USER_DATA_NUMBER=100000
#遍历用户数据入库
	for file in ${local_file_path}/${user}/user_*.avh
	do
		#创建word txt文件
		num=0
		filenamenum=0
		tmpfile=${file##*/}
		tmpfilename=${tmpfile%.*}
		touch ${local_file_path}/${user}/tmp/${tmpfilename}.txt
		chmod 711 ${local_file_path}/${user}/tmp/${tmpfilename}.txt
		while read name pword age des
		do
			if [ ! $des] || [ -z $des]; then
 				 echo	"des is empty"
			else
				if [ $num -gt $SPLIT_USER_DATA_NUMBER ]; then
					num=0
					filenamenum=$((${filenamenum} + 1))
					tmpfilename=tmpfilename_${filenamenum}
				touch ${local_file_path}/${user}/tmp/${tmpfilename}.txt
		      chmod 711 ${local_file_path}/${user}/tmp/${tmpfilename}.txt
			    else	
					num=$((${num} + 1))
		        fi		
				echo "$name	$pword	$age	$des	$gmt_create	$gmt_create"  >> ${local_file_path}/${user}/tmp/${tmpfilename}.txt			
			fi		
		done < ${file}
		
	   for tmpfile in ${local_file_path}/${user}//tmp/*.txt
	     do
		  echo "${tmpfile} >> t_user"
		  $mysql_conn --local-infile=1 -e "LOAD DATA LOCAL INFILE '${tmpfile}'  INTO TABLE t_user character set 'utf8' (name,pwd,age,des,gmt_create,gmt_modified)"
		  
		  rm -rf ${tmpfile}
		done
	done

总结

上面的功能如果使用java代码来写,比较繁琐,如果不做太多的操作,可以考虑shell脚本,实现简单,而且效率高,这是一种很好的实现方案

java实现load data local infile导入大批量数据

你可能感兴趣的:(shell脚本调用,linux,mysql)