今天在开发中遇到了一个问题,dolphinscheduler-data-quality-dev-SNAPSHOT.jar中spark sql查询clickhouse的时候对时间格式为2022-09-30 00:00:00类型的查询会自动转成2022-09-30 00:00:00.0然后再去clickhouse中查询,导致报错

Caused by: java.lang.Throwable: Code: 53. DB::Exception: Cannot convert string 2022-09-30 00:00:00.0 to type DateTime: while executing 'FUNCTION equals(YWDATE : 6, '2022-09-30 00:00:00.0' : 11) -> equals(YWDATE, '2022-09-30 00:00:00.0') UInt8 : 17'. (TYPE_MISMATCH) (version 22.3.8.39 (official build))

        at ru.yandex.clickhouse.except.ClickHouseExceptionSpecifier.specify(ClickHouseExceptionSpecifier.java:53)
        ... 26 more

原因为:Dataset<Row> inputDataSet = jdbcReader(env.sparkSession()).load();是懒加载的,就算是注册为了临时表,再进行复制的sql查询都不会立刻执行,只是简单地记住所有对数据集的转换操作逻辑,在最后excuse的时候才会解析成最方便快捷的去执行。

  • 在解析的时候如果查询条件中带了YWDATE = '2022-09-30 00:00:00'之类的时间查询,spark sql会将'2022-09-30 00:00:00'处理成'2022-09-30 00:00:00.0',再去clickhouse中查询就会报错。
  • 在解析的时候如果查询条件中带了YWDATE = '2022/09/30 00:00:00'之类的时间查询,spark sql会将'2022/09/30 00:00:00'处理掉,不在数据库中去执行,在内存中执行吧,具体的不清楚,反正就是加了时间,在clickhouse日志中也没看到,最后也查不出来数据。

尽量不要让spark sql在懒加载后的excuse中自己去解析时间类型,具有不清楚,就是有问题

通过查看clickhouse的query log可以看到

SELECT
	*
from
	`system`.query_log ql
where
	query like '%ADS_FCM_ZW_KMDXFL_COMMON_DH%'
order by
	event_time desc

1.出现问题后的总结

  1. spark sql 执行查询条件中带了YWDATE = '2022-09-30 00:00:00'之类的时间查询,spark sql会将'2022-09-30 00:00:00'处理成'2022-09-30 00:00:00.0',再去clickhouse中查询就会报错。
  2. spark sql中分为action算子和transformation 算子,action算子会立刻执行,transformation 算子不会。
  3. 就算action算子立刻执行出了结果,如果没有加缓存,最后在执行excuse的时候,spark还是会解析之前全部的操作逻辑,最后整合后,全部再执行一遍。

2.createOrReplaceTempView、缓存学习

在spark程序实际开发过程中遇到需要对文件内容做join操作,使用createOrReplaceTempView 方式将读取的文件创建临时表,然后通过 spark.sql()方式利用sql语句做join操作,但是数据量稍微大点时候,就会导致join效率很慢。查询资料得知,这里有优化的空间,利用 cache() 或者 persist() 方法。

2.1. 原理

createOrReplaceTempView是 transformation 算子,而transformation是lazy模式的,也就是spark不会立即计算结果,而只是简单地记住所有对数据集的转换操作逻辑,需要有action算子来触发spark应用程序,最简单的action算子:show()。例如:spark.sql("select *** from XXX ").createOrReplaceTempView(“tmp_test_table”).show()。
这样每次执行.show()算子时候,都需要先执行createOrReplaceTempView操作,导致效率很慢。肯有直接将读取出来的数据缓存起来,或者将createOrReplaceTempView之后的数据缓存到内存中。

缓存的方式有两种,具体使用不在此做赘述
1) cache()方法表示:使用非序列化的方式将RDD的数据全部尝试持久化到内存中,cache()只是一个transformtion,是lazy的,必须通过一个action触发,才能真正的将该RDD cache到内存中。
2)persist()方法表示:手动选择持久化级别,并使用指定的方式进行持久化。

dolphinscheduler问题:dolphinscheduler-data-quality-dev-SNAPSHOT.jar中对于ClickHouse中数据类型为DateTime类型的数据报错,如果是DateTime64就可以查询

如果增加cacha,会增加查询到内存的数据量,除非一开始就增加过滤条件去查询加载到内存的数据,dolphinscheduler先是全部load数据,然后注册成临时表,再在转换中去处理,在执行的时候,spark会默认将时间类型的查询条件变成yyyy-MM-dd HH:mm:ss.SSS去查询,如果数据类型为DateTime类型yyyy-MM-dd HH:mm:ss就会报错,只有类型是DateTime64类型才行!mysql因为时间类型就只有datetime所以没问题。