mysql的全量导出_如何在下班前全量导出mysql的10亿数据到U盘?

#前情提要

上头的上头一句话,把这一堆日表从今年1月份开始的所有数据导出来,方便审计人员核对。然后把所有数据整合起来,每100w条导出成一个单独的文件,行内容按照csv的文本格式组织。

简简单单几句话,其实里面的坑多的是:

#实现难点

*数据量大:*因为业务的特殊性,这一堆表的数据是可以预先生成的,虽然现在才3月份,但是最新的一张表已经去到了2022年,总计有2000+张日表。其次因为是消费流水数据,所以量大的惊人,通过一个脚本统计出的总数达到了10亿+。

*格式要求:*虽然我也认为这么多的数据是不可能真正一条一条打开来看的,但是领导既然要求导出成csv兼容的文本格式。那如何把mysql里的数据格式化也成了一个难点。

*条件有限:*因为前期根本没有任何工具积累,所有的信息就只告诉你哪些机器可以访问的哪个数据库,其余的一切都得从零开始做。PS. 其实隔壁同事类似的数量级,但是数据在hive上,很快就导完结束下班了。

导出到U盘: 10亿的消费数据放到U盘里。PS.如果不是知道他是真正的需求方,要不然真会怀疑他是不是想通过这些数据做什么坏事...

#踩过的坑坑洼洼

接到任务,不管多难,总得做不是。那就开始各种google百度,首先第一步是如何将数据从mysql导出到硬盘。最直接的想法是通过navicat提供的数据导出工具

15f8388aa7733b1e3f5dbb2f0497f4db.png

但是当我看着2000个表需要一个个勾选,导出到一个个文件到windows,因为对自己的电脑的充分不自信片刻就放弃了这个年头(其实写这篇文章的时候发现是自己不会玩navicat... 事实上完全可以通过navicat直接导。但是技术人员嘛,踩了坑还是希望叨逼叨逼一下涨点姿势)

然后就把思路放到了直接通过linux的一堆shell命令工具实现需求。一堆google,在stackoverflow上找到了一个47赞的问答直接戳中要点!

mysql -B -u username -p password database -h dbhost -e "SELECT * FROM accounts;" \

| sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g"

一句命令就把结果直接计算出来了。看到这个答案的时候,我心里实在是激动啊。 而事实上也主要因为自己以前对直接命令行操作mysql不熟悉,事后分析最主要的命令参数其实就一个 -e。execute命令。然后的想法很自然就是跑2000+遍这样的语句,通过管道把结果导出来。

#... ...

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221024" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221025" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221026" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221027" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221028" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221029" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221030" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221031" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221101" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221102" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221103" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221104" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221105" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221106" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

mysql -B -N -uusername -ppassword -hyourhost -Pyourport database -e "select column1, column2 ... from table_20221107" | sed "s/'/\'/;s/\t/\",\"/g;s/^/\"/;s/$/\"/;s/\n//g" >> tmp.csv

这样就可以把所有的文件导到同一个tmp.csv文件,最后再用一遍split命令按照行把tmp.csv切割成100w行一个的文件就ok了。

然后就开始跑。

结果发现跑了一段时间发现端口配错了,跑到一半的脚本莫名其妙退出。修改好端口之后打算继续跑,发现前面用的是>>管道,这就意味着批量操作中append可能会污染前面已生成的数据,几个G的数据又要重新来了。。。

所以这里就来了两个总结:

##不要用>>命令批量导出,要用>,才能防止重跑数据污染

##在不确定最终结果是否稳定的情况下,一定要保存中间过度数据

比如上面所有数据都保存到tmp.csv上就不是中间数据,因为所有数据都在一个文件中,要么完成,要么失败,损失的是时间。。。正确的做法是把每个日表导出到对应的一个文件中,最后再通过cat *.csv > final.csv合并起来。这样即使中间有数据出错,一方面好校验,一方面不至于全盘重新开始。

删完数据,继续启动。数据跑正常了,通过wc -l看一下导出数据的速度,平均每秒10000行写入到磁盘。然后计算一下时间发现竟然要四个多小时.... 当时已经是晚上8点了,是打算通宵的节奏么?不行不行,这样的策略太慢。第三个惨痛的领悟:

##导大量数据一定要并行!

