分库分表mysql到hdfs

前面文章写入如何将hdfs的数据分表插入mysql。这里主要讲解如何进行分库分表抽取。
需求:
公司线上库的一些数据量比较大的表是存在多库多表的。数仓进行数据抽取的时候,需要将这些表统一抽取过来,然后进行合并到一张hive表。
以前的方案:1、通过 mysqluldr2 这个工具将多表抽取到本地,user_1.txt,user_2.txt,user_3.txt…。
2、cat user*.txt > user.txt
3、hdfs dfs -put user.txt
4.load data inpath ‘’ into table …
原来方案的数据文件都要落地,同时源码在某次编译过程中丢失,需要进行重构。
新方案使用spark进行抽取:
1、mysqljdbc读取数据,write到hdfs文件
2、将hdfs文件映射到表插入最终src表

 大概代码如下:
 sc.parallelize(newImportConfs,1).map(conf=>(conf.getHashCode,conf)).partitionBy(new HashPartitioner(partitionNum)).foreachPartition(
     partition =>{
       val partitionId = TaskContext.get.partitionId
       var conn:Connection = null
       var ps:PreparedStatement = null
       var rs:ResultSet = null
       var os : FSDataOutputStream = null
       var fs : FileSystem = null
       try {

         // 将数据写入hdfs
         val conf = new Configuration()
         conf.set("fs.defaultFS", "hdfs://nameservice1");
         val fileName = tmpTb + "_" + partitionId.toString
         val path: String = hdfsPath + "/" + fileName + ".txt";
         fs = FileSystem.newInstance(conf);
         val pathFile = new Path(path)

         //判断path是否存在,存在则append,不存在则create
         if (!fs.exists(pathFile)) {
           logger.info("文件" + path + "不存在,进行创建")
           os = fs.create(pathFile)
         } else {
           logger.info("文件已存在,先进行删除"+path)
           fs.delete(pathFile, true)
           os = fs.create(pathFile)
         }
         partition.foreach(result => {
           val importConf = result._2
           val jdbcModel = importConf.getJdbcModel
           val query = importConf.getQuery
           conn = DbUtils.getConnection(jdbcModel)
           ps = conn.prepareStatement(query, ResultSet.TYPE_FORWARD_ONLY,
             ResultSet.CONCUR_READ_ONLY)
           ps.setFetchSize(Integer.MIN_VALUE);
           ps.setFetchDirection(ResultSet.FETCH_REVERSE);
           rs = ps.executeQuery();
           val metaData = rs.getMetaData
           val colNum = metaData.getColumnCount
           var schemaString = ""
           for (x <- 0 to colNum - 1) {
             if (x < colNum - 1) {
               schemaString = schemaString + metaData.getColumnName(x + 1) + " "
             } else {
               schemaString = schemaString + metaData.getColumnName(x + 1)
             }
           }
           while (rs.next()) {
             var line: String = ""
             for (i <- 0 to colNum - 1) {
               val colType = metaData.getColumnType(i + 1)
               val value: String = getObjectValue(rs.getString(i + 1), colType)
               if (i < colNum - 1) {
                 line = line + value + "\001"
               } else {
                 line = line + value + "\n"
               }
             }
             os.write(line.getBytes("utf-8"))
           }
           rs.close()
           ps.close()
           conn.close()
        }
         )
       }catch{
         case e:Exception => {
           e.printStackTrace()
           val result = new StringWriter();
           import java.io.PrintWriter
           val printWriter = new PrintWriter(result)
           e.fillInStackTrace.printStackTrace(printWriter)
           throw new Exception("获取数据异常"+e.getMessage+"=="+result.toString)
         }
       }finally {
         if (os != null) {
           os.close()
         }
         if (fs != null) {
           fs.close()
         }
    }

     }

总结:
1、mysql读取大量数据是需要做fetchsize设置
ps = conn.prepareStatement(query, ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY)
ps.setFetchSize(Integer.MIN_VALUE);
ps.setFetchDirection(ResultSet.FETCH_REVERSE);
防止一次性读取mysql数据发生omm。设置成流式读取,只会读取一部分数据,这样就能防止JVM OMM
2、原先想着jdbc读出的数据转成一个dataframe,然后通过spark.sql插入到tmp表。后来过程中发现executor端无法将一个list转成rdd。具体报错应该是list转成dataset时会报错。list.toDF()无法使用。根本原因是executor无法读到sparkContext和spark。所以转化时会报空指针。最后改成了直接将数据写到hdfs文件,然后映射到hive表。
3、原先代码是没有加foreachpartition的直接是foreach。然后在里面打开fs,如果存在则append,如果不存在则create。这样有的时候会导致hdfs打开文件异常,报错 file cant create because file is ready being create…错误原因我猜应该是:task再失败情况下会重跑,然后fs虽然close,但是这个失败的fs未释放这个文件的锁,导致后面的文件无法append。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值