SQL ferramenta de análise de código aberto -Apache Calcita

conceito

Apache Calcita é um open source de SQL ferramentas analíticas, uma variedade de instruções SQL pode ser analisado em abstrato cirurgia sintaxe AST (Abstract Syntax Tree), após a adopção da AST operação pode colocar algoritmos no SQL relacional para expressar refletida no código específico.

Calcita em sua vida para o Optiq (também Farrago), escrito para a linguagem Java, através de 10 anos de desenvolvimento, tornou-se seu projeto Apache de nível superior em 2013, e continua a desenvolver, o fundador do projeto é Julian Hyde, que tem por muitos anos experiência de desenvolvimento motor SQL, trabalhando atualmente em Hortonworks, é responsável pelo desenvolvimento e manutenção do projeto Calcita.

Atualmente, Calcita como SQL análise e processamento motor tem Hive, Broca, Flink, Phoenix e Tempestade, é certo que haverá mais e mais dados processamento motor usa Calcita como ferramentas de análise do SQL.

 

função

Em conclusão Calcita tem as seguintes características principais:

  • análise SQL
  • verificação de SQL
  • consulta Otimização
  • SQL Builder
  • Conexões de dados

 etapa SQL parse calcita

A figura acima, há geralmente Calcita SQL analisar os seguintes passos:

  • Analisador . Este passo Calcita por Java CC irá resolver para não verificar a AST SQL
  • O Validar . O papel principal desta etapa é uma certificação escolar Analisador passo AST é legítimo, validando esquema de SQL, campos, funções e assim se houver; instrução SQL é legal, etc. Após esta etapa seja concluída para gerar a árvore RelNode (na árvore RelNode, por favor. ver abaixo)
  • A Optimize . O papel principal da etapa de otimizar árvore RelNode, e se converteu ao plano de execução física. Principalmente relacionadas à otimização SQL regras como: otimização baseado em regras (RBO) e otimização baseado em custo (CBO); em optimze Este passo é opcional, em princípio, pela árvore RelNode após o Validar já convertidos diretamente para plano de implementação física, mas SQL moderno analisador incluem basicamente este passo, a fim de plano de execução de SQL otimizar. Os resultados obtidos neste passo é o plano de execução física.
  • O Execute , ou seja, a fase de execução. Esta fase é principalmente para fazer é: converso plano de execução física no programa pode ser executado em uma plataforma particular. A colmeia e Flink, nesta fase, em um plano de execução física CodeGen gerar o código executável correspondente.

 

componentes relacionados calcita

Calcite principalmente nos seguintes conceitos:

  • Catelog : A principal definição de SQL metadados semântica associada com o namespace.
  • Analisador SQL : SQL principalmente para ser convertido em AST.
  • Validador SQL : Catalog ao certificado escolar pela AST.
  • Otimizador de consulta : AST será transformado em um plano de implementação física, otimizar plano de execução física.
  • Gerador de SQL : reverter o plano de implementação física convertidos em instruções SQL.


1) categoria

Catálogo: As principais definições são acessados ​​por namespace SQL, incluindo o seguinte:

  1. esquema: esquema define um conjunto de principal e mesa, schame não necessariamente obrigatório, por exemplo, ter dois do mesmo nome da tabela T1, T2, é necessário distinguir entre o esquema duas tabelas, como A.T1, B.T1
  2. tabela de correspondência de uma base de dados relacional, dados representativos de uma classe, a calcite na: Tabela RelDataTypedefinição de
  3. RelDataType tabela de definição de dados representativas, tais como dados Nome da tabela, o tipo e semelhantes coluna. 

 

Schema:

public interface Schema {
  
  Table getTable(String name);

  Set<String> getTableNames();

  Set<String> getFunctionNames();

  Schema getSubSchema(String name);

  Set<String> getSubSchemaNames();
  
  Expression getExpression(SchemaPlus parentSchema, String name);
  
  boolean isMutable();

Table:
public interface Table {
  
  RelDataType getRowType(RelDataTypeFactory typeFactory);

  Statistic getStatistic();
  
  Schema.TableType getJdbcTableType();
}

