代码之旅

I love Coding !

Trino 是一种旨在使用分布式查询高效查询大量数据的工具。通常用在数据仓储、数据分析、海量数据聚合和报表生成等任务上,这些任务通常被归类为联机分析处理(online analytical processing,OLAP)。

  • 数据孤岛: 随着大数据的不断增多,和数据存储机制的日益多样: 关系数据库、NoSQL 数据库、文档数据库、键值存储和对象存储系统等。数据分散在各个孤岛上。
  • 查询能力: 其中有些系统甚至不能提供满足分析所需的查询性能
  • 横向扩展: 其他一些系统则将数据存储在单一庞大的系统上。因而不能像现代的云应用程序一样横向扩展。

通过支持不同系统上的联邦查询、并行查询和横向集群扩展等功能,Trino解决了上述问题。

  • 联邦查询: Trino的设计初衷是用来查询 HDFS 上的数据,也可以从对象存储系统、关系数据库管理系统(RDBMS)、NoSQL 数据库和其他系统中查询数据(只要提供了对应系统的Connector)。
  • 并行查询: Trino使用了很多技术来执行分布式查询,这些技术包括内存并行处理、跨集群节点管线执行、多线程执行模型(以充分利用所有 CPU 核心)、高效的扁平内存数据结构(以最小化 Java 的垃圾回收)和 Java 字节码生成等。借助上述技术,可以更快地获得查询结果。
  • 横向集群扩展: Trino存算分离的,可以各自独立地扩展。Trino代表计算层,底层的数据源代表存储层。Trino 可以通过动态扩展计算集群的规模来扩展查询能力,并可以在数据源中数据所在的位置查询数据。借助这一特性,你可以极大地优化硬件资源需求并降低成本。
阅读全文 »

