源数据描述
在 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号商业街商铺11713号13层 | 20220911220552 | NULL |
| 36799 | 2022091196213791 | 江苏省昆山市柏庐南路9991713号商业街商铺11713号13层 | 20220911220552 | 20220912115152 |
| 36800 | 2022091196213791 | 江苏省昆山市柏庐南路9991713号商业街商铺11713号13层 | 20220911220552 | 20220912115152 |
| 36801 | 2022091196213791 | 江苏省昆山市柏庐南路9991713号商业街商铺11713号13层 | 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 |108室13层 |20220320184807|
|689 |2022031949619563|浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107 |108室13层 |20220320184807|
|690 |2022031949619563|浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107 |108室13层 |20220320184807|
|691 |2022031949619563|浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107 |108室13层 |20220320184807|
+--------+----------------+-----------------------------------------------------------+-----------+--------------+
我们会发现,其中 create_time
的值变成了一个中文字符串。
我们去 MySQL 源数据中核查一下该订单编号的数据,查询结果如下:
+----------+------------------+-----------------------------------------------------------------------------------------------+----------------+----------------+
| order_id | order_sn | address | create_time | pay_time |
+----------+------------------+-----------------------------------------------------------------------------------------------+----------------+----------------+
| 688 | 2022031949619563 | 浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107,108室13层 | 20220320184807 | NULL |
| 689 | 2022031949619563 | 浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107,108室13层 | 20220320184807 | 20220321044607 |
| 690 | 2022031949619563 | 浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107,108室13层 | 20220320184807 | 20220321044607 |
| 691 | 2022031949619563 | 浙江省杭州市教工路264370号世贸丽晶城欧美中心24370号楼G区107,108室13层 | 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
中的逗号被识别成了字段间隔符,在那之后的字段整体发生了错位,产生了数据异常。
问题解决
-
替换成其它分隔符
-
不指定分隔符,用 Hive 的默认分隔符
\u0001
。
在问题复现这里我们可以很容易的发现问题产生的原因,在实际开发中,字段特别多,而且不一定每个字段都会使用到,我这里是因为使用了发生错误的字段,所以才发现了这个问题,提醒各位在建表时需要格外注意指定的字段分隔符。
虽然是个很小的 BUG,但是往往容易错的就是这种小 BUG!