代码之旅

I love Coding !

Trino源码学习-查询调度

上篇分析了SQL逻辑执行计划的生成,本篇将继续分析逻辑计划是如何在进行调度的。

阅读全文 »

The Snowflake Elastic Data Warehouse

摘要

我们生活在分布式计算的黄金时代。公有云平台几乎能够按需提供无限的计算和存储资源。同时,SaaS模型(Software-as-a-Service)将企业级系统带给了无法负担成本和相关系统复杂性的用户。传统的数仓系统正在努力的适应新的环境,但是首先,它是为固定资源而设计,从而没办法利用云的弹性能力。另外,传统的数仓依赖复杂的ETL流水线和物理调优,这个是和云的半结构数据和快速解决工作问题的新形式所需要的弹性和快速不一致的。

我们决定进行根本性的重新设计。我们的任务是去建立云上的企业级数据仓库的解决方案。就是Snowflake弹性数据仓库,简称Snowflake。Snowflake是一种多租户、事务性、安全、高度可扩展的弹性系统,具备完整的SQL支持和半结构化和schema-less数据模式支持。Snowflake在亚马逊云上提供现付即用的服务。用户只需将数据导入云上,就可以立即利用他们熟悉的工具和界面进行管理和查询。从2012年底,Snowflake开始计划实施,到2015年6月,Snowflake已经可以大体可用。现在,Snowflake被越来越多的大大小小的组织用于生产。这个系统每天运行几百万次查询在几PB的数据上。

本文主要介绍Snowflake的设计、多集群共享数据的结构和Snowflake的关键特性:极致的弹性和可用性、半结构化和schema-less的数据支持、时间旅行、端到端的安全性,最后是一些经验总结和对未来工作的展望。

阅读全文 »

Trino源码学习-SQL语法树解析

Trino在查询提交时,会将SQL文本解析成语法树。Trino将这一动作分为2个部分:

flowchart LR
    s1(SQL)-->|Parser base Antlr|s2(ANTLR AST)
    s2-->|AST builder base Antlr Visitor|s3(Presto AST)

Antlr Parser

Antlr的使用在Trino中就不多做介绍了,简单来说,Antlr基于类似巴斯卡范式(BNF)的语法规则和词法规则文件,动态生成Parser和Lexer。

在Trino项目中,该文件位于

  • core/trino-parser/src/main/antlr4/io/trino/sql/parser/SqlBase.g4

通过使用Antlr的mave插件,在generate source阶段,会动态生成SqlBaseLexer和SqlBaseParser

  • core/trino-parser/target/generated-sources/antlr4/io/trino/sql/parser/目录下

io.trino.sql.parser.SqlParser

trino在实际使用过程中并没有直接使用Antlr的Parser,而是统一使用门面类SqlParser用于执行语法解析。SqlParser的解析方法内部都是通过调用invokeParser来实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
private Node invokeParser(String name, String sql, Function<SqlBaseParser, ParserRuleContext> parseFunction, ParsingOptions parsingOptions)
{
try {
//CaseInsensitiveStream 忽略大小写
SqlBaseLexer lexer = new SqlBaseLexer(new CaseInsensitiveStream(CharStreams.fromString(sql)));
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SqlBaseParser parser = new SqlBaseParser(tokenStream);
// 此处优化是由于antlr的内存泄漏
// https://github.com/antlr/antlr4/issues/499
initializer.accept(lexer, parser);



// Override the default error strategy to not attempt inserting or deleting a token.
// Otherwise, it messes up error reporting
parser.setErrorHandler(new DefaultErrorStrategy()
{
@Override
public Token recoverInline(Parser recognizer)
throws RecognitionException
{
if (nextTokensContext == null) {
throw new InputMismatchException(recognizer);
}
throw new InputMismatchException(recognizer, nextTokensState, nextTokensContext);
}
});
// 流式遍历方式的listenner。进行后置处理
parser.addParseListener(new PostProcessor(Arrays.asList(parser.getRuleNames()), parser));

lexer.removeErrorListeners();
lexer.addErrorListener(LEXER_ERROR_LISTENER);

parser.removeErrorListeners();
parser.addErrorListener(PARSER_ERROR_HANDLER);

ParserRuleContext tree;
try {
// first, try parsing with potentially faster SLL mode
parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
tree = parseFunction.apply(parser); // 此时解析为Antlr树
}
catch (ParseCancellationException ex) {
// if we fail, parse with LL mode
tokenStream.seek(0); // rewind input stream
parser.reset();
// SLL不行的话,使用降级模式LL
parser.getInterpreter().setPredictionMode(PredictionMode.LL);
tree = parseFunction.apply(parser);
}

return new AstBuilder(parsingOptions).visit(tree); // 访问者模式,生成Presto自定义的语法树
}
catch (StackOverflowError e) {
throw new ParsingException(name + " is too large (stack overflow while parsing)");
}
}

Presto 自定义的语法节点类存放在core/trino-parser/src/main/java/io/trino/sql/tree包下。

antlr内存泄漏问题,详细内容可见 AntlrV4 的内存泄漏问题

Trino源码学习-执行计划生成

上篇分析了trino提交查询部分的源码,本篇来分析下,构建执行计划部分的源码。

阅读全文 »

Trino源码学习-Page数据结构

本篇来看下在查询执行的过程中,底层的数据结构是什么样的。

阅读全文 »

Guice简介

Guice是一个轻量级依赖注入框架。关于什么是依赖注入可以查看以前的blog,这里就不赘述了。

阅读全文 »

博客图片优化

本篇文章介绍了如何优化博客中使用的图片。

阅读全文 »

LOD表达式

详细级别表达式(LOD Expression)是处理在一个可视化视图中包含多个级别粒度的问题的方法。

详细级别指的是数据不同粒度(聚合度),共有3种不同的详细级别:

  • 行级别表达式(聚合度最低)
  • 视图级别表达式
  • 表级别表达式(聚合度最高)

行级别表达式是直接引用基础表的未聚合数据列的表达式。此时,表达式的维度是行级别的(如果表有主键的话,维度即主键)。例如:

1
[Profit Ratio]=[Sales] / [Profit]

通过将每行的销量除以利润,可以得到利润率[ProfitRatio]的新列。

视图级别表达式是引用聚合数据源列的表达式。此时,表达式的维度是当前SQL视图的维度。例如:

1
SUM(Sales)/SUM(Profit)

表级别表达式是指不使用任何范围关键字的表达式。例如以下表达式返回整个表的最早订单日期:

1
{MIN([Order Date])}

在 Tableau 支持详细级别表达式之前,无法在视图级别以外的详细级别创建计算。例如,用户的意图是将每个商店的商店销售额与所有商店的平均销售额进行比较,如果您尝试保存以下表达式,Tableau 将显示错误消息:“无法使用此函数混合聚合和非聚合参数”:

1
[Sales] – AVG([Sales])

在这种情况下,详细级别表达式就派上了用场。详细级别表达式提供了对视图详细级别之外的数据计算聚合的简单方法,实现在可视化中以任意的方式组合这些数据。

阅读全文 »

Trino源码学习-查询提交

上篇介绍了Trino的启动流程,这篇我们来学习下查询提交的流程。

阅读全文 »

Trino源码学习-Trino启动

本篇文章开始,我们将进行Trino的源码分析,一切还是从启动开始看起。

阅读全文 »