shell脚本实践 :百万数据文件数据使用shell脚本高效入数据库
Posted 踩踩踩从踩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell脚本实践 :百万数据文件数据使用shell脚本高效入数据库相关的知识,希望对你有一定的参考价值。
前言
该脚本直接采用ftp登录下载文件到本地;读取数据放到存储成临时文件,
通过load data local infile 批量导入临时文件到本地文件到mysql库中;
优点是在于不占用我们服务器的jvm内存,效率高,提高服务器的吞吐率等等;并且方便我们线上维护
只需要起定时任务去执行即可,并且可以交给在分布式任务管理器进行管理,例如xxljob等
前提
- 脚本所在linux 需要安装mysql客户端
- 文件服务器需要支持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
将检查用户<A1><AF>主目录中的.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:password@127.0.0.0 回车
(2)lftp username@127.0.0.0 回车 #默认端口为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 实践时的问题
错误解决
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脚本,实现简单,而且效率高,这是一种很好的实现方案
以上是关于shell脚本实践 :百万数据文件数据使用shell脚本高效入数据库的主要内容,如果未能解决你的问题,请参考以下文章