全部产品
Search
文档中心

ApsaraDB for ClickHouse:Gunakan JDBC untuk menghubungkan ke cluster ApsaraDB for ClickHouse

更新时间:Jul 02, 2025

Topik ini menjelaskan cara menggunakan Java Database Connectivity (JDBC) berbasis Java untuk menghubungkan ke cluster ApsaraDB for ClickHouse dalam proyek Maven.

Prasyarat

Prosedur

Berikut ini menjelaskan cara menggunakan JDBC untuk menghubungkan ke cluster ApsaraDB for ClickHouse dalam proyek Maven baru atau yang sudah ada. Anda juga dapat mengunduh kode contoh proyek dengan mengklik awesome-clickhouse-jdbc-0.1.0.zip. Untuk informasi lebih lanjut tentang kode contoh, lihat Deskripsi Proyek Contoh.

Langkah 1: Buat proyek Maven

Jika proyek Maven sudah ada, lewati langkah ini.

Buat proyek Maven menggunakan Eclipse atau alat IDE lainnya.

Langkah 2: Tambahkan dependensi yang diperlukan untuk driver ApsaraDB for ClickHouse

Tambahkan dependensi berikut ke file pom.xml:

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>3.4.5</version>
</dependency>
<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>clickhouse-jdbc</artifactId>
    <version>0.4.6</version>
</dependency>
<dependency>
    <groupId>org.lz4</groupId>
    <artifactId>lz4-java</artifactId>
    <version>1.8.0</version>
</dependency>

Langkah 3: Tulis kode aplikasi

Proses keseluruhan

Bagan alir berikut menunjukkan langkah-langkah untuk menggunakan JDBC untuk menghubungkan dan melakukan operasi pada cluster ApsaraDB for ClickHouse.

Parameter terkait mencakup informasi kluster dan parameter lainnya. Tabel berikut menjelaskan parameter tersebut.

Parameter

Deskripsi

Contoh

YOUR_INSTANCE_PROTOCOL

Protokol yang digunakan untuk membangun koneksi jaringan. Nilainya tetap "http".

http

YOUR_INSTANCE_ENDPOINT

Titik akhir dari kluster.

Nilainya dalam format VPC_ENDPOINT:8123. VPC_ENDPOINT menentukan VPC atau titik akhir publik dari kluster.

cc-bp128o64g****ky35-clickhouse.clickhouseserver.rds.aliyuncs.com:8123

YOUR_INSTANCE_USER

Nama pengguna akun database.

test

YOUR_INSTANCE_PASSWORD

Kata sandi akun database.

Password****

INSERT_BATCH_SIZE

Jumlah baris yang dimasukkan dalam satu batch. Unit: baris.

10000

INSERT_BATCH_NUM

Jumlah batch yang dimasukkan per thread. Unit: batch.

10

ENTERPRISE

Mesin tabel. Mesin tabel bervariasi berdasarkan edisi kluster. Nilai valid:

  • true: Enterprise Edition

  • false: Community-compatible Edition

true

INSERT_OPTIMIZE_LEVEL

Tingkat optimasi yang diterapkan selama penyisipan data. Nilai valid: 1, 2, dan 3.

Kecepatan penyisipan diurutkan dalam urutan berikut: 3, 2, 1.

3

Kode contoh lengkap

Dalam database default dari kluster Enterprise Edition, buat tabel bernama test. Kemudian, sisipkan data ke dalam tabel test secara bersamaan dalam batch. Setiap batch berisi 10.000 baris. Lakukan operasi ini total sebanyak 10 batch.

Untuk menjalankan kode, Anda harus memodifikasi parameter terkait berdasarkan kebutuhan bisnis Anda. Untuk informasi lebih lanjut tentang parameter, lihat bagian Proses Keseluruhan dari topik ini.

Logika utama dan titik masuk kode adalah metode main.

package com.aliyun;

