暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java 客户端…续篇来袭?!

ClickHouseInc 2024-07-09
184

本文字数:4187估计阅读时间:11 分钟


Meetup活动


ClickHouse Shanghai User Group第1届 Meetup火热报名中,详见文末海报!


ClickHouse 是一个快速、强大且功能齐全的数据库,需要一整套语言客户端生态系统来发挥其最大潜力。作为最受欢迎的集成之一,ClickHouse Java 客户端提供了一种无缝且高效的方式,让 Java 应用程序能够与 ClickHouse 数据库进行交互。它通过提供熟悉且直观的界面,提高了开发者的生产力,简化了数据库操作,促进了更高效的开发过程。

近十年来,该客户端的广泛使用证明了社区成员和 ClickHouse 团队成员的高质量贡献。他们的协作推动了创新,并有机地扩展了功能。然而,随着时间的推移,任何庞大的软件包都会面临一些关键挑战,需要解决这些挑战以改善整体开发体验,并为未来的扩展做好准备。

在这篇博客文章中,我们将介绍 ClickHouse Java 项目的重构工作以及其背后的动机。同时,您已经可以在我们的存储库中查看早期的 alpha 版本。


我们发现的问题

我们听到了用户的声音,也意识到在 V1 中开发者的整体体验存在挑战——这些问题我们不仅听到了,也亲身经历过!


简单操作的过度复杂。

目前,使用客户端进行简单操作需要过多的样板代码和不直观的编码方式。为了增强灵活性,多年来添加了许多抽象层。然而,我们意识到并非每个用户都需要或希望理解这些抽象层来执行简单的查询。新的 API 旨在提供直观且易于理解的接口,具有合理的默认设置,使用户无需深入了解客户端源代码即可开始项目。


数据插入和检索的复杂性。

使用 Java 客户端插入或检索数据需要对 ClickHouse 数据格式有深入的理解。Java 客户端支持 JSON 和 RowBinary 数据格式进行插入操作。对于有严格延迟要求的应用,更紧凑且开销较小的 RowBinary 格式是首选。

然而,目前的实现要求用户在应用中管理 RowBinary 的序列化和反序列化。尽管客户端提供了一些辅助工具,但编写 RowBinary 和 RowBinaryWithDefault 的过程复杂且容易出错,特别是在处理复杂类型如嵌套数组和映射时。新版客户端通过提供由解析数据对象生成的序列化器解决了这个问题。用户只需注册这些对象类,其余工作由客户端处理。


不安全的低级优化

某些低级优化,如响应对象重用,可能会在您的应用程序中引入意料之外的潜在危险副作用。我们暂时禁用了这些“优化”功能,所有查询结果将保持不可变,并采用惰性反序列化以减少内存占用。

我们正在讨论未来是否采用选择性启用的方式,让用户在对内存和 CPU 效率有高要求时激活这些优化功能。如果您对此有任何想法,请务必告诉我们!


新客户端 API 目标

我们希望通过以下方式解决这些问题,让用户能够“立即开始使用客户端”:


直观的 API

这是我们的重要目标,因为现有客户端的复杂性太高,包含了许多层次和抽象,用户很难确定在何处使用何种方法。我们希望简化整个过程,使操作更加顺畅。


改进文档

文档常常是开发人员的难题——它几乎从未完全符合你的需求,并且随着底层代码的变化容易过时。我们希望确保 API 是自文档化的,这样所需参数一目了然(无需查找文档),同时也让可选参数更加明显。

这还包括更多的实现示例——随着 API 范围的缩小,新示例代码将更快地帮助用户上手!


清理代码库

我相信许多开发人员都有这样的经历,代码库存在的时间越长,积累的历史包袱就越多。过去的 8 年里,我们已经弃用了部分客户端代码,但现在是时候彻底清理这些代码,并引入新的替代代码了。我们相信,从长远来看,这将使贡献变得更加轻松。


立即开始尝试吧!

设置