继续改,把上面一整个大堆的串行命令切割成n份shell_n.sh,通过nohup shell_n.sh &批量执行。

这次终于成功了,所有的数据顺利导出来。虽然现在只是中间数据,但是离胜利也不远了,剩下的就是cat,split。完成!

本次特殊情况时间紧,没来得及细想执行方案,中间遇到很多个小问题卡了不少时间。而且因为最早没有中间数据,每次卡顿都得重新开始。最后搞到1点多才走,但是也收获不少经验上的东西。

#一个自动化的脚本

最后,为了防止类似事情再度发生,这里准备了一个自动化的脚本。比较简单,只需要修改一些主要的配置就可以自动完成以上所有功能。

# coding:utf-8

import os

import datetime

start = datetime.datetime(2017, 1, 1)

end = datetime.datetime(2017, 12, 31)

host = ""

port = ""

user = ""

password = ""

database = ""

columns = "*"

tableName = ""

condition = "create_time<='2016-12-31 23:59:59'"

threadNum = 10

if not os.path.exists("shell"):

os.mkdir("shell")

if not os.path.exists("data"):

os.mkdir("data")

preSql = "SELECT ${columns} FROM ${tableName}_${time} WHERE ${condition}"

preSql = preSql.replace("${columns}", columns)

preSql = preSql.replace("${tableName}", tableName)

preSql = preSql.replace("${condition}", condition)

preShell = "mysql -B -C -N -u${user} -p${password} -h${host} -P${port} ${database} -e \"${sql}\" | sed \"s/'/\\'/;s/\\t/\\\",\\\"/g;s/^/\\\"/;s/$/\\\"/;s/\\n//g\" > ./data/tmp_${time}.csv\n"

preShell = preShell.replace("${user}", user)

preShell = preShell.replace("${password}", password)

preShell = preShell.replace("${host}", host)

preShell = preShell.replace("${port}", port)

preShell = preShell.replace("${database}", database)

preShell = preShell.replace("${sql}", preSql)

total = (end - start).days + 1

step = (total / threadNum) if (total / threadNum) > 1 else 1

shellScript = [[] for i in range(threadNum)]

count = 0

while count < total:

for j in range(threadNum):

if count < total:

current = start + datetime.timedelta(days=count)

currentStr = current.strftime("%Y%m%d")

shell = preShell.replace("${time}", currentStr)

shellScript[j].append(shell)

count += 1

count = 0

for section in shellScript:

combine = ""

for s in section:

combine += s

with open("./shell/shell_" + str(count) + ".sh", "w") as final_shell:

final_shell.write(combine)

count += 1

for index in range(count):

bash = "sudo bash ./shell/shell_" + str(index) + ".sh &"

os.popen(bash)

以上。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用Sqoop的export命令将Hive表中的数据导出MySQL中。具体步骤如下: 1. 确保MySQL数据库已经创建好,并且具有与Hive表相同的表结构。 2. 在Sqoop的命令中,使用export参数指定需要导出数据表,格式如下: ``` sqoop export --connect jdbc:mysql://mysql_host:port/mysql_database --username mysql_username --password mysql_password --table mysql_table --export-dir hive_table --input-fields-terminated-by '\t' --input-lines-terminated-by '\n' ``` 其中,`--connect`参数指定MySQL数据库的连接信息,`--username`和`--password`参数指定数据库的用户名和密码,`--table`参数指定需要导入的MySQL表名,`--export-dir`参数指定需要导出的Hive表名,`--input-fields-terminated-by`参数指定Hive表中字段的分隔符,`--input-lines-terminated-by`参数指定Hive表中行的分隔符。 3. 如果Hive表中没有主键,则需要使用`--update-key`参数指定用于更新行的列名。例如,如果Hive表中有一个名为`id`的列,则可以使用以下命令: ``` sqoop export --connect jdbc:mysql://mysql_host:port/mysql_database --username mysql_username --password mysql_password --table mysql_table --export-dir hive_table --input-fields-terminated-by '\t' --input-lines-terminated-by '\n' --update-key id ``` 这将使用`id`列作为更新行的关键字。 4. 执行命令并等待导出完成。 注意:在执行Sqoop导出命令之,需要确保Hive表中的数据已经是全量数据,否则可能会出现数据不一致的情况。如果需要覆盖MySQL中的数据,请确保备份MySQL中的数据,以防止数据丢失。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值