spark sql读取hive数据直接写入doris,离线批量导入

文章介绍了如何使用ApacheSparkSQL从Hive表中读取数据,通过自定义输出格式,并利用Doris的StreamLoad功能将数据批量写入,实现了数据迁移的优化,同时提及了如何处理写入过程中的异常和负载均衡策略。
摘要由CSDN通过智能技术生成

先简单的贴贴代码,后面会完善一下。

一,spark sql 读取hive表

这里通过catalog查询表的字段信息,然后 查询出来的字段colStr 要给下面的doris使用。

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6buE55Oc54KW5ZWk6YWS6bit_size_20_color_FFFFFF_t_70_g_se_x_16

注意:我这里是直接拿取的hive表所有的字段。

二,spark自定义输出

这里就是简单封装了一下

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6buE55Oc54KW5ZWk6YWS6bit_size_13_color_FFFFFF_t_70_g_se_x_16

实现的效果:

watermark_type_ZHJvaWRzYW5zZmFsbGJhY2s_shadow_50_text_Q1NETiBA6buE55Oc54KW5ZWk6YWS6bit_size_20_color_FFFFFF_t_70_g_se_x_16 1

三,通过stream load方式数据写入doris

循环遍历DataFrame之后写入到doris里面:

 
  1. val dorisStreamLoader = new DorisStreamLoad("192.168.5.xx:8040", "example_db", "assuer_order_test", "root", "root")
  2. val cumsArrays = colStr.split(",")
  3. val fieldDelimiter: String = "\t"
  4. val lineDelimiter: String = "\n"
  5. val NULL_VALUE: String = "\\N"
  6. val maxRowCount = 5000
  7. val maxRetryTimes = 3
  8. data.rdd.foreachPartition(partition => {
  9. val buffer = ListBuffer[String]()
  10. var jsonArrays = new JSONArray()
  11. partition.foreach(f = row => {
  12. // val value: StringJoiner = new StringJoiner(fieldDelimiter)
  13. // create one row string
  14. val json = new JSONObject()
  15. for (i <- 0 until row.size) {
  16. val field = row.get(i)
  17. val fieldName = cumsArrays(i)
  18. if (field == null) {
  19. // value.add(NULL_VALUE)
  20. json.put(fieldName, NULL_VALUE)
  21. } else {
  22. // value.add(field.toString)
  23. json.put(fieldName, field.toString)
  24. }
  25. }
  26. jsonArrays.add(json)
  27. // add one row string to buffer
  28. // buffer += value.toString
  29. // if (buffer.size >= maxRowCount) {
  30. // flush
  31. // Thread.sleep(1000L)
  32. // }
  33. if (jsonArrays.size() >= maxRowCount) {
  34. flush
  35. Thread.sleep(1000L)
  36. }
  37. })
  38. // flush buffer
  39. if (jsonArrays.size() > 0) {
  40. flush
  41. Thread.sleep(1000L)
  42. }
  43. def flush = {
  44. val loop = new Breaks
  45. loop.breakable {
  46. for (i <- 1 to maxRetryTimes) {
  47. try {
  48. // dorisStreamLoader.load(buffer.mkString(lineDelimiter))
  49. dorisStreamLoader.load(jsonArrays.toJSONString)
  50. // buffer.clear()
  51. jsonArrays.clear()
  52. loop.break()
  53. }
  54. catch {
  55. case e: Exception =>
  56. try {
  57. Thread.sleep(1000 * i)
  58. // dorisStreamLoader.load(buffer.mkString(lineDelimiter))
  59. // dorisStreamLoader.load(jsonArrays.toJSONString)
  60. dorisStreamLoader.load(jsonArrays.toJSONString)
  61. //buffer.clear()
  62. jsonArrays.clear()
  63. } catch {
  64. case ex: InterruptedException =>
  65. Thread.currentThread.interrupt()
  66. throw new IOException("unable to flush; interrupted while doing another attempt", e)
  67. }
  68. }
  69. }
  70. }
  71. }
  72. })

注意: 我在这里写入doris是直接写入doris BE节点,后面代码需要传入3个FE,然后随机找一个FE,然后再获取BE写入,这样子做到负载均衡,而且写入失败可以重试。

四,测试

