Заметки об исследовании бумаги Apache Calcite

Java задняя часть

В последнее время я обращаю внимание на технологии обработки больших данных и внедрение продуктов с открытым исходным кодом, и обнаружил, что во многих проектах упоминается штука под названием Apache Calcite. Неудивительно видеть одно и то же один или два раза, но это необходимо замечать, когда оно неоднократно упоминается в продуктах разных периодов в области обработки данных. С этой целью я также искал некоторую информацию о введении этой вещи.Статья, опубликованная в SIGMOD в 2018 году.Я думаю, что это наиболее подходящий для начала.Следующее - мои мысли и резюме этой статьи.

что

Чтобы объяснить, что такое кальцит, наиболее подходящим названием статьи является «Основополагающая структура для оптимизированной обработки запросов к разнородным источникам данных». Calcite предоставляет стандартный язык SQL, различные оптимизации запросов и возможность подключения к различным источникам данных. С функциональной точки зрения он имеет много типичных функций систем управления базами данных, таких как анализ SQL, проверка SQL, оптимизация запросов SQL, генерация SQL, запрос на подключение данных и т. д., но он не включает основные функции СУБД, такие как как обработка данных и хранение данных. С другой стороны, независимый дизайн обработки и хранения данных Calcite делает его отличным выбором для координации между несколькими источниками данных и механизмами обработки данных.

Calcite ранее назывался optiq, и optiq изначально использовался в проекте Apache Hive для обеспечения стоимостной оптимизации Hive, а именно CBO (стоимостная оптимизация). В мае 2014 года optiq стал независимым и стал инкубационным проектом сообщества Apache, а в сентябре 2014 года был официально переименован в Calcite. Цель этого проекта — универсальность (одно решение подходит для всех сценариев спроса) с надеждой на предоставление унифицированного механизма запросов для различных вычислительных платформ и источников данных.

Основной функцией Calcite является разбор синтаксиса SQL (parse) и оптимизация (optimazation). Во-первых, он анализирует инструкцию SQL в абстрактное синтаксическое дерево (Абстрактное синтаксическое дерево AST), оптимизирует алгоритм и взаимосвязь AST на основе определенных правил или затрат и, наконец, передает его каждому механизму обработки данных для выполнения.

Почему

Следующий вопрос, зачем нам такая библиотека для разбора и оптимизации SQL?

Если вы планируете разработать продукт для распределенных вычислений самостоятельно, у вас должны быть такие функции, как синтаксический анализ и выполнение SQL, однако существуют определенные технические пороги для реализации таких функций, которые требуют от разработчиков глубокого понимания реляционной алгебры и других областей. Результаты синтаксического анализа SQL также должны максимально соответствовать основному стандарту ANSI-SQL, что также может снизить затраты компании на продвижение и затраты на обучение пользователей. Кроме того, в сценарии распределенных вычислений в эпоху обработки больших данных один SQL часто можно разбить на несколько семантически эквивалентных синтаксических деревьев, но с учетом логики операций, таких как различные структуры данных, масштабы базовой обработки данных и внутренние фильтрующие соединения, конкретная эффективность выполнения между этими синтаксическими деревьями часто сильно различается, операторы SQL отличаются, базовая среда выполнения отличается, и существующие плюсы и минусы также различаются.

Следовательно, как оптимизировать путь выполнения этих синтаксических деревьев — очень важная тема. По этим двум пунктам существуют более или менее общие проблемы в области пакетных вычислений, потоковых вычислений и интерактивных запросов при обработке больших данных.Можно создать общую структуру.

Если вы являетесь пользователем данных, вы можете столкнуться с несколькими разнородными источниками данных, которые необходимо интегрировать (традиционные реляционные базы данных, поисковые системы, такие как ES, продукты кэширования, такие как MongoDB, распределенные вычислительные среды, такие как Spark и т. д.). - Платформа распределения оператора запроса и оптимизация выполнения.

должность

Так появился Apache Calcite, который в статье позиционируется как полноценная система обработки запросов, но конструкция Calcite очень гибкая, и есть два варианта его использования в реальных проектах:

  1. Рассматривайте Calcite как библиотечную библиотеку и встраивайте ее в свой собственный проект.

    把 Calcite 的自己产品的系统列表

  2. Внедрите адаптер, и проект интегрируется с Calcite через адаптер, который считывает источник данных.

    采用 Calcite 适配器的系统列表

