Spring Boot 接入华为 MRS ClickHouse(JDBC + 安全认证 + 负载均衡)实战
摘要:本文以真实落地项目为例,完整记录 Spring Boot 接入华为 MRS(FusionInsight)ClickHouse 集群的适配流程,覆盖定制 JDBC 驱动选型、外置配置设计、负载均衡器连接方式、安全认证(用户密码/机器用户)、分布式建表与批量插入等关键环节,并给出实测踩坑记录(含错误码)。方案已在 MRS 3.3.1 集群验证通过,可直接复用。
一、背景:MRS ClickHouse 与普通 ClickHouse 的差异
MRS 是华为云大数据平台,FusionInsight 是其私有化部署形态。MRS ClickHouse 虽然底层是 ClickHouse 引擎,但对外接入方式与社区版有明显差异,主要体现在三点:
- 统一入口是"负载均衡器"(ClickHouseBalancer),不是数据节点。业务侧连接串只指向 Balancer 的 IP,由它转发到集群内的 ClickHouseServer,因此应用无需感知分片/副本拓扑,天然具备故障转移能力;
- 安全集群强制认证。默认开启安全模式,JDBC 连接必须提供凭证------普通用户走「用户名 + 密码」,机器用户走 keytab 文件,与普通 ClickHouse 免密或单密码不同;
- 依赖必须用华为定制版 。
clickhouse-jdbc官方版本无法对接 MRS,需使用带h0.cbu.mrs后缀的定制驱动,且该驱动会传递引入与 Spring Boot 冲突的日志依赖,需要显式排除。
本文以 Spring Boot 2.7 + JDK 1.8 项目为例,给出完整可运行的接入方案。
二、连接拓扑与端口规划(先搞清连哪里)
MRS ClickHouse 的 Balancer 对外暴露多套端口,分别对应不同协议与加密方式:
| 协议 | 端口(本集群) | 用途 |
|---|---|---|
| HTTP(明文) | 21426 | JDBC/HTTP 非加密连接 |
| HTTPS | 21428 | JDBC/HTTP 加密连接(安全模式必用) |
| Native TCP | 21424 | 原生客户端非加密连接 |
| Native TCP + TLS | 21425 | 原生客户端加密连接 |
关键结论:
- JDBC(HTTP 协议)走 21426/21428,native 客户端走 21424/21425,两者不要混用;
- 安全模式(
sslUsed=true)下,代码会自动选用 HTTPS 端口,实际使用 21428。如果连接串仍写 21426,会被 Balancer 拒绝或握手失败; - 以上端口为本集群实测值,具体以 FusionInsight Manager 或服务器
ss -tlnp | grep -E '2142[4-8]'查询结果为准; - Balancer IP 可以配置多个(负载均衡列表),驱动侧逐个尝试,实现高可用。
三、pom.xml:华为定制 JDBC 驱动
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ★ MRS 定制 ClickHouse JDBC 驱动 -->
<dependency>
<groupId>com.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>0.4.5-h0.cbu.mrs.331.r10</version>
<exclusions>
<!-- ★ 排除 slf4j-api,避免与 Spring Boot 日志体系冲突 -->
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
两个关键点:
- 版本号对应 MRS 版本 :
0.4.5-h0.cbu.mrs.331.r10对应 MRS 3.3.1 配套的 ClickHouse 客户端。该制品不发布在 Maven 中央仓库,需要从 MRS 客户端配套依赖或公司 Maven 私服获取(内网地址,本文不做展开); - 必须排除
slf4j-api:定制驱动内部依赖 slf4j,不排除会与 Spring Boot 默认日志实现冲突,导致日志异常或启动报错。
四、配置文件:外置 conf 的设计
这个样例最有特色的设计是:连接参数不走 application.yml,而是放在运行目录外的 conf/clickhouse-example.properties 中,由代码主动加载。好处是部署后改集群地址/账号无需重新打包,坏处是目录结构必须正确,否则启动后请求才报文件找不到。
conf/clickhouse-example.properties 全字段:
properties
# Balancer(负载均衡器)IP 列表,多个用逗号分隔
loadBalancerIPList=<lb_ip_1>,<lb_ip_2>
# 是否启用 SSL(安全模式必须为 true)
sslUsed=true
# HTTP 明文端口(sslUsed=false 时生效)
loadBalancerHttpPort=21426
# HTTPS 端口(sslUsed=true 时生效,安全模式实测用 21428)
loadBalancerHttpsPort=21428
# 集群是否开启安全认证
CLICKHOUSE_SECURITY_ENABLED=true
# 普通用户认证(安全模式下使用)
user=<your_user>
password=<your_password>
# 是否机器用户认证(需 conf/user.keytab,与上面互斥)
isMachineUser=false
isSupportMachineUser=false
# 集群名/库名/表名(on cluster 分布式 DDL 使用)
clusterName=<cluster_name>
databaseName=<database>
tableName=<table>
# 批量插入参数:batchNum 批 × batchRows 行
batchRows=10000
batchNum=10
application.yml 只需要服务端口等无关 Redis/数据库的参数:
yaml
spring:
main:
allow-bean-definition-overriding: true
server:
port: 8084
字段选型逻辑值得注意(代码中 getProperties() 的规则):
sslUsed=true时,代码读取loadBalancerHttpsPort(21428) ;sslUsed=false才读明文端口;CLICKHOUSE_SECURITY_ENABLED=true时才会读取password,非安全集群可留空;isMachineUser=true时走 keytab 认证(需把user.keytab放到conf/目录),与用户名密码互斥。
五、核心代码解析
5.1 整体调用链
ClickHouseExampleController (/clickhouse/executeQuery)
↓
ClickHouseExampleService
↓
ClickHouseFunc # 流程编排:读配置 → 组装 LB 列表 → drop → 建库 → 建表 → 插数 → 查询
↓
Util # JDBC 执行层:建连、多 Balancer 容错重试、批量插入、结果解析
Controller、Service 均为标准 Spring 三层写法,真正的技术含量在 ClickHouseFunc(流程编排)与 Util(JDBC 执行)中。
5.2 JDBC 连接:安全 + SSL 参数
java
private static final String JDBC_PREFIX = "jdbc:clickhouse://";
static Properties clickHouseProperties = new Properties();
private static void initProperties(Properties props) {
String user = ClickHouseFunc.user;
String password = ClickHouseFunc.password == null ? "" : ClickHouseFunc.password;
try {
Class.forName("com.clickhouse.jdbc.ClickHouseDriver");
} catch (Exception e) {
log.error(e.getMessage());
}
props.setProperty(ClickHouseClientOption.CONNECTION_TIMEOUT.getKey(), "60000");
props.setProperty(ClickHouseClientOption.SOCKET_TIMEOUT.getKey(), "300000");
props.setProperty(ClickHouseClientOption.MAX_EXECUTION_TIME.getKey(), "0"); // 查询不限时
props.setProperty("user", user);
props.setProperty("password", password);
// 机器用户模式:额外指定 keytab(conf/user.keytab)
if (ClickHouseFunc.isSec && ClickHouseFunc.isMachineUser) {
props.setProperty("isMachineUser", "true");
props.setProperty("keytabPath", System.getProperty("user.dir") + File.separator + "conf" + File.separator + "user.keytab");
}
// SSL:安全模式必须开启,并关闭证书校验(内网自签证书)
if (ClickHouseFunc.sslUsed) {
props.setProperty(ClickHouseClientOption.SSL.getKey(), "true");
props.setProperty(ClickHouseClientOption.SSL_MODE.getKey(), "none");
}
}
连接时 URL 指向 Balancer (不是数据节点),实际建连串形如 jdbc:clickhouse://<ip>:21428,驱动内部会解析为 https://<ip>:21428/default(日志中可见):
java
ClickHouseDataSource dataSource =
new ClickHouseDataSource(JDBC_PREFIX + server, clickHouseProperties);
try (Connection conn = dataSource.getConnection(user, password);
PreparedStatement stmt = conn.prepareStatement(sql)) {
ResultSet rs = stmt.executeQuery();
// 解析结果...
}
5.3 多 Balancer 容错:轮询 + 指数退避
这是样例工程里最值得借鉴的部分------配置了多个 Balancer IP 时,依次尝试,失败后指数退避重试下一台,全部失败才抛异常:
java
public static List<List<String>> executeQuery(String sql) throws Exception {
List<String> serverList = ClickHouseFunc.ckLbServerList; // 形如 [ip:port, ip:port]
initProperties(clickHouseProperties);
int tries = 0;
StringBuilder errorDetails = new StringBuilder();
while (tries < serverList.size()) {
String server = serverList.get(tries);
boolean ok = execSqlOnServer(sql, server, resultList, errorDetails);
if (ok) break;
tries++;
if (tries < serverList.size()) {
long backoffMs = 1000L * (long) Math.pow(2, tries - 1); // 1s、2s、4s...
Thread.sleep(backoffMs);
}
if (tries == serverList.size()) {
throw new SQLException("Failed on all servers. Details: " + errorDetails);
}
}
return resultList;
}
5.4 集群分布式 DDL 与数据写入
MRS ClickHouse 的分布式表通常成对出现:本地表(ReplicatedMergeTree)+ 分布式表(Distributed)。样例的建表 SQL 模板:
sql
-- 建库(on cluster 全集群生效)
create database if not exists <database> on cluster <cluster_name>;
-- 本地表:ReplicatedMergeTree,{shard}/{replica} 由集群自动填充
create table if not exists <database>.<table> on cluster <cluster_name>
(name String, age UInt8, date Date)
engine = ReplicatedMergeTree('/clickhouse/tables/{shard}/<database>.<table>', '{replica}')
partition by toYYYYMM(date) order by age;
-- 分布式表(查询入口):rand() 随机分布到各分片
create table if not exists <database>.<table>_all on cluster <cluster_name>
(name String, age UInt8, date Date)
ENGINE = Distributed(<cluster_name>, <database>, <table>, rand());
批量插入用标准 PreparedStatement + 分批 executeBatch,样例参数为 batchNum × batchRows(10 × 10000 行),每批之间 sleep(1500) 防止打满服务端:
java
String insertSql = "insert into " + databaseName + "." + tableName + " values (?,?,?)";
for (int j = 0; j < batchNum; j++) {
for (int i = 0; i < batchRows; i++) {
statement.setString(1, "huawei_" + (i + j * 10));
statement.setInt(2, (int) (Math.random() * 100));
statement.setDate(3, randomDate);
statement.addBatch();
}
statement.executeBatch(); // 每批一次网络往返
Thread.sleep(1500);
}
⚠️ 注意:官方样例的 executeQuery() 是"全流程演示",每次调用都会 drop 旧表再重建(为了演示幂等)。业务项目接入时务必去掉 drop/create 步骤,只保留建连 + insert/select。
六、运行与验证
6.1 运行
两种方式任选:
- IDE 直接运行
ClickHouseApplication(注意工作目录下要有conf/clickhouse-example.properties); - 打包运行 :
mvn package(工程已配置 maven-assembly-plugin 打出jar-with-dependencies),把conf/目录放在与 jar 同级的运行目录下再java -jar。
6.2 验证
bash
curl http://localhost:8084/clickhouse/executeQuery
返回 ClickHouse springboot client runs normally. 即全部链路打通。日志(clickhouse-springboot-example.log)中会依次出现关键里程碑:
loadBalancerIPList is ..., loadBalancerHttpPort is 21428 ... ← 配置加载成功(注意是 HTTPS 端口)
Try times is 0, current load balancer is <ip>:21428. ← 正在连接 Balancer
Execute sql create database if not exists ... on cluster ..., time is ... ms
Insert batch time is ... ms ← 批量插入
之后即可查询到 name/age/date 的演示数据。
七、常见问题(实测错误码)
| 报错(含错误码) | 根因 | 解法 |
|---|---|---|
Code: 497. Not enough privileges ... grant CLUSTER ON *.* |
用户无集群级权限,on cluster 分布式 DDL 被拒 |
FusionInsight Manager 中给用户授予 CLUSTER 相关权限后重试(本项目实测遇到) |
Code: 701. Requested cluster 'default_cluster' not found |
clusterName 配错(如误写默认值 default_cluster) |
改为 Manager 中集群的真实名称(本项目实测遇到) |
| 连接被拒 / SSL 握手失败 | sslUsed=true 却连了明文 HTTP 端口 21426 |
确认使用 HTTPS 端口 21428;sslUsed=false 才用 21426 |
FileNotFoundException: conf/clickhouse-example.properties |
运行目录下缺少 conf/ 目录或文件 |
保证运行目录结构与 conf/clickhouse-example.properties 一致(与 jar 同级) |
| 日志/类加载异常 | 定制驱动传递引入 slf4j 与 Spring Boot 冲突 | pom 中排除 slf4j-api(见第三节) |
SSLHandshakeException |
内网自签证书校验失败 | 驱动设置 ssl_mode=none(仅限内网/测试环境,生产建议导入 CA 证书) |
所有 Balancer 都失败后报 SQLException: Failed to exec the sql on all n servers |
每个 LB 都被尝试且失败(原因见 errorDetails) | 查看日志中 errorDetails 的具体错误码,多为权限或端口问题 |
八、总结
MRS ClickHouse 接入的三个要点:
- 连对入口与端口:统一连 Balancer 而非数据节点;安全模式走 HTTPS 端口(本项目为 21428),协议(JDBC HTTP vs Native)与端口一一对应;
- 认证与依赖配套 :安全集群需用户名密码(或机器用户 keytab);驱动必须用
h0.cbu.mrs定制版并排除 slf4j-api; - 分布式语义 :建库建表都要带
on cluster <真实集群名>,读写入口是Distributed分布式表,clusterName配错会直接报 Code 701。
本文方案整理自已在 MRS 3.3.1 集群验证通过的示例项目(含两次真实权限/集群名踩坑),希望能帮你快速打通 MRS ClickHouse 这条接入链路。
Spring Boot 接入华为 MRS ClickHouse(JDBC + 安全认证 + 负载均衡)实战
摘要:本文以真实落地项目为例,完整记录 Spring Boot 接入华为 MRS(FusionInsight)ClickHouse 集群的适配流程,覆盖定制 JDBC 驱动选型、外置配置设计、负载均衡器连接方式、安全认证(用户密码/机器用户)、分布式建表与批量插入等关键环节,并给出实测踩坑记录(含错误码)。方案已在 MRS 3.3.1 集群验证通过,可直接复用。
一、背景:MRS ClickHouse 与普通 ClickHouse 的差异
MRS 是华为云大数据平台,FusionInsight 是其私有化部署形态。MRS ClickHouse 虽然底层是 ClickHouse 引擎,但对外接入方式与社区版有明显差异,主要体现在三点:
- 统一入口是"负载均衡器"(ClickHouseBalancer),不是数据节点。业务侧连接串只指向 Balancer 的 IP,由它转发到集群内的 ClickHouseServer,因此应用无需感知分片/副本拓扑,天然具备故障转移能力;
- 安全集群强制认证。默认开启安全模式,JDBC 连接必须提供凭证------普通用户走「用户名 + 密码」,机器用户走 keytab 文件,与普通 ClickHouse 免密或单密码不同;
- 依赖必须用华为定制版 。
clickhouse-jdbc官方版本无法对接 MRS,需使用带h0.cbu.mrs后缀的定制驱动,且该驱动会传递引入与 Spring Boot 冲突的日志依赖,需要显式排除。
本文以 Spring Boot 2.7 + JDK 1.8 项目为例,给出完整可运行的接入方案。
二、连接拓扑与端口规划(先搞清连哪里)
MRS ClickHouse 的 Balancer 对外暴露多套端口,分别对应不同协议与加密方式:
| 协议 | 端口(本集群) | 用途 |
|---|---|---|
| HTTP(明文) | 21426 | JDBC/HTTP 非加密连接 |
| HTTPS | 21428 | JDBC/HTTP 加密连接(安全模式必用) |
| Native TCP | 21424 | 原生客户端非加密连接 |
| Native TCP + TLS | 21425 | 原生客户端加密连接 |
关键结论:
- JDBC(HTTP 协议)走 21426/21428,native 客户端走 21424/21425,两者不要混用;
- 安全模式(
sslUsed=true)下,代码会自动选用 HTTPS 端口,实际使用 21428。如果连接串仍写 21426,会被 Balancer 拒绝或握手失败; - 以上端口为本集群实测值,具体以 FusionInsight Manager 或服务器
ss -tlnp | grep -E '2142[4-8]'查询结果为准; - Balancer IP 可以配置多个(负载均衡列表),驱动侧逐个尝试,实现高可用。
三、pom.xml:华为定制 JDBC 驱动
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ★ MRS 定制 ClickHouse JDBC 驱动 -->
<dependency>
<groupId>com.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>0.4.5-h0.cbu.mrs.331.r10</version>
<exclusions>
<!-- ★ 排除 slf4j-api,避免与 Spring Boot 日志体系冲突 -->
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
两个关键点:
- 版本号对应 MRS 版本 :
0.4.5-h0.cbu.mrs.331.r10对应 MRS 3.3.1 配套的 ClickHouse 客户端。该制品不发布在 Maven 中央仓库,需要从 MRS 客户端配套依赖或公司 Maven 私服获取(内网地址,本文不做展开); - 必须排除
slf4j-api:定制驱动内部依赖 slf4j,不排除会与 Spring Boot 默认日志实现冲突,导致日志异常或启动报错。
四、配置文件:外置 conf 的设计
这个样例最有特色的设计是:连接参数不走 application.yml,而是放在运行目录外的 conf/clickhouse-example.properties 中,由代码主动加载。好处是部署后改集群地址/账号无需重新打包,坏处是目录结构必须正确,否则启动后请求才报文件找不到。
conf/clickhouse-example.properties 全字段:
properties
# Balancer(负载均衡器)IP 列表,多个用逗号分隔
loadBalancerIPList=<lb_ip_1>,<lb_ip_2>
# 是否启用 SSL(安全模式必须为 true)
sslUsed=true
# HTTP 明文端口(sslUsed=false 时生效)
loadBalancerHttpPort=21426
# HTTPS 端口(sslUsed=true 时生效,安全模式实测用 21428)
loadBalancerHttpsPort=21428
# 集群是否开启安全认证
CLICKHOUSE_SECURITY_ENABLED=true
# 普通用户认证(安全模式下使用)
user=<your_user>
password=<your_password>
# 是否机器用户认证(需 conf/user.keytab,与上面互斥)
isMachineUser=false
isSupportMachineUser=false
# 集群名/库名/表名(on cluster 分布式 DDL 使用)
clusterName=<cluster_name>
databaseName=<database>
tableName=<table>
# 批量插入参数:batchNum 批 × batchRows 行
batchRows=10000
batchNum=10
application.yml 只需要服务端口等无关 Redis/数据库的参数:
yaml
spring:
main:
allow-bean-definition-overriding: true
server:
port: 8084
字段选型逻辑值得注意(代码中 getProperties() 的规则):
sslUsed=true时,代码读取loadBalancerHttpsPort(21428) ;sslUsed=false才读明文端口;CLICKHOUSE_SECURITY_ENABLED=true时才会读取password,非安全集群可留空;isMachineUser=true时走 keytab 认证(需把user.keytab放到conf/目录),与用户名密码互斥。
五、核心代码解析
5.1 整体调用链
ClickHouseExampleController (/clickhouse/executeQuery)
↓
ClickHouseExampleService
↓
ClickHouseFunc # 流程编排:读配置 → 组装 LB 列表 → drop → 建库 → 建表 → 插数 → 查询
↓
Util # JDBC 执行层:建连、多 Balancer 容错重试、批量插入、结果解析
Controller、Service 均为标准 Spring 三层写法,真正的技术含量在 ClickHouseFunc(流程编排)与 Util(JDBC 执行)中。
5.2 JDBC 连接:安全 + SSL 参数
java
private static final String JDBC_PREFIX = "jdbc:clickhouse://";
static Properties clickHouseProperties = new Properties();
private static void initProperties(Properties props) {
String user = ClickHouseFunc.user;
String password = ClickHouseFunc.password == null ? "" : ClickHouseFunc.password;
try {
Class.forName("com.clickhouse.jdbc.ClickHouseDriver");
} catch (Exception e) {
log.error(e.getMessage());
}
props.setProperty(ClickHouseClientOption.CONNECTION_TIMEOUT.getKey(), "60000");
props.setProperty(ClickHouseClientOption.SOCKET_TIMEOUT.getKey(), "300000");
props.setProperty(ClickHouseClientOption.MAX_EXECUTION_TIME.getKey(), "0"); // 查询不限时
props.setProperty("user", user);
props.setProperty("password", password);
// 机器用户模式:额外指定 keytab(conf/user.keytab)
if (ClickHouseFunc.isSec && ClickHouseFunc.isMachineUser) {
props.setProperty("isMachineUser", "true");
props.setProperty("keytabPath", System.getProperty("user.dir") + File.separator + "conf" + File.separator + "user.keytab");
}
// SSL:安全模式必须开启,并关闭证书校验(内网自签证书)
if (ClickHouseFunc.sslUsed) {
props.setProperty(ClickHouseClientOption.SSL.getKey(), "true");
props.setProperty(ClickHouseClientOption.SSL_MODE.getKey(), "none");
}
}
连接时 URL 指向 Balancer (不是数据节点),实际建连串形如 jdbc:clickhouse://<ip>:21428,驱动内部会解析为 https://<ip>:21428/default(日志中可见):
java
ClickHouseDataSource dataSource =
new ClickHouseDataSource(JDBC_PREFIX + server, clickHouseProperties);
try (Connection conn = dataSource.getConnection(user, password);
PreparedStatement stmt = conn.prepareStatement(sql)) {
ResultSet rs = stmt.executeQuery();
// 解析结果...
}
5.3 多 Balancer 容错:轮询 + 指数退避
这是样例工程里最值得借鉴的部分------配置了多个 Balancer IP 时,依次尝试,失败后指数退避重试下一台,全部失败才抛异常:
java
public static List<List<String>> executeQuery(String sql) throws Exception {
List<String> serverList = ClickHouseFunc.ckLbServerList; // 形如 [ip:port, ip:port]
initProperties(clickHouseProperties);
int tries = 0;
StringBuilder errorDetails = new StringBuilder();
while (tries < serverList.size()) {
String server = serverList.get(tries);
boolean ok = execSqlOnServer(sql, server, resultList, errorDetails);
if (ok) break;
tries++;
if (tries < serverList.size()) {
long backoffMs = 1000L * (long) Math.pow(2, tries - 1); // 1s、2s、4s...
Thread.sleep(backoffMs);
}
if (tries == serverList.size()) {
throw new SQLException("Failed on all servers. Details: " + errorDetails);
}
}
return resultList;
}
5.4 集群分布式 DDL 与数据写入
MRS ClickHouse 的分布式表通常成对出现:本地表(ReplicatedMergeTree)+ 分布式表(Distributed)。样例的建表 SQL 模板:
sql
-- 建库(on cluster 全集群生效)
create database if not exists <database> on cluster <cluster_name>;
-- 本地表:ReplicatedMergeTree,{shard}/{replica} 由集群自动填充
create table if not exists <database>.<table> on cluster <cluster_name>
(name String, age UInt8, date Date)
engine = ReplicatedMergeTree('/clickhouse/tables/{shard}/<database>.<table>', '{replica}')
partition by toYYYYMM(date) order by age;
-- 分布式表(查询入口):rand() 随机分布到各分片
create table if not exists <database>.<table>_all on cluster <cluster_name>
(name String, age UInt8, date Date)
ENGINE = Distributed(<cluster_name>, <database>, <table>, rand());
批量插入用标准 PreparedStatement + 分批 executeBatch,样例参数为 batchNum × batchRows(10 × 10000 行),每批之间 sleep(1500) 防止打满服务端:
java
String insertSql = "insert into " + databaseName + "." + tableName + " values (?,?,?)";
for (int j = 0; j < batchNum; j++) {
for (int i = 0; i < batchRows; i++) {
statement.setString(1, "huawei_" + (i + j * 10));
statement.setInt(2, (int) (Math.random() * 100));
statement.setDate(3, randomDate);
statement.addBatch();
}
statement.executeBatch(); // 每批一次网络往返
Thread.sleep(1500);
}
⚠️ 注意:官方样例的 executeQuery() 是"全流程演示",每次调用都会 drop 旧表再重建(为了演示幂等)。业务项目接入时务必去掉 drop/create 步骤,只保留建连 + insert/select。
六、运行与验证
6.1 运行
两种方式任选:
- IDE 直接运行
ClickHouseApplication(注意工作目录下要有conf/clickhouse-example.properties); - 打包运行 :
mvn package(工程已配置 maven-assembly-plugin 打出jar-with-dependencies),把conf/目录放在与 jar 同级的运行目录下再java -jar。
6.2 验证
bash
curl http://localhost:8084/clickhouse/executeQuery
返回 ClickHouse springboot client runs normally. 即全部链路打通。日志(clickhouse-springboot-example.log)中会依次出现关键里程碑:
loadBalancerIPList is ..., loadBalancerHttpPort is 21428 ... ← 配置加载成功(注意是 HTTPS 端口)
Try times is 0, current load balancer is <ip>:21428. ← 正在连接 Balancer
Execute sql create database if not exists ... on cluster ..., time is ... ms
Insert batch time is ... ms ← 批量插入
之后即可查询到 name/age/date 的演示数据。
七、常见问题(实测错误码)
| 报错(含错误码) | 根因 | 解法 |
|---|---|---|
Code: 497. Not enough privileges ... grant CLUSTER ON *.* |
用户无集群级权限,on cluster 分布式 DDL 被拒 |
FusionInsight Manager 中给用户授予 CLUSTER 相关权限后重试(本项目实测遇到) |
Code: 701. Requested cluster 'default_cluster' not found |
clusterName 配错(如误写默认值 default_cluster) |
改为 Manager 中集群的真实名称(本项目实测遇到) |
| 连接被拒 / SSL 握手失败 | sslUsed=true 却连了明文 HTTP 端口 21426 |
确认使用 HTTPS 端口 21428;sslUsed=false 才用 21426 |
FileNotFoundException: conf/clickhouse-example.properties |
运行目录下缺少 conf/ 目录或文件 |
保证运行目录结构与 conf/clickhouse-example.properties 一致(与 jar 同级) |
| 日志/类加载异常 | 定制驱动传递引入 slf4j 与 Spring Boot 冲突 | pom 中排除 slf4j-api(见第三节) |
SSLHandshakeException |
内网自签证书校验失败 | 驱动设置 ssl_mode=none(仅限内网/测试环境,生产建议导入 CA 证书) |
所有 Balancer 都失败后报 SQLException: Failed to exec the sql on all n servers |
每个 LB 都被尝试且失败(原因见 errorDetails) | 查看日志中 errorDetails 的具体错误码,多为权限或端口问题 |
八、总结
MRS ClickHouse 接入的三个要点:
- 连对入口与端口:统一连 Balancer 而非数据节点;安全模式走 HTTPS 端口(本项目为 21428),协议(JDBC HTTP vs Native)与端口一一对应;
- 认证与依赖配套 :安全集群需用户名密码(或机器用户 keytab);驱动必须用
h0.cbu.mrs定制版并排除 slf4j-api; - 分布式语义 :建库建表都要带
on cluster <真实集群名>,读写入口是Distributed分布式表,clusterName配错会直接报 Code 701。
本文方案整理自已在 MRS 3.3.1 集群验证通过的示例项目(含两次真实权限/集群名踩坑),希望能帮你快速打通 MRS ClickHouse 这条接入链路。