You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决ClickHouse中的“Unexpected content at the end of chunk”错误?

ClickHouse双ResultSet迭代合并时出现MalformedChunkCodingException问题

问题场景

我尝试同时迭代两个ClickHouse连接的ResultSet,对两张已排序的表做归并合并。两张表单独执行查询均正常,但同时进行迭代操作时,突然出现了从未见过的MalformedChunkCodingException错误。

相关代码

获取ClickHouse连接的工具方法

public static void execMany(Consumer<Connection> cons) {
    // ClickHouse server URL
    String url = "jdbc:clickhouse://myip:8123/db2";
    ClickHouseProperties properties = new ClickHouseProperties();
    properties.setSocketTimeout(6000000);
    properties.setSessionTimeout(6000000L);
    properties.setDatabase("db2");
    properties.setUser("default");
    properties.setPassword("12345");
    ClickHouseDataSource dataSource = new ClickHouseDataSource(url, properties);
    try (ClickHouseConnection connection = dataSource.getConnection()) {
        cons.accept(connection);
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

归并迭代逻辑代码

var ref = new Object() {
    long txCnt = 0;
    long addrCnt = 0;
};
var cnt = ClickHouse.count("transactions_tmp2");
ClickHouse.execMany(conn -> ClickHouse.execMany(conn1 -> {
    try {
        var select1 = conn.createStatement().executeQuery("SELECT * FROM %s order by %s".formatted(t1,f1));
        var select2 = conn1.createStatement().executeQuery("SELECT * FROM %s order by %s".formatted(t2,f2));
        String val2 = null;
        String val1 = null;
        while (true) {
            if (ref.txCnt % 1000000 == 0)
                System.out.println((ref.txCnt) + "/" + cnt + "=" + (1. * ref.txCnt / cnt) + " addrCnt=" + ref.addrCnt + " fromAddress=" + val2 + " txAddress=" + val1);
            if (val2 == null) {
                if (!select2.next()) break;
                ref.addrCnt++;
                val2 = select2.getString(f2);
            }
            if (val1 == null) {
                if (!select1.next()) break;
                ref.txCnt++;
                val1 = select1.getString(f1);
            }
            if (val1.compareTo(val2) < 0) {
                val1 = null;
                continue;
            }
            if (val2.compareTo(val1) < 0) {
                val2 = null;
                continue;
            }
            assert val1.equals(val2);
            cons.accept(select1,select2);
        }
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }
}));

错误堆栈信息

Exception in thread "main" java.lang.RuntimeException: java.sql.SQLException: org.apache.http.MalformedChunkCodingException: Unexpected content at the end of chunk
    at me.nanosecond.TxImporter.lambda$merge$1(TxImporter.java:123)
    at me.nanosecond.ClickHouse.execMany(ClickHouse.java:291)
    at me.nanosecond.TxImporter.lambda$merge$2(TxImporter.java:78)
    at me.nanosecond.ClickHouse.execMany(ClickHouse.java:291)
    at me.nanosecond.TxImporter.merge(TxImporter.java:78)
    at me.nanosecond.TxImporter.main(TxImporter.java:185)
Caused by: java.sql.SQLException: org.apache.http.MalformedChunkCodingException: Unexpected content at the end of chunk
    at ru.yandex.clickhouse.response.ClickHouseResultSet.hasNext(ClickHouseResultSet.java:164)
    at ru.yandex.clickhouse.response.ClickHouseResultSet.next(ClickHouseResultSet.java:200)
    at me.nanosecond.TxImporter.lambda$merge$1(TxImporter.java:90)
    ... 5 more
Caused by: org.apache.http.MalformedChunkCodingException: Unexpected content at the end of chunk
    at org.apache.http.impl.io.ChunkedInputStream.getChunkSize(ChunkedInputStream.java:254)
    at org.apache.http.impl.io.ChunkedInputStream.nextChunk(ChunkedInputStream.java:222)
    at org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:183)
    at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:135)
    at java.base/java.io.DataInputStream.read(DataInputStream.java:158)
    at ru.yandex.clickhouse.util.Utils.readFully(Utils.java:163)
    at ru.yandex.clickhouse.util.Utils.readFully(Utils.java:147)
    at ru.yandex.clickhouse.response.ClickHouseLZ4Stream.readNextBlock(ClickHouseLZ4Stream.java:102)
    at ru.yandex.clickhouse.response.ClickHouseLZ4Stream.checkNext(ClickHouseLZ4Stream.java:75)
    at ru.yandex.clickhouse.response.ClickHouseLZ4Stream.read(ClickHouseLZ4Stream.java:61)
    at ru.yandex.clickhouse.response.StreamSplitter.readFromStream(StreamSplitter.java:92)
    at ru.yandex.clickhouse.response.StreamSplitter.next(StreamSplitter.java:63)
    at ru.yandex.clickhouse.response.ClickHouseResultSet.hasNext(ClickHouseResultSet.java:149)
    ... 7 more

采用该实现方式的原因

由于ClickHouse原生对两张大排序表按主键合并时性能表现不佳,因此自行实现了基于双ResultSet的归并逻辑来完成表合并操作。

内容的提问来源于stack exchange,提问作者Stepan Yakovenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 19:28:25