Доступ к ClickHouse с помощью JDBC

в 12:17, , рубрики: big data, clickhouse, gcc-6, hibernate, java, jdbc, spring framework, sql

Привет! Не так давно я имел удовольствие посетить встречу PyData Moscow на площадке Яндекса. Я не могу назвать себя python разрабочиком, но имею интересы в области аналитики и анализа данных. Посетив данное мероприятие, я узнал о существовании СУБД ClickHouse, разработанной в Яндексе и выложенной на GitHub под открытой лицензией. Колоночная SQL СУБД с отечественными корнями пробудила во мне интерес. В этой статье я поделюсь опытом установки и настройки ClickHouse, а также попыткой доступа к ней из Spring приложения с помощью Hibernate.

Сборка и установка

Знакомство с СУБД я начал со страницы документации.

Весьма удивили рекомендации по использованию оперативной памяти. Разработки советуют использовать столько же памяти, сколько у вас данных (во всяком случае пока объем данных меньше 200 Гб). Не совсем ясно, идет ли речь о суммарной памяти всех машин в кластере или о памяти каждой машины. В любом случае, я смог довольствоваться лишь 8 гб памяти своей виртуальной машины. При этом размер тестового набора данных составлял больше 60 ГБ в несжатом виде.

Первая сложность, с которой мне пришлось стокнуться, – это рекомнедация использовать Ubuntu и остуствие rpm пакетов (UPD: позже я обнаружил вот этот репозиторий). Я предпочитаю rpm системы и принял решения собрать СУБД из исходных кодов под Centos 7.

Ребята из Яндекса приготовили нам инструкции для сборки. Здесь есть несколько любопытных моментов:

  • Нам понадобиться gcc аж 6 версии, когда в centos из коробки идет только 4.8. Любопытно, с чем связано это ограничение? Я не тратил время на изучение кода clickHouse, возможно используется новый стандарт C++, который не поддерживается в 4ой версии. Так или иначе, GCC 6 пришлось тоже собирать из исходных кодов. В инструкции по сборке clickhouse описан процесс сборки gcc, однако он опять-таки применим прежде всего к Ubuntu. Помогла следующая статья. Сборка компилятора заняла у меня 1.5 часа.
  • Второй интересной особенностью сборки оказалось зависимость clickhouse от libmysqlclient. Интересно, как и зачем в clikchouse используется функции клиента MySQL?

Сборка самой СУБД заняла порядка 40 минут. Настройка clickhouse в моем случае заключалась в правке конфигурационного /usr/local/etc/clickhouse-server/config.xml, так как я решил хранить базу данных на отдельном разделе. Запусить сервер я смог с помощью sudo /usr/local/bin/clickhouse-server --config /usr/local/etc/clickhouse-server/config.xml

Проверить работу СУБД я решил на пример из этого поста, загрузив данные об авиаперелётах в США с 1987 по 2015 год. Процесс загрузки и примеры запросов приведены выше. Загрузка у меня заняла 24 минуты, размер базы данных на диске составил 14 Гб при объеме загруженных данных в 61.6 Гб.
Время выполнения тестовых запросов из статьи выше на моей машине составило:

Запрос Время, с
какие направления были самыми популярными в 2015 году 2.067
из каких городов отправляется больше рейсов 3.744
из каких городов можно улететь по максимальному количеству направлений 7.012
как зависит задержка вылета рейсов от дня недели 3.195
из каких городов, самолёты чаще задерживаются с вылетом более чем на час 3.392
какие наиболее длинные рейсы 12.466
распределение времени задержки прилёта, по авиакомпаниям 4.596
какие авиакомпании прекратили перелёты 1.653
в какие города стали больше летать в 2015 году 0.380
перелёты в какие города больше зависят от сезонности 8.806

Еще раз отмечу, что объем памяти моей машины составлял всего 8 ГБ, что почти в 8 раз меньше, чем рекомендуется разработчиками. В связи с этим цифры выше – не более чем очень грубая прикидка того, как clickhouse будет себя вести на ноутбуке разработчика, нежели в production. В целом СУБД работала стабильно и, на мой взгляд, достаточно быстро. В анализируемой таблице было 166 миллионов записей.

Дружим ClickHouse и Java