 Representativas em que os tipos de dados RelDataType Row, para os quadros de dados estatísticos, em particular para a tabela de CBO de consideração para a tabela de cálculo.

2) SQL analisador

Escrito por Java CC, será convertida para SQL AST.

  • Java CC refere-se ao compilador Java compilador, um determinado linguagem específica de domínio pode ser convertido em linguagem Java
  • A bandeira calcite (Token) é expresso em SqlNodee pelo método de conversão inversa no SQLSqlnodeunparse

fundido (id como flutuador)

Java CC pode ser expressa como

<CAST>
<LPAREN>
e = Expression(ExprContext.ACCEPT_SUBQUERY)
<AS>
dt = DataType() {agrs.add(dt);}
<RPAREN>
....

3) Pesquisa Optimizer

primeiro olhar

INSERT INTO tmp_node
SELECT s1.id1, s1.id2, s2.val1
FROM source1 as s1 INNER JOIN source2 AS s2
ON s1.id1 = s2.id1 and s1.id2 = s2.id2 where s1.val1 > 5 and s2.val2 = 3; 

Por Calcita em:

LogicalTableModify(table=[[TMP_NODE]], operation=[INSERT], flattened=[false])
  LogicalProject(ID1=[$0], ID2=[$1], VAL1=[$7])
    LogicalFilter(condition=[AND(>($2, 5), =($8, 3))])
      LogicalJoin(condition=[AND(=($0, $5), =($1, $6))], joinType=[INNER])
        LogicalTableScan(table=[[SOURCE1]])
        LogicalTableScan(table=[[SOURCE2]])

É árvore RelNode unoptimized pode ser encontrada é a TableScan fundo, tabela de dados brutos é lido, seguido LogicalJoin, tipo Joiner é INNER JOIN, em seguida, tomou LogicalJoin fazer a operação LogicalFilter, correspondente ao SQL WHERE condição, finalmente, não do projeto é projetada para operar.

Mas podemos observar para INNER JOIN, ONDE condição que pode ser empurrado para baixo, como

LogicalTableModify(table=[[TMP_NODE]], operation=[INSERT], flattened=[false])
  LogicalProject(ID1=[$0], ID2=[$1], VAL1=[$7])
      LogicalJoin(condition=[AND(=($0, $5), =($1, $6))], joinType=[inner])
        LogicalFilter(condition=[=($4, 3)])  
          LogicalProject(ID1=[$0], ID2=[$1],      ID3=[$2], VAL1=[$3], VAL2=[$4],VAL3=[$5])
            LogicalTableScan(table=[[SOURCE1]])
        LogicalFilter(condition=[>($3,5)])    
          LogicalProject(ID1=[$0], ID2=[$1], ID3=[$2], VAL1=[$3], VAL2=[$4],VAL3=[$5])
            LogicalTableScan(table=[[SOURCE2]])

Isso pode reduzir a quantidade de dados Cadastre-se melhorar a eficiência do SQL

A quantidade de dados nas condições reais do processo pode ser empurrado para baixo com menos Adira JOIN de

INSERT INTO tmp_node
SELECT s1.id1, s1.id2, s2.val1
FROM source1 as s1 LEFT JOIN source2 AS s2
ON s1.id1 = s2.id1 and s1.id2 = s2.id2 and s1.id3 = 5

 s1.id3 = 5 Esta condição pode ser empurrado para baixo para o s1 dados filtrados, mas em determinados cenários, alguns não empurrado para baixo, o seguinte SQL:

INSERT INTO tmp_node
SELECT s1.id1, s1.id2, s2.val1
FROM source1 as s1 LEFT JOIN source2 AS s2
ON s1.id1 = s2.id1 and s1.id2 = s2.id2 and s2.id3 = 5

Se S1, S2 é a tabela de fluxo (tabela dinâmica referem-se ao conceito de streaming de Flink), então ele não pode ser empurrado para baixo, o impulso para baixo como S1, então, uma vez que não há nenhum driver de dados juntar Após a operação de filtração, e, assim, obter os resultados desejados ( ver Flink / faíscas-vivo)

Então, o próximo possamos ter uma pergunta, em que circunstâncias pode fazer semelhante empurrado para baixo, a operação de push-up, é realizada de acordo com o que princípios? Como mostrado na FIG.