本文是学习Trino源码前的准备工作。在开始之前,先做好准备工作:

  1. 编译环境 Mac OS X or Linux
  2. 安装Java 17.0.4+, Trino 依赖 Java 17.0.4 或更新的版本来进行构建(本文基于 https://gitee.com/chutian0610/trino/tree/code-study/)。
  3. 安装 Maven, Maven的版本不重要,Trino编译时会使用mvnw指定mvn版本
  4. 安装 Docker
阅读全文 »

最近在做SQL方面的工作,主要是SQL语法解析和改写。因此打算记录下解析的技术&工具。

文本解析一般首推使用正则表达式。对于大多数的文本,正则都可以很好的解析。以ELK套件中的Logstash为例,其内置的Grok插件支持了120+种常用Pattern,可以很好的用于解析多种形式的文本。但是。正则不是文本解析的万能钥匙,由于正则表达式的自身实现约束,大多数正则表达式引擎不能较好的处理嵌套结构的数据。此外,对于脚本语言或编程语言,正则表达式也是无能为力。

举个例子,如果想替换SQL中的某个字段名,如果只是简单的使用正则替换,那么很可能会错误修改其他位置的文本。例如想修改下面SQL中的order_cnt别名时,就很有可能错误修改其他位置的order_cnt,要避免这类问题需要对正则添加很多断言和约束,由于实际场景中可能会对SQL的列名,表名,条件等各个位置进行改写,场景十分复杂。所以正则这种方式不能从根本上解决问题。由此,引出了接下来要介绍的技术–语法解析器(Parser)。

1
2
3
4
5
6
7
8
select 
dt,
sum(order_cnt) as order_cnt
from(
select order_cnt
from order_info
where dt between xxx and xxx
)group by dt
阅读全文 »

LFU(Least Frequently Used)最近最少使用算法。它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。

举个例子,缓存空间大小为3:

  1. put(1,“a”)
  2. put(2,“b”)
  3. get(1)
  4. get(2)
  5. put(3,“c”)
  6. put(4,“d”) // 此时LFU应该淘汰(3,“c”)
阅读全文 »

Maven依赖传递是Maven的核心机制之一,它能够一定程度上简化Maven的依赖配置。

flowchart LR
    A -->|depend on|B
    B -->|depend on|C
    A -->|depend on|C

如上图所示,模块A依赖模块B,模块B依赖模块C。此时B是A的直接依赖,C是A的间接依赖。

Maven的依赖传递机制是指,不管Maven项目存在多少间接依赖,POM中都只需要定义其直接依赖,而不需要定义任何间接依赖。Maven 会动读取当前项目各个直接依赖的 POM,将那些必要的间接依赖以传递性依赖的形式引入到当前项目中。Maven 的依赖传递机制能够帮助用户一定程度上简化POM的配置。

基于 A、B、C 三者的依赖关系,根据 Maven 的依赖传递机制,我们只需要在模块 A 的 POM 中定义其直接依赖 B,在模块 B 的 POM 中定义其直接依赖 C,Maven会解析A的直接依赖 B 的POM,将间接依赖 C 以传递性依赖的形式引入到模块 A 中。通过这种依赖传递关系,可以使依赖关系树迅速增长到一个很大的量级,很有可能会出现依赖重复,依赖冲突等情况,Maven 针对这些情况提供了如下功能进行处理:

  • 依赖关系传递
    • 可选依赖
    • 排除依赖
  • 依赖关系范围
  • 依赖关系管理
阅读全文 »

clickhouse 在查询中使用别名时可能会有下面的问题1

1
2
3
$ SELECT avg(number) AS number, max(number) FROM numbers(10)

Aggregate function avg(number) is found inside another aggregate function in query.

If aliased expression contains aggregate function, alias should not be resolved inside another aggregate function.
If alias name clashes with the column name, the substitution of this alias should be cancelled.

原因是clickhouse的别名如果和某个列名相同,就会有上面的异常。可以通过添加下面了配置处理上述问题2.

1
2
SELECT avg(number) AS number, max(number) FROM numbers(10)
settings prefer_column_name_to_alias = 1;

但是这个配置引入了新的问题3.

Prefer alias for ORDER BY after GROUP BY in case of set prefer_column_name_to_alias=1;
Clickhouse version 21.6

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
$ SELECT
max(number) AS number,
sum(number) AS sum
FROM numbers(100)
ORDER BY number ASC

Query id: 058b2cb1-7ea6-44de-b6a8-662ac4d949a8


0 rows in set. Elapsed: 0.002 sec.

Received exception from server (version 21.6.1):
Code: 184. DB::Exception: Received from localhost:9000. DB::Exception: Aggregate function max(number) is found inside another aggregate function in query: While processing max(number) AS number.

$ set prefer_column_name_to_alias=1;

$ SELECT
max(number) AS number,
sum(number) AS sum
FROM numbers(100)
ORDER BY number ASC

Query id: f8ca2417-9bba-42c6-9f12-42987a26e75d


0 rows in set. Elapsed: 0.004 sec.

Received exception from server (version 21.6.1):
Code: 215. DB::Exception: Received from localhost:9000. DB::Exception: Column `number` is not under aggregate function and not in GROUP BY: While processing number ASC.

clickhouse 社区又提出了新的pr4处理上述问题:

Add more options to prefer_column_name_to_alias setting. This is for #24237

  1. set prefer_column_name_to_alias=1 --> prefer column over alias in all sub clauses;
  2. set prefer_column_name_to_alias=2 --> prefer column over alias in all sub clauses before and include group by;
  3. set prefer_column_name_to_alias=3 --> prefer column over alias in all sub clauses before group by;

参考

maven的包名可以通过finalName配置。

1
2
3
<build>
<finalName> ${project.artifactId}-${project.version}</finalName>
</build>

如果想在包名上增加时间戳,可以通过在finalName中添加属性maven.build.timestamp.

1
2
3
<build>
<finalName> ${project.artifactId}-${maven.build.timestamp}</finalName>
</build>

maven.build.timestamp的格式是通过maven.build.timestamp.format参数设置的。

1
2
3
<properties>
<maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
</properties>

时区问题

maven自带时间组件时区是只能使用UTC,要使用正确的时间,需要另一个插件的帮助。

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
<build>
<!-- 要将maven.build.timestamp替换成build.time,因为要使用buildnubmer-maven-plugin组件中声明的时间串。-->
<finalName>${project.artifactId}-${build.time}</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>timestamp-property</id>
<goals>
<goal>timestamp-property</goal>
</goals>
<configuration>
<name>build.time</name>
<pattern>yyyyMMddHHmm</pattern>
<locale>zh_CN</locale>
<timeZone>GMT+8</timeZone>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。为了和 CSS 区分,这里把攻击的第一个字母改成了 X,于是叫做 XSS。XSS 的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。

而由于直接在用户的终端执行,恶意代码能够直接获取用户的信息,或者利用这些信息冒充用户向网站发起攻击者定义的请求。在部分情况下,由于输入的限制,注入的恶意脚本比较短。但可以通过引入外部的脚本,并由浏览器执行,来完成比较复杂的攻击策略。

阅读全文 »

在排查线上问题的时候,发现日志中只有java.lang.NullPointerException: null,没有打印出日志堆栈。第一反应是log.error()用错了方法。

1
2
3
void error(String msg);
void error(String format, Object... arguments);
void error(String msg, Throwable t);

在打印异常的时候建议使用 void error(String msg, Throwable t)这个方法

如果想使用void error(String format, Object... arguments)这个方法,需要注意2点:

  1. slf4j的版本要大于或等于1.6.0.
  2. 使用占位符时,不要带上Exception。
1
2
3
4
5
6
String s = "Hello world";
try {
Integer i = Integer.valueOf(s);
} catch (NumberFormatException e) {
logger.error("Failed to format {}", s, e);
}

仔细检查了代码,发现并不是这个问题引起的。

阅读全文 »

在使用antlr生成的语法解析器处理多个文件后,JVM 最终会产生内存不足异常,PredictionContextCache中的hashMap和DFA数组_decisionToDFA会不断增长。因为 PredictionContextCache和_decisionToDFA在生成Parser和Lexer中是类共享的。

1
2
3
4
5
6
public class XXXParser extends Parser {
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
// ...
}
阅读全文 »