функциональная агрегация

DBMS 五部分

Вообще говоря, мы можем разделить систему управления базами данных на пять вышеуказанных частей.В начале своего проектирования Calcite решил сосредоточиться и реализовать только три синие части на рисунке, а серые управление данными и хранение данных открыть для внешних вычисления. , механизм хранения для достижения. Цель этого состоит в том, что характеристики самих данных делают части управления данными и хранения данных разнообразными и сложными (файлы, реляционные базы данных, колоночные базы данных, распределенное хранилище и т. д.) Calcite отказывается от этих двух частей и фокусируется на более общий верхний уровень.Модули могут эффективно управлять сложностью системы и фокусироваться на областях, которые вы можете делать, можете делать и можете делать глубже и лучше.

Calcite не повторяет создание колес, а использует готовые вещи, когда они доступны.Например, в части парсинга SQL JavaCC с открытым исходным кодом напрямую используется для преобразования операторов SQL в код Java, а затем в абстрактный синтаксис дерево для следующего этапа использования. Другим примером является то, что для реализации гибких функций метаданных Calcite должен поддерживать компиляцию Java-кода во время выполнения, а JavaC по умолчанию слишком тяжелый и требует более легкого компилятора.Здесь используется Janino с открытым исходным кодом.

Эта функциональная направленность, отсутствие необходимости изобретать велосипед и достаточно простые идеи дизайна продукта делают Calcite простым и достаточно стабильным для реализации.

Гибкая подключаемая архитектура

Calcite 架构

На приведенном выше рисунке показана архитектура Calcite, упомянутая в документе.Оптимизатор Calcite использует реляционное дерево операторов в качестве внутреннего представления, а его внутренний механизм оптимизации в основном состоит из трех компонентов: правил, поставщиков метаданных и механизмов планирования. Пунктирная линия на рисунке представляет собой взаимодействие кальцита с внешней средой, и из рисунка видно, что существует множество способов этого взаимодействия.

Верхний клиент JDBC на рисунке представляет собой внешнее приложение, которое обычно вводится в виде операторов SQL при доступе и обращается к серверу JDBC внутри Calcite через клиент JDBC. Затем сервер JDBC проанализирует и проверит входящий оператор SQL с помощью модуля синтаксического анализатора и валидатора SQL, а расположенный рядом с ним построитель выражений используется для поддержки структуры Calcite для анализа и проверки SQL. Затем идет модуль «Выражения оператора» для обработки реляционных выражений, поставщики метаданных для поддержки внешних настраиваемых метаданных, подключаемые правила для определения правил оптимизации и основной оптимизатор запросов для оптимизации запросов.

Calcite включает внутренний синтаксический анализатор запросов и средство проверки, которые преобразуют SQL-запросы в деревья реляционных операторов. Поскольку Calcite не содержит уровня хранения данных, он предоставляет механизм для определения таблиц и представлений во внешних механизмах хранения через адаптеры, поэтому Calcite можно использовать поверх этих механизмов хранения. Calcite может обеспечить оптимизацию SQL не только для систем, поддерживаемых языком базы данных, но и для систем, которые уже имеют собственный язык синтаксического анализа и интерпретации.

Поскольку разделение функциональных модулей является относительно независимым и разумным, Calcite не нужно интегрировать их все, он позволяет вам интегрировать и использовать только некоторые функции. По сути, каждый модуль также поддерживает настройку, что позволяет пользователям добиться более гибкой настройки функций.

Как это сделать

Вообще говоря, Calcite анализирует SQL со следующими шагами: 1. Parser, Calcite анализирует SQL в непроверенный AST через Java CC 2. Проверка. Основная функция этого шага — проверить, является ли AST на предыдущем шаге допустимым, например, проверить, существуют ли схема SQL, поле, функция и т. д., допустим ли оператор SQL и т. д. После этого шаг завершен, RelNode сгенерирован Tree 3. Оптимизировать. Основная функция этого шага — оптимизировать дерево RelNode и преобразовать его в физический план выполнения. Обычно используются два типа оптимизации правил SQL: оптимизация на основе правил (RBO) и оптимизация на основе затрат (CBO). Этот шаг в принципе необязателен. Дерево RelNode после проверки может фактически напрямую преобразовать план физического выполнения. Но современный Парсеры SQL в основном имеют этот шаг для оптимизации плана выполнения SQL. Результатом этого шага является план физического выполнения. 4. Выполнение. Основная цель этого шага — преобразовать план физического выполнения в программу, которую можно выполнить на определенной платформе. Например, и Hive, и Flink на этом этапе генерируют соответствующий исполняемый код из плана физического выполнения CodeGen.

