使用 Spark 抽取 MySQL 数据到 Hive 时某列字段值出现异常(字段错位)

源数据描述

在 MySQL 中建立了表 order_info ,其字段信息如下所示:

+--------------------+------------------+------+-----+-------------------+-----------------------------+
| Field              | Type             | Null | Key | Default           | Extra                       |
+--------------------+------------------+------+-----+-------------------+-----------------------------+
| order_id           | int(10) unsigned | NO   | PRI | NULL              | auto_increment              |
| order_sn           | varchar(100)     | NO   |     | NULL              |                             |
| address            | varchar(100)     | NO   |     | NULL              |                             |
| create_time        | varchar(100)     | NO   |     | NULL              |                             |
| pay_time           | varchar(100)     | NO   |     | NULL              |                             |
+--------------------+------------------+------+-----+-------------------+-----------------------------+

除了 order_id 字段,其余字段类型都为 varchar(100)

根据 create_time 降序查询前 20 列,输出结果如下:

+----------+------------------+----------------------------------------------------------------------------------------+----------------+----------------+
| order_id | order_sn         | address                                                                                | create_time    | pay_time       |
+----------+------------------+----------------------------------------------------------------------------------------+----------------+----------------+
|    36876 | 2022091196411460 | 江苏省南京市中央路2014271号第一层110店铺11| 20220911220617 | NULL           |
|    36877 | 2022091196411460 | 江苏省南京市中央路2014271号第一层110店铺11| 20220911220617 | 20220911232917 |
|    36878 | 2022091196411460 | 江苏省南京市中央路2014271号第一层110店铺11| 20220911220617 | 20220911232917 |
|    36879 | 2022091196411460 | 江苏省南京市中央路2014271号第一层110店铺11| 20220911220617 | 20220911232917 |
|    36880 | 2022091196411460 | 江苏省南京市中央路2014271号第一层110店铺11| 20220911220617 | 20220911232917 |
|    36798 | 2022091196213791 | 江苏省昆山市柏庐南路9991713号商业街商铺1171313| 20220911220552 | NULL           |
|    36799 | 2022091196213791 | 江苏省昆山市柏庐南路9991713号商业街商铺1171313| 20220911220552 | 20220912115152 |
|    36800 | 2022091196213791 | 江苏省昆山市柏庐南路9991713号商业街商铺1171313| 20220911220552 | 20220912115152 |
|    36801 | 2022091196213791 | 江苏省昆山市柏庐南路9991713号商业街商铺1171313| 20220911220552 | 20220912115152 |
|    36811 | 2022091010006041 | 江苏省南京市中山路3213071号鼓楼医院南扩新院区门诊大楼一楼5| 20220911220057 | NULL           |
|    36812 | 2022091010006041 | 江苏省南京市中山路3213071号鼓楼医院南扩新院区门诊大楼一楼5| 20220911220057 | 20220912103157 |
|    36813 | 2022091010006041 | 江苏省南京市中山路3213071号鼓楼医院南扩新院区门诊大楼一楼5| 20220911220057 | 20220912103157 |
|    36814 | 2022091010006041 | 江苏省南京市中山路3213071号鼓楼医院南扩新院区门诊大楼一楼5| 20220911220057 | 20220912103157 |
|    37448 | 2022091154427501 | 上海市上海市杨树浦路26887789号上海国际时尚中心18| 20220911213924 | NULL           |
|    37449 | 2022091154427501 | 上海市上海市杨树浦路26887789号上海国际时尚中心18| 20220911213924 | 20220912115924 |
|    37450 | 2022091154427501 | 上海市上海市杨树浦路26887789号上海国际时尚中心18| 20220911213924 | 20220912115924 |
|    37451 | 2022091154427501 | 上海市上海市杨树浦路26887789号上海国际时尚中心18| 20220911213924 | 20220912115924 |
|    37227 | 2022091178845429 | 浙江省宁波市鄞州区四明中路9996584号万达广场银泰百货1F15层                              | 20220911212820 | NULL           |
|    37228 | 2022091178845429 | 浙江省宁波市鄞州区四明中路9996584号万达广场银泰百货1F15层                              | 20220911212820 | 20220912110220 |
|    37229 | 2022091178845429 | 浙江省宁波市鄞州区四明中路9996584号万达广场银泰百货1F15层                              | 20220911212820 | 20220912110220 |
+----------+------------------+----------------------------------------------------------------------------------------+----------------+----------------+

可以在这里看到原始数据的形态。

问题复现

现在我对这个问题进行复现,在下面这段代码里,我在 Hive 中的 ods 层建立了 MySQL 的对应表 order_info,其字段信息如下所示:

order_id                int                                         
order_sn                string
address                 string
create_time             string
pay_time                string                                      
etl_date                string

同样除了 order_id 字段,其余字段类型都为 string,其中 etl_date 字段为该表的分区字段。

import org.apache.spark.sql.SparkSession

object Test{

