当前位置: 首页 > 科技观察

我用的是官方的ClickHouseJDBC驱动,坑无数,.

时间:2023-03-21 21:39:17 科技观察

前言最近遇到一个ClickHouse的在线问题:代码:242,e.displayText()=DB::Exception:Tableisinreadonlymode(zookeeperpath:/clickhouse/tables/02/xxx)(version21.12.4.1)(正式版)在网上找到了这个问题的原因,说是由于Zookeeper压力过大,表变成了只读,导致ClickHouse无法插入数据。具体原因有两个:写数据的频率太高。Zookeeper中的集群节点挂掉。我们项目出现这个问题的原因第一:写入数据的频率太高。但是在网上查找资料的过程中,又发现了一个问题:我们的项目使用了JDBC驱动MavengroupIdru.yandex.clickhouse,但是ClickHouse官方并没有推荐。于是果断访问了ClickHouse的官网,通过官网访问了ClickHouse的GitHub地址:https://github.com/ClickHouse/clickhouse-jdbc。确认官网不推荐使用ru.yandex.clickhouse驱动:应该改为com.clickhouse驱动,建议使用0.3.2以上版本:所以,JDBC驱动ClickHouse的升级之旅在接下来的几天开始了。踩过很多坑,分享给大家,希望对大家有所帮助。1.第一次,ClickHouse官方GitHub上推荐的JDBC驱动是0.3.2以上版本:所以,我果断将项目中pom.xml文件中的groupId替换为com.clickhouse,版本为更改为0.3.2。刷新maven,在本地启动项目,即可正常运行。然后在本地测试了业务功能,可以正常读写ClickHouse的数据。不禁在心里想:这升级也太容易了吧。2、第二次升级后,项目组同事建议换到最新版本,说新功能多了,性能有了很大的提升。听说性能有了很大的提升,于是决定再次尝试升级。所以,版本升级到0.3.2-patch11。本地再次测试,各项业务功能正常。然后将项目部署到测试环境。3.问题发现了。第二天收到两封sentry的告警邮件,告警级别为warn。第一封电子邮件中指出了一个例外:此驱动程序已弃用。请改用[com.clickhouse.jdbc.ClickHouseDriver]。表示ru.yandex.clickhouse驱动已经废弃,请使用com.clickhouse.jdbc.ClickHouseDriver驱动。第二封电子邮件中指出了一个例外:[ru.yandex.clickhouse]包中的所有内容也将从0.4.0开始删除。这意味着ru.yandex.clickhouse将被删除。看到这两封邮件,我有点懵。我刚才不是用了com.clickhouse驱动包吗?ru.yandex.clickhouse从何而来?于是在全球范围内搜索ru.yandex.clickhouse关键字,没有找到任何记录。这让我更加困惑。接下来打开clickhouse-jdbc-0.3.2-patch11-all.jar文件,看到一个意想不到的结果:jar包下居然有两个目录:com.clickhouse和ru.yandex.clickhouse,也就是say,jar包中的新驱动和旧驱动都支持。并且有两个ClickhouseDriver类:此时我脑子里有十万个理由:为什么不直接删除ru.yandex.clickhouse包的代码,而是在日志文件中打印一些警告?这实在是太可怜了。也就是升级驱动后,项目还是用老驱动的代码,我孤零零的测试了一下。..4、如何使用新版驱动?接下来,我心中的OS是:既然ClickHouse官方的驱动包同时支持新旧驱动,那肯定有一个开关来控制是使用新的JDBC驱动还是使用旧的JDBC驱动。从目前来看,如果不调整开关,ClickHouse官方驱动包默认使用旧的JDBC驱动。接下来,最重要的问题就是搞清楚:如何使用新的驱动程序?很快,我发现通过配置如下参数:spring.datasource.clickhouse.drive-class-name=com.clickhouse.jdbc.ClickHouseDriver可以指定Spring使用的JDBC驱动。果然,在配置数据源的application.properties文件中,添加了这样的配置,重启项目,Spring使用了新的ClickHouseJDBC驱动。电子邮件中的两个警告未打印在日志中。这时候心里暗自高兴,终于用上了ClickHouse官方推荐的JDBC驱动。项目一直运行正常,赶紧测试一下业务功能是否正常。5.出了两个新问题,立马被打脸。在测试批量插入数据的业务场景时,系统运行日志出现两个异常:Exception1:Code:6.DB:Exception:Cannotprsestring'2022-11-2214:42:37.025'asDateTime:syntaxerroratposition19...从提示信息来看,表示时间2022-11-2214:42:37.025不能转为DateTime类型。例外2:请考虑使用一个且仅一个值表达式,例如:使用'values(?)'而不是'values(?),(?).'从提示信息看,是不支持批量插入数据。我去。..升级ClickHouseJDBC驱动出现问题。ClickHouse官方最新的JDBC驱动不支持批量插入数据,这是比较严重的问题。快速搜索解决方案。6.回滚版本很快,在clickhouse-jdbc的issues中发现了类似的问题,地址:https://github.com/ClickHouse/clickhouse-jdbc/issues/1106。问题如下:下面有人回答:使用旧版本没有这个警告。我突然如梦初醒。不要执着于最新版本,clickhouse-jdbc一定要找到最合适的版本。于是,查看了dev、st、ga环境的ClickHouse服务器版本,发现dev使用的是20.12.8.5,而st、ga使用的是21.12.4.1。为了兼容dev环境,ClickHouse服务器版本以20+为准,再看看clickhouse-jdbc能用到什么版本。很快在releases中发现clickhouse-jdbc可以使用0.3.2,最高只能是0.3.2-patch1。因为0.3.2-patch2及以上版本,要求ClickHouse服务器版本为21+。因此,我只能将clickhouse-jdbc的版本回滚到:0.3.2-patch1。果然,回滚版本后,解决了无法批量插入的问题。接下来,有一个问题。7.DateTime一起回顾一下那个问题:Code:6.DB:Exception:Cannotprsestring'2022-11-2214:42:37.025'asDateTime:syntaxerroratposition19...从提示信息看,它说时间2022-11-2214:42:37.025不能转换为DateTime类型。DateTime的时间格式是:yyyy-MM-ddHH:mm:ss,这个问题是因为2022-11-2214:42:37.025包含毫秒,不能直接转换成2022-11-2214:42:37领先。我检查了代码和表结构。代码中将Entity中的time字段定义为Date类型。表中定义的时间字段为DateTime类型。ClickHouse官方驱动无法直接将Date类型时间转为DateTime类型。如何解决这个问题呢?答:修改表中的字段类型是不行的。将DateTime转换为DateTime64。DateTime64支持毫秒。我亲测使用DateTime64类型接收Java中Date类型的时间是可以正常解析的。该表具有三个DateTime字段:create_time、edit_time和time。前两个字段的字段类型可以轻松修改成功。但是在修改时间字段的时候报异常:Code:524,e.displayText()=DB::Exception:AlterofkeycolumntimefromtypeDateTimetotypeDateTime64(3)mustbemetadata-only(20.12.8.5)表示不能修改用作键的字段。为什么?8.orderby这次我直接查看了那个表的建表语句:showcreatetabletest;我发现除了主键和普通索引外,表还特地加了orderbyindex。例如:orderby(code,time)。看到这里,我很快就明白了,时间字段就是orderby的索引字段,难怪不能随便修改。于是,找DBA商量对策。DBA表示,要修改ClickHouse中表的索引字段的类型,唯一的办法就是重建表,然后同步数据。显然这个方案太麻烦了。我想知道是否还有其他更简单的解决方案?9、我这时候在想date_time_input_format这个参数,不会是时间转换的问题吧?让ClickHouse在保存数据的时候自动转换一个时间格式不就解决了吗?在官网上找到了一个参数,名字叫:date_time_input_format。此参数允许为日期和时间的文本表示选择解析器。它的可能值:'best_effort'-启用扩展解析。ClickHouse可以解析基本的YYYY-MM-DDHH:MM:SS格式和所有ISO8601日期和时间格式。例如,'2018-06-08T01:02:03.000Z'.'basic'-使用基本解析器。ClickHouse只能解析基本的YYYY-MM-DDHH:MM:SS格式。例如,“2019-08-2010:18:56”。默认值:“基本”。原来这是时间转换失败的根本原因。如果我们将date_time_input_format的值设置为best_effort,问题并不能解决。为了不影响全局,我只想调整那三个表的date_time_input_format的值。但是在保存设置的时候,报错。原来date_time_input_format参数只允许在MergeTree存储引擎上使用,而我们表的存储引擎使用的是ReplacingMergeTree。称重。..只能想别的办法了。10.ParseDateTimeBestEffortOrNull在插入数据的地方,用函数手动转换不行吗?当然,也可以把Java的Entity中的Date类型改成String,但是看了代码,这个改动有点大,涉及的地方也比较多。最小的更改在映射器层处理,因为映射器中最多有一个插入。而我查看了所有的ClickHouse表,只有3张表使用了DateTime类型,其他表都是DateTime64类型。一开始是在ClickHouse的官方文档中找到了formatDateTime这个函数。经过测试,我发现这个功能并不适用。后来找到parseDateTimeBestEffort系列函数,决定使用parseDateTimeBestEffortOrNull函数。只需在mapper.xml的insert语句中使用parseDateTimeBestEffortOrNull(#{item.time})修改即可。经过测试,发现时间转换问题解决了。后来这个异常出现在一个select语句中。一开始以为是toDate(time)函数引起的,后来发现是select的where条件中使用了time字段作为查询条件,导致了问题。这时候也使用parseDateTimeBestEffortOrNull函数来解决问题。至此ClickHouseJDBC驱动包升级完成,没有其他问题。需要特别注意的是:如果新建的表或者以后新增的字段中有时间类型的字段,一定要定义为DateTime64类型。其实在使用ClickHouse的过程中,我们也遇到了很多坑。文章开头的问题只是其中之一。稍后会有专门的文章与大家分享,敬请期待。