创建客户端对象采用构建器风格。所有设置都通过方法设定,然后在内部进行验证:

    Client.Builder clientBuilder = new Client.Builder()
    .addEndpoint(endpoint)
    .setUsername(user)
    .setPassword(password)
    .compressServerResponse(true)
    .setDefaultDatabase(database);


    this.client = clientBuilder.build();

    插入 POJO(简单 Java 对象)

    以下是如何使用示例 POJO 和新的序列化 API 插入数据的示例(请确保你有 getters 方法):

      public class ArticleViewEvent {
      private Double postId;
      private LocalDateTime viewTime;
      private String clientId;
      }

      在使用插入方法之前,我们需要先注册:

        client.register(ArticleViewEvent.class, client.getTableSchema(TABLE_NAME));

        register 方法的第二个参数是表架构,可以是精确的表架构,也可以是自定义的架构。

        最后,只需将 ArticleViewEvent 对象的集合传递给插入方法:

          ArrayList<ArticleViewEvent> events = … ; // filled collection
          client.insert(TABLE_NAME, events, new InsertSettings());

          新的客户端会选择最有效的格式,并将数据传输到服务器。


          从流中插入数据

          在某些情况下,数据已经是 ClickHouse 支持的格式,例如 JSONEachRow。在这种情况下,不需要注册任何内容,只需将数据作为 InputStream 传递即可:

            public void insertData_JSONEachRowFormat(InputStream inputStream) {
            try {
            InsertResponse response = client.insert(TABLE_NAME,
            inputStream,
            ClickHouseFormat.JSONEachRow)
            .get(3, TimeUnit.SECONDS);


            log.info("Insert finished: {} rows written",
            response.getWrittenRows());






            } catch (Exception e) {
            log.error("Failed to write JSONEachRow data", e);
            throw new RuntimeException(e);
            }
            }

            读取数据

            可以将 ClickHouse 中的数据读取为记录集合,或者进行迭代。我们已经实现了 RowBinary 和 Native 格式的读取器,方便用户使用,同时也可以读取原始数据。更多的格式读取器也将推出,例如 CSV 和 TSV 等常见格式。

            以下是从结果集中获取第一条记录的示例:

              GenericRecord hostnameRecord = client
              .queryAll("SELECT hostname()")
              .stream()
              .findFirst()
              .get();

              或者获取整个集合:

                   List<GenericRecord> records = client.queryAll(
                "SELECT col1, col2, col3 FROM " + DATASET_TABLE
                );




                for (GenericRecord record : records) {
                record.getString(3); // string column col3
                }

                如果需要更多的响应信息,com.clickhouse.client.api.Client#queryRecords 将非常方便。它返回一个可迭代的 com.clickhouse.client.api.query.Records 对象,并且提供了底层服务器响应的接口。

                      Records records = client.queryRecords(
                  "SELECT col1, col2, col3 FROM " + DATASET_TABLE
                  ).get(3, TimeUnit.SECONDS);
                  System.out.println("Result rows: " + records.getResultRows());




                  for (GenericRecord record : records) {
                  record.getString(3); // string column col3
                  }


                  接下来的步骤

                  在未来几周或几个月内,我们将重构部分底层客户端代码,并扩展 v2 代码,因此我们非常希望您能进行尝试!获取 0.6.1+ 版本(包含早期 alpha 版本)的 Java 客户端,并开始探索 client-v2 —— 实际使用是最好的测试方式。


                  我可以在哪里提问或报告问题?

                  很高兴您问这个问题!最好的方式是在我们的 GitHub 存储库中使用“v2-feedback”标签创建一个问题,但您也可以在社区的 Slack 平台上找到我们。如果您有任何问题、意见或疑虑,请务必告诉我们——我们非常期待您的反馈!


                  旧版客户端怎么办?

                  我们将继续支持旧版(v1)客户端,提供安全更新、错误修复和小幅增强,直到 2025 年底。但不出意料,我们未来的重点将是新客户端。

                  另见:

                  使用 V2 的 Java 示例应用程序 【https://github.com/ClickHouse/clickhouse-java/tree/main/examples/client-v2】



                  最近 ClickHouse 官方首次推出了 ClickHouse 认证开发人员(点击查看详情)的考试。提升您的职业生涯至行业顶尖水平!报名参加我们的官方 ClickHouse 认证考试,证明您的 ClickHouse 专业技能。

                  提示:我们正在开通国内购买渠道,请大家积极填写认证考试意愿到场问卷,敬请期待

                  Meetup 活动报名通知

                  好消息:ClickHouse Shanghai User Group第1届 Meetup 已经开放报名,将于2024年7月20日在上海市徐汇区龙耀路8号阿里巴巴徐汇滨江园区X6-304 梅溪书院举行,扫码免费报名


                  征稿启示

                  面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com

                  文章转载自ClickHouseInc,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                  评论