    def main(args: Array[String]): Unit = {

        val spark: SparkSession = SparkSession
                .builder()
                .appName("Test")
                .master("local[*]")
                .enableHiveSupport()
                .getOrCreate()

        // TODO 读取 MySQL 表数据
        spark.read.format("jdbc")
                .option("driver", "com.mysql.jdbc.Driver")
                .option("url", "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8")
                .option("user", "root")
                .option("password", "123456")
                .option("dbtable", "order_info")
                .load()
                .createOrReplaceTempView("order_info")

        // TODO 在 Hive 中 ods 层创建表
        spark.sql("create database if not exists ods")

        spark.sql("drop table if exists ods.order_info")

        spark.sql(
            """
              |create table ods.order_info(
              |     order_id            int,
              |     order_sn            string,
              |     address             string,
              |     create_time         string,
              |     pay_time            string)
              |partitioned by(etl_date string)
              |row format delimited fields terminated by ','
              |""".stripMargin)

        // TODO 往表中写入数据
        spark.sql(
            """
              |insert into ods.order_info partition(etl_date="20230425")
              |select
              |     order_id,
              |     order_sn,
              |		address,
              |     create_time,
              |     pay_time
              |from
              |     order_info
              |""".stripMargin)

        // TODO 读取写入的数据
        spark.sql(
            """
              |select
              |     order_id,
              |     order_sn,
              |     address,
              |     create_time,
              |     pay_time
              |from
              |     ods.order_info
              |where
              |     order_sn = "2022031949619563"
              |""".stripMargin).show(20,truncate = false)

        spark.stop()
        

    }

}

抽取完成后,在 Hive 中查询 order_info 表订单编号值为 2022031949619563 的数据,输出结果如下:

+--------+----------------+-----------------------------------------------------------+-----------+--------------+
|order_id|order_sn        |address                                                    |create_time|pay_time      |
+--------+----------------+-----------------------------------------------------------+-----------+--------------+
|688     |2022031949619563|浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107         |10813|20220320184807|
|689     |2022031949619563|浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107         |10813|20220320184807|
|690     |2022031949619563|浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107         |10813|20220320184807|
|691     |2022031949619563|浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107         |10813|20220320184807|
+--------+----------------+-----------------------------------------------------------+-----------+--------------+

我们会发现,其中 create_time 的值变成了一个中文字符串。

我们去 MySQL 源数据中核查一下该订单编号的数据,查询结果如下:

+----------+------------------+-----------------------------------------------------------------------------------------------+----------------+----------------+
| order_id | order_sn         | address                                                                                       | create_time    | pay_time       |
+----------+------------------+-----------------------------------------------------------------------------------------------+----------------+----------------+
|      688 | 2022031949619563 | 浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107,10813| 20220320184807 | NULL           |
|      689 | 2022031949619563 | 浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107,10813| 20220320184807 | 20220321044607 |
|      690 | 2022031949619563 | 浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107,10813| 20220320184807 | 20220321044607 |
|      691 | 2022031949619563 | 浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107,10813| 20220320184807 | 20220321044607 |
+----------+------------------+-----------------------------------------------------------------------------------------------+----------------+----------------+

可以通过上下文对比轻松发现问题,数据抽取完成后,字段值出现了异常,那么为什么会这样呢?

问题解析

这是由于我们在创建 Hive 对应表时指定的字段分隔符是逗号 ",",所以在写入数据时,会默认将逗号作为字段分隔符。

        spark.sql(
            """
              |create table ods.order_info(
              |     order_id            int,
              |     order_sn            string,
              |     address             string,
              |     create_time         string,
              |     pay_time            string)
              |partitioned by(etl_date string)
              |row format delimited fields terminated by ','
              |""".stripMargin)

这里导致了字段 address 中的逗号被识别成了字段间隔符,在那之后的字段整体发生了错位,产生了数据异常。

问题解决

  1. 替换成其它分隔符

  2. 不指定分隔符,用 Hive 的默认分隔符 \u0001

在问题复现这里我们可以很容易的发现问题产生的原因,在实际开发中,字段特别多,而且不一定每个字段都会使用到,我这里是因为使用了发生错误的字段,所以才发现了这个问题,提醒各位在建表时需要格外注意指定的字段分隔符。

虽然是个很小的 BUG,但是往往容易错的就是这种小 BUG!

要实现 Spark 抽取 MySQL 数据Hive,可以采用以下步骤: 1. 在 Spark 中首先读取 MySQL 数据,可以使用 JDBC 连接 MySQL 数据库,将需要的数据读取到 Spark 的 DataFrame 中。 2. 在 Spark 中创建 Hive 表,将 DataFrame 写入到 Hive 表中。可以使用 Spark SQL 中的 `saveAsTable` 方法将 DataFrame 保存为 Hive 表。 下面是一个示例代码: ```scala import org.apache.spark.sql.SparkSession object MySQLToHive { def main(args: Array[String]): Unit = { val spark = SparkSession.builder() .appName("MySQLToHive") .enableHiveSupport() .getOrCreate() val jdbcUrl = "jdbc:mysql://localhost:3306/mydb?user=root&password=123456" val mysqlTable = "mytable" val hiveTable = "myhive" val df = spark.read.format("jdbc") .option("url", jdbcUrl) .option("dbtable", mysqlTable) .load() df.write.mode("overwrite").saveAsTable(hiveTable) } } ``` 这段代码中,首先使用 SparkSession 创建 Spark 应用程序,并启用 Hive 支持。然后指定 MySQL 数据库的 JDBC URL、需要读取的 MySQL 表名和需要创建的 Hive 表名。 使用 Spark 的 `read.format("jdbc")` 方法读取 MySQL 数据库中的数据,然后使用 `write.mode("overwrite").saveAsTable(hiveTable)` 方法将 DataFrame 写入到 Hive 表中。其中 `mode("overwrite")` 表示如果 Hive 表已经存在,则覆盖原有表。 在执行代码之前,需要先在 Hive 中创建一个与代码中指定的 Hive 表名相同的表,表结构需要与 MySQL 表结构一致。 这样就实现了 Spark 抽取 MySQL 数据Hive 的过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月亮给我抄代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值