更新时间:2024-08-03 GMT+08:00

快速开发ClickHouse应用

ClickHouse是面向联机分析处理的列式数据库,支持SQL查询,且查询性能好,特别是基于大宽表的聚合分析查询性能非常优异,比其他分析型数据库速度快一个数量级。

ClickHouse的设计优点:

  • 数据压缩比高
  • 多核并行计算
  • 向量化计算引擎
  • 支持嵌套数据结构
  • 支持稀疏索引
  • 支持数据Insert和Update

ClickHouse的应用场景:

  • 实时数仓场景

    使用流式计算引擎(如Flink)把实时数据写入ClickHouse,借助ClickHouse的优异查询性能,在亚秒级内响应多维度、多模式的实时查询分析请求。

  • 离线查询场景

    把规模庞大的业务数据导入到ClickHouse,构造数亿至数百亿记录规模、数百以上的维度的大宽表,随时进行个性化统计和持续探索式查询分析,辅助商业决策,具有非常好的查询体验。

MRS对外提供了ClickHouse JDBC的应用开发样例工程,本实践用于指导您创建MRS集群后,获取并导入样例工程并在本地进行编译调测,用于实现MRS集群中的ClickHouse的表创建、删除以及数据的插入、查询等操作。

创建MRS ClickHouse集群

  1. 购买一个包含有ClickHouse组件的MRS集群,详情请参见购买自定义集群

    本文以购买的MRS 3.2.0-LTS.1版本的集群为例,组件包含ClickHouse组件,集群开启Kerberos认证。

  2. 单击“立即购买”,等待MRS集群创建成功。

准备应用程序认证用户

对于开启Kerberos认证的MRS集群,需提前准备具有相关组件操作权限的用户用于程序认证。

以下ClickHouse权限配置示例供参考,在实际业务场景中可根据业务需求灵活调整。

  1. 集群创建成功后,登录FusionInsight Manager。
  2. 在FusionInsight Manager界面选择系统 > 权限 > 角色 > 添加角色

    1. 填写角色的名称,例如developrole,单击“确定”保存角色。
    2. 在“配置资源权限”的表格中选择“待操作集群的名称 > ClickHouse > Clickhouse管理员权限”。

  3. 选择系统 > 权限 > 用户 > 添加用户,创建一个人机用户,例如developuser,“角色”加入developrole。

    用户创建成功后,使用该用户登录FusionInsight Manager,根据界面提示修改初始密码。