import com.clickhouse.jdbc.ClickHouseDataSource;
import com.clickhouse.data.ClickHouseOutputStream;
import com.clickhouse.data.ClickHouseWriter;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    private final static String YOUR_INSTANCE_PROTOCOL = "http";
    private final static String YOUR_INSTANCE_ENDPOINT = "VPC_ENDPOINT:8123"; // KONFIGURASI ANDA DI SINI
    private final static String YOUR_INSTANCE_USER = "USER"; // KONFIGURASI ANDA DI SINI
    private final static String YOUR_INSTANCE_PASSWORD = "PASSWORD"; // KONFIGURASI ANDA DI SINI
    private final static String JDBC_URL = "jdbc:clickhouse:%s://%s";
    private final static Integer INSERT_BATCH_SIZE = 10000;
    private final static Integer INSERT_BATCH_NUM = 10;
    private final static boolean ENTERPRISE = true; // KONFIGURASI ANDA DI SINI
    private final static Integer INSERT_OPTIMIZE_LEVEL = 3;

    public static void main(String[] args) {
        try {
            // 1. Hubungkan ke database. 
            HikariConfig conf = buildHikariDataSource();
            try(HikariDataSource ds = new HikariDataSource(conf)) {
                Connection conn = ds.getConnection();

                // 2. Buat tabel. 
                createTable(conn);

                // 3. Sisipkan data secara bersamaan. 
                int concurrentNum = 5;
                // Mulai lima thread. 
                CountDownLatch countDownLatch = new CountDownLatch(concurrentNum);
                ExecutorService executorService = Executors.newFixedThreadPool(concurrentNum);
                for (int i = 0; i < concurrentNum; i++) {
                    executorService.submit(() -> {
                        System.out.printf("[%d] Thread mulai menyisipkan\n", Thread.currentThread().getId());
                        try {
                            //Sisipkan data. 
                            batchInsert(ds.getConnection(), INSERT_OPTIMIZE_LEVEL);
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            System.out.printf("[%d] Thread berhenti menyisipkan\n", Thread.currentThread().getId());
                            countDownLatch.countDown();
                        }
                    });
                }
                // Tunggu hingga setiap thread menyelesaikan penyisipan data. 
                countDownLatch.await();

                // 4. Lihat hasilnya. 
                count(conn);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * generate JDBC URL
     * @param protocol mendukung http, https, grpc
     * @param endpoint endpoint
     * @return JDBC URL
     */
    public static String getJdbcUrl(String protocol, String endpoint) {
        return String.format(JDBC_URL, protocol, endpoint);
    }

    /**
     * build HikariDataSource
     * @return HikariConfig
     */
    public static HikariConfig buildHikariDataSource() throws Exception {
        HikariConfig conf = new HikariConfig();

        // datasource config
        conf.setDataSource(new ClickHouseDataSource(getJdbcUrl(YOUR_INSTANCE_PROTOCOL, YOUR_INSTANCE_ENDPOINT)));
        conf.setUsername(YOUR_INSTANCE_USER);
        conf.setPassword(YOUR_INSTANCE_PASSWORD);

        // connection pool config
        conf.setMaximumPoolSize(10);
        conf.setMinimumIdle(5);
        conf.setIdleTimeout(30000);
        conf.setMaxLifetime(60000);
        conf.setConnectionTimeout(30000);
        conf.setPoolName("HikariPool");

        return conf;
    }

    /**
     * create table
     * @param conn Koneksi ClickHouse
     * @throws Exception
     */
    public static void createTable(Connection conn) throws Exception {
        if (ENTERPRISE) {
            conn.createStatement().execute("CREATE TABLE IF NOT EXISTS `default`.`test` ON CLUSTER default (id Int64, name String) ENGINE = MergeTree() ORDER BY id;");
        } else {
            // buat tabel lokal
            conn.createStatement().execute("CREATE TABLE IF NOT EXISTS `default`.`test_local` ON CLUSTER default (id Int64, name String) ENGINE = MergeTree() ORDER BY id;");
            // buat tabel terdistribusi
            conn.createStatement().execute("CREATE TABLE IF NOT EXISTS `default`.`test` ON CLUSTER default (id Int64, name String) ENGINE = Distributed(default, default, test_local, rand());");
        }
    }

    /**
 * batch insert
 * @param conn Koneksi ClickHouse
 * @param optimizeLevel tingkat optimasi penyisipan, 3 lebih cepat dari 2, 2 lebih cepat dari 1<br/>
 *                      1: insert into `default`.`test` (id, name) values(?, ?) -- dengan query tambahan untuk mendapatkan struktur tabel.
 *                         Ini portabel.<br/>
 *                      2: insert into `default`.`test` select id, name from input('id Int64, name String') -- efektif mengubah dan menyisipkan data yang dikirim ke server
 *                         dengan struktur tertentu ke tabel dengan struktur lain. Ini TIDAK portabel (terbatas pada ClickHouse).<br/>
 *                      3: insert into `default`.`test` format RowBinary -- paling cepat (mendekati Java client) dengan mode streaming tetapi memerlukan serialisasi manual dan ini
 *                         TIDAK portabel (terbatas pada ClickHouse).
 * @throws Exception
 */
    public static void batchInsert(Connection conn, int optimizeLevel) throws Exception {
        // prepared statement
        PreparedStatement preparedStatement = null;
        switch (optimizeLevel) {
            case 1:
                preparedStatement = conn.prepareStatement("insert into `default`.`test` (id, name) values(?, ?)");
                break;
            case 2:
                preparedStatement = conn.prepareStatement("insert into `default`.`test` select id, name from input('id Int64, name String')");
                break;
            case 3:
                preparedStatement = conn.prepareStatement("insert into `default`.`test` format RowBinary");
                break;
            default:
                throw new IllegalArgumentException("optimizeLevel harus 1, 2 atau 3");
        }

        // sisipkan data
        long randBase = (long) (Math.random() * 1000000); // nomor acak, mencegah duplikasi dan hilangnya data
        for (int i = 0; i < INSERT_BATCH_NUM; i++) {
            long insertStartTime = System.currentTimeMillis();
            switch (optimizeLevel) {
                case 1:
                case 2:
                    for (int j = 0; j < INSERT_BATCH_SIZE; j++) {
                        long id = (long) i * INSERT_BATCH_SIZE + j + randBase;
                        preparedStatement.setLong(1, id);
                        preparedStatement.setString(2, "name" + id);
                        preparedStatement.addBatch();
                    }
                    preparedStatement.executeBatch();
                    break;
                case 3:
                    class MyClickHouseWriter implements ClickHouseWriter {
                        int batchIndex = 0;
                        public MyClickHouseWriter(int batchIndex) {
                            this.batchIndex = batchIndex;
                        }
                        @Override
                        public void write(ClickHouseOutputStream clickHouseOutputStream) throws IOException {
                            for (int j = 0; j < INSERT_BATCH_SIZE; j++) {
                                long id = (long) batchIndex * INSERT_BATCH_SIZE + j + randBase;
                                // tulis id(Int64)
                                ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
                                buffer.order(ByteOrder.LITTLE_ENDIAN);
                                buffer.putLong(id);
                                clickHouseOutputStream.write(buffer.array());
                                // tulis nama(String)
                                clickHouseOutputStream.writeUnicodeString("name" + id);
                            }
                        }
                    }
                    preparedStatement.setObject(1, new MyClickHouseWriter(i));
                    preparedStatement.executeUpdate();
                    break;
            }

            System.out.printf("[%d] optimizeLevel=%d, sisipkan batch [%d/%d] berhasil, biaya %d ms\n",
                    Thread.currentThread().getId(), optimizeLevel, i + 1, INSERT_BATCH_NUM, System.currentTimeMillis() - insertStartTime);
        }
    }

    /**
     * hitung tabel
     * @param conn Koneksi ClickHouse
     * @throws Exception
     */
    public static void count(Connection conn) throws Exception {
        ResultSet resultSet = conn.createStatement().executeQuery("SELECT count() as cnt FROM `default`.`test`");
        if (resultSet.next()) {
            System.out.printf("tabel `default`.`test` memiliki %d baris\n", resultSet.getInt("cnt"));
        } else {
            throw new RuntimeException("gagal menghitung tabel `default`.`test`");
        }
    }
}

Deskripsi proyek contoh

Klik awesome-clickhouse-jdbc-0.1.0.zip untuk mengunduh kode contoh.

Lingkungan proyek

  • Versi Maven: 3.9.6

  • Versi JDK: 1.8

Struktur proyek

Gambar dan tabel berikut menjelaskan struktur proyek.

image

Nama file

Deskripsi

awesome-clickhouse-jdbc-0.1.0

Nama proyek.

mybatis-hikari-example

Nama subproyek.

  • Fitur:

    • Proyek menggunakan HikariCP sebagai kumpulan koneksi database dan MyBatis sebagai kerangka pemetaan objek-relasional (ORM) umum. Proyek juga menggunakan JDBC untuk mengakses lapisan persistensi database.

    • Proyek memiliki struktur lengkap, termasuk lapisan entitas (entity), lapisan akses data (mapper), dan lapisan logika bisnis (service). Struktur ini sesuai dengan pengembangan proyek aktual.

  • Skenario: Anda ingin menggunakan kerangka MyBatis untuk mengakses cluster ApsaraDB for ClickHouse.

native-example

Nama subproyek.

  • Fitur:

    • Sub-proyek menggunakan HikariCP sebagai kumpulan koneksi database dan JDBC untuk mengakses lapisan persistensi database.

    • Sub-proyek hanya memiliki satu kelas utama, dan semua logika termasuk dalam kelas tersebut.

  • Skenario: Anda ingin mempelajari cara menghubungkan ke cluster ApsaraDB for ClickHouse menggunakan JDBC atau menjalankan tes kinerja sederhana.

Catatan penggunaan

mybatis-hikari-example

Logika kode proyek sama dengan logika kode native-example. Saat membaca dan menggunakan kode, perhatikan parameter berikut dan titik masuk kode:

  • Konfigurasi parameter database: src/main/resources/application.yml

  • Titik masuk untuk membaca kode dan mengonfigurasi parameter lainnya: src/main/java/com/aliyun/Main.java

Tabel berikut menjelaskan parameter.

Titik masuk

Parameter

Deskripsi

Contoh

src/main/resources/application.yml

url

Titik akhir kluster.

Nilainya dalam format jdbc:clickhouse:http://VPC_ENDPOINT:8123. VPC_ENDPOINT menentukan VPC atau titik akhir publik kluster.

jdbc:clickhouse:http://cc-bp128o64g****ky35-clickhouse.clickhouseserver.rds.aliyuncs.com:8123

username

Nama pengguna akun database.

test

password

Kata sandi akun database.

Password****

src/main/java/com/aliyun/Main.java

INSERT_BATCH_SIZE

Jumlah data yang ingin Anda sisipkan ke dalam tabel. Unit: baris.

10000

INSERT_BATCH_NUM

Jumlah baris yang disisipkan setiap kali. Unit: baris.

10

ENTERPRISE

Mesin tabel. Nilai valid:

true: Enterprise Edition

false: Community-compatible Edition

true

INSERT_OPTIMIZE_LEVEL

Tingkat optimasi yang diterapkan selama penyisipan data. Nilai valid: 1, 2, dan 3.

Kecepatan penyisipan diurutkan dalam urutan berikut: 3, 2, 1.

3

native-example

Titik masuk untuk membaca kode dan semua konfigurasi parameter disimpan di src/main/java/com/aliyun/Main.java. Untuk informasi lebih lanjut, lihat Langkah 3: Tulis Kode Aplikasi.

Referensi

Jika Anda memerlukan alat untuk masuk ke kluster, lihat topik berikut:

FAQ

Apa yang harus saya lakukan ketika pesan kesalahan "connect timed out" muncul setelah saya memulai program?

Untuk menangani masalah ini, lakukan langkah-langkah berikut:

  1. Periksa daftar putih: Pastikan alamat IP program telah ditambahkan ke daftar putih cluster tujuan. Untuk informasi lebih lanjut, lihat Konfigurasikan Daftar Putih.

  2. Periksa jaringan:

    Periksa apakah aplikasi dan cluster tujuan berada di VPC yang sama.

  3. Periksa titik akhir yang dikonfigurasi:

    • Periksa apakah titik akhir VPC atau publik valid.

    • Periksa apakah port valid. Port default adalah 8123.