T1 JOIN T2 JOIN T3

ADERIR sequência semelhante para este caso é o valor do primeiro ou o último? Este refere-se a um método utilizado Optimizer, a principal finalidade Optimizer é para reduzir a quantidade de dados processados ​​SQL, para reduzir o consumo de recursos e maximizar a eficiência de tais como SQL: coluna corte inútil, projecção combinado, é convertido em subconsultas JOIN, ADERIR reordenadas pushdown de projecção, filtração de pushdown. Existem dois tipos principais de métodos de otimização: CBO (CBO) baseados sintaxe (RBO) e com base em

  1. RBO (Based Optimization Rule)

ponto popular, então, que é pré-definido conjunto de regras, em seguida, para otimizar o plano de execução com base nestas regras.
como

  • ProjectFilterRule

    Esta regra cenários de uso para o filtro sobre o Projeto, o filtro pode ser empurrado para baixo. Se um determinado RelNode árvore

 

    LogicalFilter
      LogicalProject
        LogicalTableScan

Pode ser otimizado para

 

    LogicalProject
      LogicalFilter
        LogicalTableScan
  • FilterJoinRule

    Esta é uma regra de filtro é usado sobre a cena Junte, então não pode fazer primeiro filtro de associação, para reduzir o número de Junte

E assim por diante, existem muitas regras semelhantes. Mas a experiência de otimização tentativa RBO, até certo ponto, não existe uma fórmula para determinar qual a melhor otimização. A implementação é na CalcitaHepPlanner

  1. CBO (Cost Based Optimization)

Esse ditado popular é: Calcule todos plano de execução de SQL possível por alguns de "preço" algoritmo, selecione uma das menor custo de plano de implementação, como descrito acima para as três tabelas JOIN, que geralmente plano de implementação RBO não pode julgar melhor otimização, apenas a calcular o custo de cada método JOIN.

Calcite vai cada operação (por exemplo LogicaJoin, LocialFilter, LogicalProject, LogicalScan) o número real de esquema em comparação com o custo de betão custo plano de execução diferente, e, em seguida, seleccionar um programa relativamente pequeno, como resultado final, a razão disse relativamente pequena, porque se você quiser completamente transversais calcular todos os custos possíveis podem superar os benefícios, gastar mais recursos humanos e, por isso, apenas dizer escolher um plano de execução relativamente ideal. CBO objetivo é "evitar o uso do plano de execução de pior, em vez de encontrar o melhor"

Calcita está atualmente na otimização do uso da CBO, para a implementação VolcanoPlanner, os detalhes específicos deste algoritmo pode se referir ao código original


uso calcita

Desde Calcita está escrito na linguagem Java, só é necessário introduzir o pacote Jar apropriado para o projecto ou programa, o seguinte é um exemplo de uma corrida:

public class TestOne {
    public static class TestSchema {
        public final Triple[] rdf = {new Triple("s", "p", "o")};
    }

    public static void main(String[] args) {
        SchemaPlus schemaPlus = Frameworks.createRootSchema(true);
        
        //给schema T中添加表
        schemaPlus.add("T", new ReflectiveSchema(new TestSchema()));
        Frameworks.ConfigBuilder configBuilder = Frameworks.newConfigBuilder();
        //设置默认schema
        configBuilder.defaultSchema(schemaPlus);

        FrameworkConfig frameworkConfig = configBuilder.build();

        SqlParser.ConfigBuilder paresrConfig = SqlParser.configBuilder(frameworkConfig.getParserConfig());
        
        //SQL 大小写不敏感
        paresrConfig.setCaseSensitive(false).setConfig(paresrConfig.build());

        Planner planner = Frameworks.getPlanner(frameworkConfig);

        SqlNode sqlNode;
        RelRoot relRoot = null;
        try {
            //parser阶段
            sqlNode = planner.parse("select \"a\".\"s\", count(\"a\".\"s\") from \"T\".\"rdf\" \"a\" group by \"a\".\"s\"");
            //validate阶段
            planner.validate(sqlNode);
            //获取RelNode树的根
            relRoot = planner.rel(sqlNode);
        } catch (Exception e) {
            e.printStackTrace();
        }

        RelNode relNode = relRoot.project();
        System.out.print(RelOptUtil.toString(relNode));
    }
}