获取样例工程

  1. 通过开源镜像站获取样例工程。

    下载样例工程的Maven工程源码和配置文件,并在本地配置好相关开发工具,可参考通过开源镜像站获取样例工程

    根据集群版本选择对应的分支,下载并获取MRS相关样例工程。

    例如本章节场景对应示例为“clickhouse-examples”样例,获取地址:https://github.com/huaweicloud/huaweicloud-mrs-example/tree/mrs-3.2.0.1/src/clickhouse-examples

  2. 本地使用IDEA工具导入样例工程,等待Maven工程下载相关依赖包,具体操作可参考配置并导入样例工程

    图1 ClickHouse样例工程示例

    本地配置好Maven及SDK相关参数后,样例工程会自动加载相关依赖包。

  3. 在本示例工程中,程序通过配置文件中的IP地址信息及用户信息与ClickHouse服务端进行连接。因此工程导入完成后,需要修改样例工程的“conf”目录下的“clickhouse-example.properties”文件,根据实际环境信息修改相关参数。

    loadBalancerIPList=192.168.64.10,192.168.64.122
    sslUsed=true
    loadBalancerHttpPort=21425
    loadBalancerHttpsPort=21426
    CLICKHOUSE_SECURITY_ENABLED=true
    user=developuser
    #密码明文存储存在安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全
    password=用户密码
    isMachineUser=false
    isSupportMachineUser=false
    clusterName=default_cluster
    databaseName=testdb
    tableName=testtb
    batchRows=10000
    batchNum=10
    clickhouse_dataSource_ip_list=192.168.64.10:21426,192.168.64.122:21426
    native_dataSource_ip_list=192.168.64.10:21424,192.168.64.122:21424
    表1 配置说明表

    配置名称

    描述

    loadBalancerIPList

    ClickHouseBalancer实例的地址信息。

    登录FusionInsight Manager,选择“集群 > 服务 > ClickHouse > 实例”,可查看实例对应的IP地址。

    例如本示例中,配置为“192.168.64.10,192.168.64.122”。

    sslUsed

    是否启用ssl加密,安全模式集群配置为“true”。

    loadBalancerHttpPort

    LoadBalance的HTTP、HTTPS端口。

    登录FusionInsight Manager,选择“集群 > 服务 > ClickHouse > 逻辑集群”,查看对应逻辑集群的“HTTP Balancer端口号”中的“非加密端口”及“加密端口”。

    loadBalancerHttpsPort

    CLICKHOUSE_SECURITY_ENABLED

    ClickHouse安全模式开关。

    本示例中,配置为“true”。

    user

    准备好的开发用户的认证信息,如果是机机用户,“password”为空。

    密码明文存储存在安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全

    password

    isMachineUser

    认证用户是否为机机用户。

    isSupportMachineUser

    是否支持机机用户认证的功能,在本示例中配置为“false”。

    clusterName

    程序连接的ClickHouse逻辑集群名称,在本示例中保持默认值“default_cluster”。

    databaseName

    样例代码工程中需要创建的数据库和数据表名称,可以根据实际情况修改。

    tableName

    batchRows

    一个批次写入数据的条数,在本示例中配置为“10”。

    batchNum

    写入数据的总批次,在本示例中保持默认值。

    clickhouse_dataSource_ip_list

    ClickHouseBalancer实例的地址和HTTP连接端口信息。

    登录FusionInsight Manager,选择“集群 > 服务 > ClickHouse > 逻辑集群”,本示例为安全模式集群,因此查看对应逻辑集群的“HTTP Balancer端口号”中的“加密端口”。

    例如本示例中,配置为“192.168.64.10:21426,192.168.64.122:21426”。

    native_dataSource_ip_list

    ClickHouseBalancer实例的地址和TCP连接端口信息。

    登录FusionInsight Manager,选择“集群 > 服务 > ClickHouse > 逻辑集群”,查看对应逻辑集群的“TCP Balancer端口号”中的“非加密端口”。

    例如本示例中,配置为“192.168.64.10:21424,192.168.64.122:21424”。

  4. 本样例工程中,基于业务场景的开发思路如下,通过clickhouse-jdbc API接口来进行实现,各功能代码片段详情说明可参考开发ClickHouse应用

    • 建立连接:建立和ClickHouse服务实例的连接。

      创建连接时传入表1中配置的用户信息作为认证凭据,在服务端进行安全认证。

      clickHouseProperties.setPassword(userPass);
      clickHouseProperties.setUser(userName);
      BalancedClickhouseDataSource balancedClickhouseDataSource = new BalancedClickhouseDataSource(JDBC_PREFIX + UriList, clickHouseProperties);
    • 创建库:创建ClickHouse数据库。
      通过on cluster语句在集群中创建数据库。
      private void createDatabase(String databaseName, String clusterName) throws Exception  {    
           String createDbSql = "create database if not exists " + databaseName + " on cluster " + clusterName;    
           util.exeSql(createDbSql);
      }
    • 创建表:创建ClickHouse数据库下的表。

      通过on cluster语句在集群中创建ReplicatedMerge表和Distributed表。

      private void createTable(String databaseName, String tableName, String clusterName) throws Exception { 
         String createSql = "create table " + databaseName + "." + tableName + " on cluster " + clusterName  + " (name String, age UInt8, date Date)engine=ReplicatedMergeTree('/clickhouse/tables/{shard}/" + databaseName + "." + tableName + "'," + "'{replica}') partition by toYYYYMM(date) order by age"; 
         String createDisSql = "create table " + databaseName + "." + tableName + "_all" + " on cluster " + clusterName + " as " + databaseName + "." + tableName + " ENGINE = Distributed(default_cluster," + databaseName + "," + tableName + ", rand());";    ArrayList<String> sqlList = new ArrayList<String>();   
         sqlList.add(createSql);   
         sqlList.add(createDisSql);  
         util.exeSql(sqlList);
      }
    • 插入数据:插入数据到ClickHouse表中。
      向创建的表中插入数据,本示例创建的表具有三个字段,分别是String、UInt8和Date类型。
      String insertSql = "insert into " + databaseName + "." + tableName + " values (?,?,?)";
      PreparedStatement preparedStatement = connection.prepareStatement(insertSql);
      long allBatchBegin = System.currentTimeMillis();
      for (int j = 0; j < batchNum; j++) {
          for (int i = 0; i < batchRows; i++) { 
             preparedStatement.setString(1, "huawei_" + (i + j * 10));
             preparedStatement.setInt(2, ((int) (Math.random() * 100)));
             preparedStatement.setDate(3, generateRandomDate("2018-01-01", "2021-12-31")); 
             preparedStatement.addBatch();
          }
         long begin = System.currentTimeMillis();
         preparedStatement.executeBatch();    
         long end = System.currentTimeMillis();
         log.info("Inert batch time is {} ms", end - begin);
      }
      long allBatchEnd = System.currentTimeMillis();
      log.info("Inert all batch time is {} ms", allBatchEnd - allBatchBegin);

