Topik ini menjelaskan cara menggunakan Java Database Connectivity (JDBC) berbasis Java untuk menghubungkan ke cluster ApsaraDB for ClickHouse dalam proyek Maven.
Prasyarat
Alamat IP server tempat aplikasi diinstal telah ditambahkan ke daftar putih cluster ApsaraDB for ClickHouse. Untuk informasi lebih lanjut tentang cara mengonfigurasi daftar putih, lihat Konfigurasikan Daftar Putih IP.
CatatanJika server tempat aplikasi diinstal dan cluster tujuan ApsaraDB for ClickHouse berada di virtual private cloud (VPC) yang berbeda, Anda harus membangun koneksi jaringan. Untuk informasi lebih lanjut, lihat Bagaimana Cara Menyelesaikan Masalah Konektivitas Jaringan antara Kluster Target dan Sumber Data? Anda juga dapat menggunakan titik akhir publik untuk terhubung ke kluster. Untuk informasi lebih lanjut tentang cara mengajukan titik akhir publik, lihat Ajukan atau Lepaskan Titik Akhir Publik.
Akun database telah dibuat. Untuk informasi lebih lanjut, lihat Buat Akun.
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 |
|
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 |
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.

Nama file | Deskripsi |
awesome-clickhouse-jdbc-0.1.0 | Nama proyek. |
mybatis-hikari-example | Nama subproyek.
|
native-example | Nama subproyek.
|
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.ymlTitik masuk untuk membaca kode dan mengonfigurasi parameter lainnya:
src/main/java/com/aliyun/Main.java
Tabel berikut menjelaskan parameter.
Titik masuk | Parameter | Deskripsi | Contoh |
| url | Titik akhir kluster. Nilainya dalam format |
|
username | Nama pengguna akun database. | test | |
password | Kata sandi akun database. | Password**** | |
| 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:
Periksa daftar putih: Pastikan alamat IP program telah ditambahkan ke daftar putih cluster tujuan. Untuk informasi lebih lanjut, lihat Konfigurasikan Daftar Putih.
Periksa jaringan:
Periksa apakah aplikasi dan cluster tujuan berada di VPC yang sama.
Jika aplikasi dan cluster tujuan berada di VPC yang sama, gunakan titik akhir VPC untuk terhubung ke kluster.
Jika aplikasi dan cluster tujuan berada di VPC yang berbeda, Anda harus membangun koneksi jaringan. Untuk informasi lebih lanjut, lihat Bagaimana Cara Menyelesaikan Masalah Konektivitas Jaringan antara Kluster Target dan Sumber Data? Anda juga dapat menggunakan titik akhir publik untuk terhubung ke kluster. Untuk informasi lebih lanjut tentang cara mengajukan titik akhir publik, lihat Ajukan atau Lepaskan Titik Akhir Publik.
Periksa titik akhir yang dikonfigurasi:
Periksa apakah titik akhir VPC atau publik valid.
Periksa apakah port valid. Port default adalah 8123.