mesa Triplo definição correspondente classe:

public class Triple {
    public String s;
    public String p;
    public String o;

    public Triple(String s, String p, String o) {
        super();
        this.s = s;
        this.p = p;
        this.o = o;
    }

}

Ver código específico: https://github.com/yuqi1129/calcite-test

 

Calcita usar Mysql Demonstração

Configurando calcita tabela de consulta mysql local no banco de dados do aluno sob dbtest_1

modelo json como segue:

{
  version: '1.0',
  defaultSchema: 'dbtest_1',
  schemas: [
    {
      name: 'dbtest_1',
      type: 'custom',
      factory: 'org.apache.calcite.adapter.jdbc.JdbcSchema$Factory',
      operand: {
        jdbcDriver: 'com.mysql.jdbc.Driver',
        jdbcUrl: 'jdbc:mysql://localhost:3306/dbtest_1',
        jdbcUser: 'xxx',
        jdbcPassword: 'xxx'
      }
    }
  ]
}

código de teste:

package com.learn.mysql;

import org.apache.calcite.jdbc.CalciteConnection;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * @Description:
 * @Date:Create in 下午5:38 2018/9/15
 * @Modified By:
 */
public class QueryMysql {
  public static void main(String[] args) throws SQLException, ClassNotFoundException {
    Class.forName("com.mysql.jdbc.Driver");
    Properties info = new Properties();
    info.put("model",
      "inline:"
        + "{\n"
        + "  version: '1.0',\n"
        + "  defaultSchema: 'dbtest_1',\n"
        + "  schemas: [\n"
        + "    {\n"
        + "      name: 'dbtest_1',\n"
        + "      type: 'custom',\n"
        + "      factory: 'org.apache.calcite.adapter.jdbc.JdbcSchema$Factory',\n"
        + "      operand: {\n"
        + "        jdbcDriver: 'com.mysql.jdbc.Driver',\n"
        + "        jdbcUrl:'jdbc:mysql://localhost:3306/dbtest_1',\n"
        + "        jdbcUser: 'xxx',\n"
        + "        jdbcPassword: 'xxx'\n"
        + "      }\n"
        + "    }\n"
        + "  ]\n"
        + "}");

    Connection connection =
      DriverManager.getConnection("jdbc:calcite:", info);
    // must print "directory ... not found" to stdout, but not fail
    Statement statement = connection.createStatement();
    CalciteConnection calciteConnection =
      connection.unwrap(CalciteConnection.class);
    ResultSet resultSet =
      statement.executeQuery("select * from \"dbtest_1\".\"student\"");

    ResultSet tables =
      connection.getMetaData().getTables(null, null, null, null);

    final StringBuilder buf = new StringBuilder();
    while (resultSet.next()) {
      int n = resultSet.getMetaData().getColumnCount();
      for (int i = 1; i <= n; i++) {
        buf.append(i > 1 ? "; " : "")
          .append(resultSet.getMetaData().getColumnLabel(i))
          .append("=")
          .append(resultSet.getObject(i));
      }
      System.out.println(buf.toString());
      buf.setLength(0);
    }
    resultSet.close();
    statement.close();
    connection.close();
  }
}

resultado

id=1; age=18; name=李四
id=2; age=18; name=张三
id=4; age=12; name=studentA

depender

 <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.calcite</groupId>
            <artifactId>calcite-core</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.calcite.avatica</groupId>
                    <artifactId>avatica-core</artifactId>
                </exclusion>
            </exclusions>
            <version>1.12.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.1.0</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.1.0</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.1.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.calcite.avatica/avatica -->
        <dependency>
            <groupId>org.apache.calcite.avatica</groupId>
            <artifactId>avatica</artifactId>
            <version>1.9.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.41</version>
        </dependency>
    </dependencies>
Publicado 121 artigos originais · Louvor obteve 24 · vista 130 000 +

Acho que você gosta

Origin blog.csdn.net/CodeAsWind/article/details/104799684
Recomendado
Clasificación