编译并运行程序

本地和MRS集群网络互通时,可以直接在本地进行调测运行。

  1. 在开发环境IntelliJ IDEA工程“clickhouse-examples”中单击“Run 'Demo'”运行应用程序工程。

    图2 运行ClickHouse Demo程序

  2. 控制台显示部分运行结果如下,可以看到ClickHouse表创建成功并插入数据。

    ...
    2023-06-03 11:30:27,127 | INFO  | main | Execute query:create table testdb.testtb on cluster default_cluster (name String, age UInt8, date Date)engine=ReplicatedMergeTree('/clickhouse/tables/{shard}/testdb.testtb','{replica}') partition by toYYYYMM(date) order by age | com.huawei.clickhouse.examples.Util.exeSql(Util.java:68)
    2023-06-03 11:30:27,412 | INFO  | main | Execute time is 284 ms | com.huawei.clickhouse.examples.Util.exeSql(Util.java:72)
    2023-06-03 11:30:27,412 | INFO  | main | Current load balancer is 192.168.64.10:21426 | com.huawei.clickhouse.examples.Util.exeSql(Util.java:63)
    2023-06-03 11:30:28,426 | INFO  | main | Execute query:create table testdb.testtb_all on cluster default_cluster as testdb.testtb ENGINE = Distributed(default_cluster,testdb,testtb, rand()); | com.huawei.clickhouse.examples.Util.exeSql(Util.java:68)
    2023-06-03 11:30:28,686 | INFO  | main | Execute time is 259 ms | com.huawei.clickhouse.examples.Util.exeSql(Util.java:72)
    2023-06-03 11:30:28,686 | INFO  | main | Current load balancer is 192.168.64.10:21426 | com.huawei.clickhouse.examples.Util.insertData(Util.java:137)
    2023-06-03 11:30:29,784 | INFO  | main | Insert batch time is 227 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:154)
    2023-06-03 11:30:31,490 | INFO  | main | Insert batch time is 200 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:154)
    2023-06-03 11:30:33,337 | INFO  | main | Insert batch time is 335 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:154)
    2023-06-03 11:30:35,295 | INFO  | main | Insert batch time is 454 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:154)
    2023-06-03 11:30:37,077 | INFO  | main | Insert batch time is 275 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:154)
    2023-06-03 11:30:38,811 | INFO  | main | Insert batch time is 218 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:154)
    2023-06-03 11:30:40,468 | INFO  | main | Insert batch time is 144 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:154)
    2023-06-03 11:30:42,216 | INFO  | main | Insert batch time is 238 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:154)
    2023-06-03 11:30:43,977 | INFO  | main | Insert batch time is 257 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:154)
    2023-06-03 11:30:45,756 | INFO  | main | Insert batch time is 277 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:154)
    2023-06-03 11:30:47,270 | INFO  | main | Inert all batch time is 17720 ms | com.huawei.clickhouse.examples.Util.insertData(Util.java:158)
    2023-06-03 11:30:47,271 | INFO  | main | Current load balancer is 192.168.64.10:21426 | com.huawei.clickhouse.examples.Util.exeSql(Util.java:63)
    2023-06-03 11:30:47,828 | INFO  | main | Execute query:select * from testdb.testtb_all order by age limit 10 | com.huawei.clickhouse.examples.Util.exeSql(Util.java:68)
    2023-06-03 11:30:47,917 | INFO  | main | Execute time is 89 ms | com.huawei.clickhouse.examples.Util.exeSql(Util.java:72)
    2023-06-03 11:30:47,918 | INFO  | main | Current load balancer is 192.168.64.10:21426 | com.huawei.clickhouse.examples.Util.exeSql(Util.java:63)
    2023-06-03 11:30:48,580 | INFO  | main | Execute query:select toYYYYMM(date),count(1) from testdb.testtb_all group by toYYYYMM(date) order by count(1) DESC limit 10 | com.huawei.clickhouse.examples.Util.exeSql(Util.java:68)
    2023-06-03 11:30:48,680 | INFO  | main | Execute time is 99 ms | com.huawei.clickhouse.examples.Util.exeSql(Util.java:72)
    2023-06-03 11:30:48,682 | INFO  | main | name	age	date	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,682 | INFO  | main | huawei_89	3	2021-02-21	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,682 | INFO  | main | huawei_81	3	2020-05-27	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,682 | INFO  | main | huawei_70	4	2021-10-28	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,682 | INFO  | main | huawei_73	4	2020-03-23	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,683 | INFO  | main | huawei_44	5	2020-12-10	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,683 | INFO  | main | huawei_29	6	2021-10-12	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,683 | INFO  | main | huawei_74	6	2021-03-03	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,683 | INFO  | main | huawei_38	7	2020-05-30	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,683 | INFO  | main | huawei_57	8	2020-09-27	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,683 | INFO  | main | huawei_23	8	2020-08-08	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,683 | INFO  | main | toYYYYMM(date)	count()	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,684 | INFO  | main | 202005	8	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,684 | INFO  | main | 202007	7	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,684 | INFO  | main | 202004	6	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,684 | INFO  | main | 202009	6	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,684 | INFO  | main | 202103	6	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,685 | INFO  | main | 202012	6	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,685 | INFO  | main | 202010	5	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,685 | INFO  | main | 202112	5	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,685 | INFO  | main | 202003	5	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,685 | INFO  | main | 202104	4	 | com.huawei.clickhouse.examples.Demo.queryData(Demo.java:159)
    2023-06-03 11:30:48,689 | INFO  | main | Use HA module. | ru.yandex.clickhouse.BalancedClickhouseDataSource.<init>(BalancedClickhouseDataSource.java:122)
    2023-06-03 11:30:51,651 | INFO  | main | Name is: huawei_89, age is: 3 | com.huawei.clickhouse.examples.ClickhouseJDBCHaDemo.queryData(ClickhouseJDBCHaDemo.java:73)
    2023-06-03 11:30:51,651 | INFO  | main | Name is: huawei_81, age is: 3 | com.huawei.clickhouse.examples.ClickhouseJDBCHaDemo.queryData(ClickhouseJDBCHaDemo.java:73)
    2023-06-03 11:30:51,651 | INFO  | main | Name is: huawei_70, age is: 4 | com.huawei.clickhouse.examples.ClickhouseJDBCHaDemo.queryData(ClickhouseJDBCHaDemo.java:73)
    2023-06-03 11:30:51,651 | INFO  | main | Name is: huawei_73, age is: 4 | com.huawei.clickhouse.examples.ClickhouseJDBCHaDemo.queryData(ClickhouseJDBCHaDemo.java:73)
    2023-06-03 11:30:51,652 | INFO  | main | Name is: huawei_44, age is: 5 | com.huawei.clickhouse.examples.ClickhouseJDBCHaDemo.queryData(ClickhouseJDBCHaDemo.java:73)
    2023-06-03 11:30:51,652 | INFO  | main | Name is: huawei_29, age is: 6 | com.huawei.clickhouse.examples.ClickhouseJDBCHaDemo.queryData(ClickhouseJDBCHaDemo.java:73)
    2023-06-03 11:30:51,652 | INFO  | main | Name is: huawei_74, age is: 6 | com.huawei.clickhouse.examples.ClickhouseJDBCHaDemo.queryData(ClickhouseJDBCHaDemo.java:73)
    2023-06-03 11:30:51,652 | INFO  | main | Name is: huawei_38, age is: 7 | com.huawei.clickhouse.examples.ClickhouseJDBCHaDemo.queryData(ClickhouseJDBCHaDemo.java:73)
    2023-06-03 11:30:51,654 | INFO  | main | Name is: huawei_57, age is: 8 | com.huawei.clickhouse.examples.ClickhouseJDBCHaDemo.queryData(ClickhouseJDBCHaDemo.java:73)
    2023-06-03 11:30:51,654 | INFO  | main | Name is: huawei_23, age is: 8 | com.huawei.clickhouse.examples.ClickhouseJDBCHaDemo.queryData(ClickhouseJDBCHaDemo.java:73)
    ...

  3. 安装MRS集群客户端,登录ClickHouse客户端。

    例如客户端安装目录为“/opt/client”,以客户端安装用户,登录安装客户端的节点。

    cd /opt/client

    source bigdata_env

    kinit developuser

  4. 使用clickhouse client命令连接ClickHouse服务端:

    clickhouse client --host ClickHouseServer的实例IP --port 连接端口 --secure

    ClickHouse的实例IP地址可登录集群FusionInsight Manager,然后选择“集群 > 服务 > ClickHouse > 实例”,获取ClickHouseServer实例对应的业务IP地址。连接端口可通过ClickHouse服务配置中搜索“tcp_port_secure”参数获取。

    例如执行命令如下:

    clickhouse client --host 192.168.64.10 --port 21427 --secure

  5. 执行以下命令,查看程序创建的数据表内容。

    select * from testdb.testtb;

    ┌─name──────┬─age─┬───────date─┐
    │ huawei_70 │   4 │ 2021-10-28 │
    │ huawei_29 │   6 │ 2021-10-12 │
    │ huawei_16 │  28 │ 2021-10-04 │
    │ huawei_15 │  29 │ 2021-10-03 │
    └───────────┴─────┴────────────┘
    ...