Следующий шаг в моем изучении clickhouse – попытка доступа к нему из Java. Ребята из Яндекса выложили jdbc драйвер.

Подключить его к maven проекту очень просто:

        <dependency>
            <groupId>ru.yandex.clickhouse</groupId>
            <artifactId>clickhouse-jdbc</artifactId>
            <version>${clickhouse-jdbc-version}</version>
        </dependency>

При использовании есть всего несколько моментов, которые по незнанию могут вызвать сложности. Прежде всего, clikchouse поддерживает несколько протоколов. По-умолчанию родной бинарный протокол использует порт 9000, а http протокол – 8123. JDBC драйвер умеет работать только с http, поэтому указываем порт 8123. Название БД по-умолчиню – default. Имя пользователя и пароль по-умлочанию задавать не нужно. Чтобы clickhouse разрешал подключения с удаленных машин, в конфигурационном файле следует добавить строку <listen_host>::</listen_host>.

Далее все как в обычном JDBC:

   private static final String DB_URL = "jdbc:clickhouse://localhost:8123/default";

    private final Connection conn;

    /**
     * Creates new instance
     * @throws SQLException in case of connection issue
     */
    public ClickHouseJDBCDemo() throws SQLException {
        conn = DriverManager.getConnection(DB_URL);
    }

    /**
     * Queries db to get most popular flight route for ths given year
     * @param year year to query
     * @throws SQLException in case of query issue
     */
    public void popularYearRoutes(int year) throws SQLException {
        String query = "SELECT " +
                "    OriginCityName, " +
                "    DestCityName, " +
                "    count(*) AS flights, " +
                "    bar(flights, 0, 20000, 40) AS bar " +
                "FROM ontime WHERE Year = ? GROUP BY OriginCityName, DestCityName ORDER BY flights DESC LIMIT 20";
        long time = System.currentTimeMillis();
        try (PreparedStatement statement = conn.prepareStatement(query)) {
            statement.setInt(1, year);
            try (ResultSet rs = statement.executeQuery()) {
                Util.printRs(rs);
            }
        }
        System.out.println("Time: " + (System.currentTimeMillis() - time) +" ms");
    }

Как видим, ничего необычного. Полный код примера доступен здесь.

А что с Hibernate?

Следующим моим шагом стала попытка подключить ORM. Наивно полагая, что раз есть JDBC, то будет и ORM, я потянулся к Spring Data JPA. Но, к сожалению, я столкнулся здесь с рядом проблем. Прежде всего, при подключении к БД необходимо указать SQL Dialect. Реализованного диалекта для ClickHouse мне найти не удалось.

В результате я просто создал потомок стандартного org.hibernate.dialect.Dialect:

public class ClickHouseDialect extends Dialect {
    
}

Теперь подключится к базе данных становится возможным. Проблема начинается при выполнении запросов. Дело в том, что clickhouse не поддерживает alias для имен таблиц. Другими словами, запрос вида:

SELECT alias.column from tablename alias

для clickHouse не является допустимым.

Проблема в том, что судя по всем hibernate не позволяет отключить генерацию alias для таблиц путем определения SQL Dialect. Во всяком случае мне не удалось найти, как этого добиться.

Немного покопавшись в коде hibernate я начал приходить к выводу, что эта функциональность зашита довольно глубоко в его ядре. Впрочем, мой опыт использования hibernate не так уж велик. Буду рад, если кто-либо поделиться известным способом обойти подобное ограничение.

Итог

Исследовать clickhouse было увлекательным занятием. СУБД показала себя с хорошей стороны. Удалось собрать ее из исходных кодов, выполнить запросы с помощью консольного клиента и jdbc. К сожалению, неполная поддержка конструкций ANSI SQL не позволила легко подключить Hibernate. Впрочем, следует учитывать и назначение СУБД. Главная ниша clickhouse — сложные аналитические запросы, большинство из которых все равно придется полностью или частично писать руками. Хотя иметь возможность простого просмотра и выборки данных «из коробки» при помощи ORM было бы не лишним.

Полагаю, будет крайне интересно наблюдать за развитием проекта, особенно учитывая растущее внимание к clikchouse в сообществе последнее время и активную поддержку проекта со стороны Яндекса.

Автор: grekon

Источник


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js