В последнее время я обращаю внимание на технологии обработки больших данных и внедрение продуктов с открытым исходным кодом, и обнаружил, что во многих проектах упоминается штука под названием 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 очень гибкая, и есть два варианта его использования в реальных проектах:
-
Рассматривайте Calcite как библиотечную библиотеку и встраивайте ее в свой собственный проект.
-
Внедрите адаптер, и проект интегрируется с Calcite через адаптер, который считывает источник данных.
функциональная агрегация
Вообще говоря, мы можем разделить систему управления базами данных на пять вышеуказанных частей.В начале своего проектирования Calcite решил сосредоточиться и реализовать только три синие части на рисунке, а серые управление данными и хранение данных открыть для внешних вычисления. , механизм хранения для достижения. Цель этого состоит в том, что характеристики самих данных делают части управления данными и хранения данных разнообразными и сложными (файлы, реляционные базы данных, колоночные базы данных, распределенное хранилище и т. д.) Calcite отказывается от этих двух частей и фокусируется на более общий верхний уровень.Модули могут эффективно управлять сложностью системы и фокусироваться на областях, которые вы можете делать, можете делать и можете делать глубже и лучше.
Calcite не повторяет создание колес, а использует готовые вещи, когда они доступны.Например, в части парсинга SQL JavaCC с открытым исходным кодом напрямую используется для преобразования операторов SQL в код Java, а затем в абстрактный синтаксис дерево для следующего этапа использования. Другим примером является то, что для реализации гибких функций метаданных Calcite должен поддерживать компиляцию Java-кода во время выполнения, а JavaC по умолчанию слишком тяжелый и требует более легкого компилятора.Здесь используется Janino с открытым исходным кодом.
Эта функциональная направленность, отсутствие необходимости изобретать велосипед и достаточно простые идеи дизайна продукта делают 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();
}
}