就直接上代码了,自己验证了吧:

 
  1. package com.sjb.spark2doris.test
  2. import java.io.IOException
  3. import com.alibaba.fastjson.{JSONArray, JSONObject}
  4. import com.sjb.spark2doris.{DorisStreamLoad, SparkDataFrame2Doris}
  5. import org.apache.commons.lang3.StringUtils
  6. import org.apache.log4j.{Level, Logger}
  7. import org.apache.spark.SparkConf
  8. import org.apache.spark.sql.catalog.Column
  9. import org.apache.spark.sql.{Dataset, SparkSession}
  10. import scala.collection.mutable.ListBuffer
  11. import scala.util.control.Breaks
  12. //todo com.sjb.spark2doris.test.SparkDataFrame2DorisTest
  13. object SparkDataFrame2DorisTest {
  14. var LOGGER: Logger = Logger.getLogger(SparkDataFrame2Doris.getClass)
  15. Logger.getLogger("org.apache.hadoop").setLevel(Level.WARN)
  16. Logger.getLogger("org.apache.spark").setLevel(Level.INFO)
  17. Logger.getLogger("org.apache.eclipse.jetty.server").setLevel(Level.OFF)
  18. def main(args: Array[String]): Unit = {
  19. val sparkConf = new SparkConf().setAppName(this.getClass.getName)
  20. if (args.length < 1) {
  21. println("本地模式.......................")
  22. sparkConf.setMaster("local[*]")
  23. // sys.exit(1)
  24. } else {
  25. println("生产模式............")
  26. }
  27. // System.setProperty("HADOOP_USER_NAME", "hive")
  28. sparkConf.set("javax.jdo.option.ConnectionURL", "jdbc:mysql://192.168.x.x:3306/hive?createDatabaseIfNotExist=true&characterEncoding=UTF-8")
  29. sparkConf.set("javax.jdo.option.ConnectionDriverName", "com.mysql.jdbc.Driver")
  30. sparkConf.set("javax.jdo.option.ConnectionUserName", "hive")
  31. sparkConf.set("javax.jdo.option.ConnectionPassword", "Hive")
  32. val spark = SparkSession
  33. .builder
  34. .config(sparkConf)
  35. .enableHiveSupport()
  36. .config("spark.sql.warehouse.dir", "spark-warehouse")
  37. .config("dfs.client.use.datanode.hostname", "true")
  38. .getOrCreate()
  39. try {
  40. //设置hive数据库
  41. spark.sql("set hive.exec.dynamic.partition=true")
  42. spark.sql("set hive.exec.dynamic.partition.mode=nonstrict")
  43. // spark.sql("use doris_test")
  44. // spark.sql("show tables").show(100)
  45. //todo 打印输出表字段
  46. spark.catalog.listColumns("doris_test", "fs_plt_assure_orders_test").show()
  47. val aa: Dataset[Column] = spark.catalog.listColumns("doris_test", "fs_plt_assure_orders_test")
  48. val colList = aa.collect().map(x => x.name)
  49. val colStr = colList.mkString(",")
  50. val sql = "select " + colStr + " from doris_test.fs_plt_assure_orders_test"
  51. println(sql)
  52. val data = spark.sql(sql).toDF()
  53. val dorisStreamLoader = new DorisStreamLoad("192.168.5.xx:8040", "example_db", "assuer_order_test", "root", "root")
  54. val cumsArrays = colStr.split(",")
  55. val fieldDelimiter: String = "\t"
  56. val lineDelimiter: String = "\n"
  57. val NULL_VALUE: String = "\\N"
  58. val maxRowCount = 5000
  59. val maxRetryTimes = 3
  60. data.rdd.foreachPartition(partition => {
  61. val buffer = ListBuffer[String]()
  62. var jsonArrays = new JSONArray()
  63. partition.foreach(f = row => {
  64. // val value: StringJoiner = new StringJoiner(fieldDelimiter)
  65. // create one row string
  66. val json = new JSONObject()
  67. for (i <- 0 until row.size) {
  68. val field = row.get(i)
  69. val fieldName = cumsArrays(i)
  70. if (field == null) {
  71. // value.add(NULL_VALUE)
  72. json.put(fieldName, NULL_VALUE)
  73. } else {
  74. // value.add(field.toString)
  75. json.put(fieldName, field.toString)
  76. }
  77. }
  78. jsonArrays.add(json)
  79. // add one row string to buffer
  80. // buffer += value.toString
  81. // if (buffer.size >= maxRowCount) {
  82. // flush
  83. // Thread.sleep(1000L)
  84. // }
  85. if (jsonArrays.size() >= maxRowCount) {
  86. flush
  87. Thread.sleep(1000L)
  88. }
  89. })
  90. // flush buffer
  91. if (jsonArrays.size() > 0) {
  92. flush
  93. Thread.sleep(1000L)
  94. }
  95. def flush = {
  96. val loop = new Breaks
  97. loop.breakable {
  98. for (i <- 1 to maxRetryTimes) {
  99. try {
  100. // dorisStreamLoader.load(buffer.mkString(lineDelimiter))
  101. dorisStreamLoader.load(jsonArrays.toJSONString)
  102. // buffer.clear()
  103. jsonArrays.clear()
  104. loop.break()
  105. }
  106. catch {
  107. case e: Exception =>
  108. try {
  109. Thread.sleep(1000 * i)
  110. // dorisStreamLoader.load(buffer.mkString(lineDelimiter))
  111. // dorisStreamLoader.load(jsonArrays.toJSONString)
  112. dorisStreamLoader.load(jsonArrays.toJSONString)
  113. //buffer.clear()
  114. jsonArrays.clear()
  115. } catch {
  116. case ex: InterruptedException =>
  117. Thread.currentThread.interrupt()
  118. throw new IOException("unable to flush; interrupted while doing another attempt", e)
  119. }
  120. }
  121. }
  122. }
  123. }
  124. })
  125. LOGGER.info("data write success.....")
  126. } catch {
  127. case e: Exception => {
  128. e.printStackTrace()
  129. }
  130. }
  131. spark.stop()
  132. }
  133. }

DorisStreamLoad这个类也很简单,看我之前的文章或者对doris stream load方式写入了解都知道很简单,如果你不懂的话 还是先学习一下。

五,总结

 
  1. 这种方式比起我之前需要通过flink读取hdfs写入kafka,再由kafka写入到doris,就省事很多,测试 上百个字段的hive表数据 千万级别的 本地idea运行也没有OOM、
  2. 最后的话,有需要的不懂的小伙伴可以私信问我 我很乐意分享,我也是参考官方的代码。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值