Ниже приведен пример демо-запроса Calcite.Мы имитируем SQL для написания оператора запроса, но внутреннее хранилище данных не использует какую-либо БД, а использует данные, хранящиеся в памяти JVM. Этот пример может дать интуитивное представление о простом использовании кальцита.

импорт maven

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.study.calcite</groupId>
    <artifactId>demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--calcite 核心-->
        <dependency>
            <groupId>org.apache.calcite</groupId>
            <artifactId>calcite-core</artifactId>
            <version>1.19.0</version>
        </dependency>

    </dependencies>
</project>

Определите структуру схемы

Определите структуру Schema, чтобы представить, как выглядит структура хранения данных.В примере определена схема с именем JavaHrSchema, которую можно сравнить с экземпляром БД в базе данных. В этой схеме есть две таблицы, «Сотрудник» и «Отдел», которые можно понимать как таблицы в базе данных.Наконец пример инициализирует некоторые данные для этих двух таблиц в памяти.

package org.study.calcite.demo.inmemory;

/**
 * 定义 Schema 结构
 *
 * @author niwei
 */
public class JavaHrSchema {

    public static class Employee {
        public final int emp_id;
        public final String name;
        public final int dept_no;

        public Employee(int emp_id, String name, int dept_no) {
            this.emp_id = emp_id;
            this.name = name;
            this.dept_no = dept_no;
        }
    }

    public static class Department {
        public final String name;
        public final int dept_no;

        public Department(int dept_no, String name) {
            this.dept_no = dept_no;
            this.name = name;
        }
    }

    public final Employee[] employee = {
            new Employee(100, "joe", 1),
            new Employee(200, "oliver", 2),
            new Employee(300, "twist", 1),
            new Employee(301, "king", 3),
            new Employee(305, "kelly", 1)
    };

    public final Department[] department = {
            new Department(1, "dev"),
            new Department(2, "market"),
            new Department(3, "test")
    };
}

Пример кода Java

Следующим шагом является написание оператора SQL и его выполнение.Предпосылка выполнения этих действий состоит в том, чтобы сообщить Calcite определение схемы и таблицы, которые будут работать в настоящее время, что требует добавления источника данных в Calcite. Судя по API, предоставляемому Calcite, он на самом деле очень похож на код доступа к базе данных в JDBC. Студенты, написавшие этот код, должны быть хорошо знакомы с ним, поэтому я не буду представлять их по одному.

package org.study.calcite.demo.inmemory;

import org.apache.calcite.adapter.java.ReflectiveSchema;
import org.apache.calcite.jdbc.CalciteConnection;
import org.apache.calcite.schema.SchemaPlus;

import java.sql.*;
import java.util.Properties;

public class QueryDemo {
    public static void main(String[] args) throws Exception {
        Class.forName("org.apache.calcite.jdbc.Driver");
        Properties info = new Properties();
        info.setProperty("lex", "JAVA");
        Connection connection = DriverManager.getConnection("jdbc:calcite:", info);
        CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class);

        SchemaPlus rootSchema = calciteConnection.getRootSchema();
        /**
         * 注册一个对象作为 schema ,通过反射读取 JavaHrSchema 对象内部结构,将其属性 employee 和 department 作为表
         */
        rootSchema.add("hr", new ReflectiveSchema(new JavaHrSchema()));
        Statement statement = calciteConnection.createStatement();
        ResultSet resultSet = statement.executeQuery(
                "select e.emp_id, e.name as emp_name, e.dept_no, d.name as dept_name "
                        + "from hr.employee as e "
                        + "left join hr.department as d on e.dept_no = d.dept_no");
        /**
         * 遍历 SQL 执行结果
         */
        while (resultSet.next()) {
            for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
                System.out.print(resultSet.getMetaData().getColumnName(i) + ":" + resultSet.getObject(i));
                System.out.print(" | ");
            }
            System.out.println();
        }

        resultSet.close();
        statement.close();
        connection.close();

    }
}