当前位置: 代码网 > it编程>数据库>MsSqlserver > 2024年最新阿卡姆大数据科普报告——Calcite,2024年最新我的腾讯大数据开发面试经历分享

2024年最新阿卡姆大数据科普报告——Calcite,2024年最新我的腾讯大数据开发面试经历分享

2024年07月28日 MsSqlserver 我要评论
所以如果你有一些存储下来的数据希望通过SQL访问它,首先需要定义一个自定义表或是schema,然后再去定义一些能使数据访问高效的规则。是通用格式,包含了一个自定义表org.apache.calcite.adapter.csv.CsvTableFactory,这个类实现了。和它对应的文件),同时也提供给了开发者更多的控制选项(例如,为每一个table提供不同参数)。)有,例如,有100列和100万行数据,你肯定希望用户在每次查询过程中不检索全量数据。)实现和查询都没有问题,因为我们的表中并没有大量的数据。

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

±-------±-------±--------±--------±---------------±-------±------±–+

| empno  |  name  | deptno  | gender  |      city      | empid  |  age  | s |

±-------±-------±--------±--------±---------------±-------±------±–+

| 100    | fred   | 10      |         |                | 30     | 25    | t |

| 110    | eric   | 20      | m       | san francisco  | 3      | 80    | n |

| 110    | john   | 40      | m       | vancouver      | 2      | null  | f |

| 120    | wilma  | 20      | f       |                | 1      | 5     | n |

| 130    | alice  | 40      | f       | vancouver      | 2      | null  | f |

±-------±-------±--------±--------±---------------±-------±------±–+

接下来是表连接和分组聚合查询:

sqlline> select d.name, count(*)

. . . .> from emps as e join depts as d on e.deptno = d.deptno

. . . .> group by d.name;

±-----------±--------+

|    name    | expr$1  |

±-----------±--------+

| sales      | 1       |

| marketing  | 2       |

±-----------±--------+

最后,一个计算操作返回一个单行记录,也可以通过这种简便的方法来测试表达式和sql函数

sqlline> values char_length('hello, ’ || ‘world!’);

±--------+

| expr$0  |

±--------+

| 13      |

±--------+

calcite还包含很多sql特性,这里就不一一列举了。

schema探索


那么calcite是如何发现表的呢?事实上calcite的核心是并不能理解csv文件的(作为一个“没有存储层的databse”,calcite是了解任何文件格式),之所以calcite能读取上文中的元数据,是因为在calcite-example-csv里我们撰写了相关代码。

在执行链里包含着很多步骤。首先我们定义一个可以被库工厂加载的模型文件(we define a schema based on a schema factory class in a model file.)。然后库工厂会加载成数据库并创建许多表,每一个表都需要知道自己如何加载csv中的数据。最后calcite解析完查询并将查询计划映射到这几个表上时,calcite会在查询执行时触发这些表去读取数据。接下来我们更深入地解析其中的细节步骤。

举个栗子(a model in json format):

{

version: ‘1.0’,

defaultschema: ‘sales’,

schemas: [

{

name: ‘sales’,

type: ‘custom’,

factory: ‘org.apache.calcite.adapter.csv.csvschemafactory’,

operand: {

directory: ‘target/test-classes/sales’

}

}

]

}

这个模型文件定义了一个库(schema)叫sales,这个库是由一个插件类(a plugin class)支持的,org.apache.calcite.adapter.csv.csvschemafactory这个是calcite-example-csv工程里interface schemafactory的一个实现。它的create方法将一个schema实例化了,将model file中的directory作为参数传递过去了。

public schema create(schemaplus parentschema, string name,

map<string, object> operand) {

string directory = (string) operand.get(“directory”);

string flavorname = (string) operand.get(“flavor”);

csvtable.flavor flavor;

if (flavorname == null) {

flavor = csvtable.flavor.scannable;

} else {

flavor = csvtable.flavor.valueof(flavorname.touppercase());

}

return new csvschema(

new file(directory),

flavor);

}

根据模型(model)描述,库工程(schema factory)实例化了一个名为’sales’的简单库(schema)。这个库(schema)是org.apache.calcite.adapter.csv.csvschema的实例并且实现了calcite里的接口schema。

一个库(schema)的主要职责就是创建一个表(table)的列表(库的职责还包括子库列表、函数列表等,但是calcite-example-csv项目里并没有包含他们)。这些表实现了calcite的table接口。csvschema创建的表全部是csvtable和他的子类的实例。

下面是csvschema的一些相关代码,对基类abstractschema中的gettablemap()方法进行了重载。

protected map<string, table> gettablemap() {

// look for files in the directory ending in “.csv”, “.csv.gz”, “.json”,

// “.json.gz”.

file[] files = directoryfile.listfiles(

new filenamefilter() {

public boolean accept(file dir, string name) {

final string namesansgz = trim(name, “.gz”);

return namesansgz.endswith(“.csv”)

|| namesansgz.endswith(“.json”);

}

});

if (files == null) {

system.out.println(“directory " + directoryfile + " not found”);

files = new file[0];

}

// build a map from table name to table; each file becomes a table.

final immutablemap.builder<string, table> builder = immutablemap.builder();

for (file file : files) {

string tablename = trim(file.getname(), “.gz”);

final string tablenamesansjson = trimornull(tablename, “.json”);

if (tablenamesansjson != null) {

jsontable table = new jsontable(file);

builder.put(tablenamesansjson, table);

continue;

}

tablename = trim(tablename, “.csv”);

final table table = createtable(file);

builder.put(tablename, table);

}

return builder.build();

}

/** creates different sub-type of table based on the “flavor” attribute. */

private table createtable(file file) {

switch (flavor) {

case translatable:

return new csvtranslatabletable(file, null);

case scannable:

return new csvscannabletable(file, null);

case filterable:

return new csvfilterabletable(file, null);

default:

throw new assertionerror("unknown flavor " + flavor);

}

}

schema会扫描指定路径,找到所有以.csv/结尾的文件。在本例中,指定路径是 target/test-classes/sales,路径中包含文件’emps.csv’和’depts.csv’,这两个文件会转换成表empsdepts

表和视图


值得注意的是,我们在模型文件(model)里并不需要定义任何表,schema会自动创建的。 你可以额外扩展一些表(tables),使用这个schema中其他表的属性。

让我们看看如何创建一个重要且常用的一种表——视图。

在写一个查询时,视图就相当于一个table,但它不存储数据。它通过执行查询来生成数据。在查询转换为执行计划时,视图会被展开,所以查询执行器可以执行一些优化策略,例如移除一些select子句中存在但在最终结果中没有用到的表达式。

举个栗子:

{

version: ‘1.0’,

defaultschema: ‘sales’,

schemas: [

{

name: ‘sales’,

type: ‘custom’,

factory: ‘org.apache.calcite.adapter.csv.csvschemafactory’,

operand: {

directory: ‘target/test-classes/sales’

},

tables: [

{

name: ‘female_emps’,

type: ‘view’,

sql: ‘select * from emps where gender = ‘f’’

}

]

}

]

}

栗子中type:view这一行将female_emps定义为一个视图,而不是常规表或者是自定义表。注意通常在json文件里,定义view的时候,需要对单引号进行转义。

用json来定义长字符串易用性不太高,因此calcite支持了一种替代语法。如果你的视图定义中有长sql语句,可以使用多行来定义一个长字符串:

{

name: ‘female_emps’,

type: ‘view’,

sql: [

‘select * from emps’,

‘where gender = ‘f’’

]

}

现在我们定义了一个视图(view),我们可以再查询中使用它就像使用普通表(table)一样:

sqlline> select e.name, d.name from female_emps as e join depts as d on e.deptno = d.deptno;

±-------±-----------+

|  name  |    name    |

±-------±-----------+

| wilma  | marketing  |

±-------±-----------+

自定义表


自定义表是由用户定义的代码来实现定义的,不需要额外自定义schema

继续举个栗子model-with-custom-table.json

{

version: ‘1.0’,

defaultschema: ‘custom_table’,

schemas: [

{

name: ‘custom_table’,

tables: [

{

name: ‘emps’,

type: ‘custom’,

factory: ‘org.apache.calcite.adapter.csv.csvtablefactory’,

operand: {

file: ‘target/test-classes/sales/emps.csv.gz’,

flavor: “scannable”

}

}

]

}

]

}

我们可以一样来查询表数据:

sqlline> !connect jdbc:calcite:model=target/test-classes/model-with-custom-table.json admin admin

sqlline> select empno, name from custom_table.emps;

±-------±-------+

| empno  |  name  |

±-------±-------+

| 100    | fred   |

| 110    | eric   |

| 110    | john   |

| 120    | wilma  |

| 130    | alice  |

±-------±-------+

上面的schema是通用格式,包含了一个自定义表org.apache.calcite.adapter.csv.csvtablefactory,这个类实现了calcite中的tablefactory接口。它在create方法里实例化了csvscannabletable,将model文件中的file参数传递过去。

public csvtable create(schemaplus schema, string name,

map<string, object> map, reldatatype rowtype) {

string filename = (string) map.get(“file”);

final file file = new file(filename);

final relprotodatatype protorowtype =

rowtype != null ? reldatatypeimpl.proto(rowtype) : null;

return new csvscannabletable(file, protorowtype);

}

通常做法是实现一个自定义表(a custom table)来替代实现一个自定义库(a custom schema)。两个方法最后都会创建一个table接口的实例,但是自定义表无需重新实现元数据(metadata)获取部分。(csvtablefactorycsvschema一样,都创建了csvscannabletable,但是自定表实现就不需要实现在文件系统里检索.csv文件。)

自定义表(table)要求开发者在model上执有多操作(开发者需要在model文件中显式指定每一个table和它对应的文件),同时也提供给了开发者更多的控制选项(例如,为每一个table提供不同参数)。

模型中的注释


注释使用语法 /* ... */ 和 //:

{

version: ‘1.0’,

/* 多行

注释 */

defaultschema: ‘custom_table’,

// 单行注释

schemas: [

]

}

(注释不是标准json格式,但不会造成影响。)

使用查询计划来优化查询


目前来看表(table)实现和查询都没有问题,因为我们的表中并没有大量的数据。但如果你的自定义表(table)有,例如,有100列和100万行数据,你肯定希望用户在每次查询过程中不检索全量数据。你会希望calcite通过适配器来进行衡量,并找到一个更有效的方法来访问数据。

这个衡量过程是一个简单的查询优化格式。calcite是通过添加执行器规则(planner rules)来支持查询优化的。执行器规则(planner rules)通过在查询解析中寻找指定模式(patterns)(例如在某个项目中匹配到某种类型的table是生效),使用实现优化后的新节点替换寻找到节点。

执行器规则(planner rules)也是可扩展的,就像schemastables一样。所以如果你有一些存储下来的数据希望通过sql访问它,首先需要定义一个自定义表或是schema,然后再去定义一些能使数据访问高效的规则。

为了查看效果,我们可以使用一个执行器规则(planner rules)来访问一个csv文件中的某些子列集合。我们可以在两个相似的schema中执行同样的查询:

sqlline> !connect jdbc:calcite:model=target/test-classes/model.json admin admin

sqlline> explain plan for select name from emps;

±----------------------------------------------------+

| plan                                                |

±----------------------------------------------------+

| enumerablecalcrel(expr#0…9=[{inputs}], name=[$t1]) |

|   enumerabletablescan(table=[[sales, emps]])        |

±----------------------------------------------------+

sqlline> !connect jdbc:calcite:model=target/test-classes/smart.json admin admin

sqlline> explain plan for select name from emps;

±----------------------------------------------------+

| plan                                                |

±----------------------------------------------------+

| enumerablecalcrel(expr#0…9=[{inputs}], name=[$t1]) |

|   csvtablescan(table=[[sales, emps]])               |

±----------------------------------------------------+

这两个计划到底有什么不同呢?通过对比可以发现,在smart.json里只多了一行:

flavor: “translatable”

这会让csvschema携带参数参数falvor = translatable 参数进行创建,并且它的createtable方法会创建csvtranslatabletable,而不是csvscannabletable.

csvtranslatabletable实现了translatabletable.torel()方法来创建csvtablescan. 扫描表(table scan)操作是查询执行树中的叶子节点,默认实现方式是enumerabletablescan,但我们构造了一种不同的的子类型来让规则生效。

下面是完整的代码:

public class csvprojecttablescanrule extends reloptrule {

public static final csvprojecttablescanrule instance =

new csvprojecttablescanrule();

private csvprojecttablescanrule() {

super(

operand(project.class,

operand(csvtablescan.class, none())),

“csvprojecttablescanrule”);

}

@override

public void onmatch(reloptrulecall call) {

final project project = call.rel(0);

final csvtablescan scan = call.rel(1);

int[] fields = getprojectfields(project.getprojects());

if (fields == null) {

// project contains expressions more complex than just field references.

return;

}

call.transformto(

new csvtablescan(

scan.getcluster(),

scan.gettable(),

scan.csvtable,

fields));

}

private int[] getprojectfields(list<rexnode> exps) {

final int[] fields = new int[exps.size()];

for (int i = 0; i < exps.size(); i++) {

final rexnode exp = exps.get(i);

if (exp instanceof rexinputref) {

fields[i] = ((rexinputref) exp).getindex();

} else {

return null; // not a simple projection

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

ettable(),

scan.csvtable,

fields));

}

private int[] getprojectfields(list<rexnode> exps) {

final int[] fields = new int[exps.size()];

for (int i = 0; i < exps.size(); i++) {

final rexnode exp = exps.get(i);

if (exp instanceof rexinputref) {

fields[i] = ((rexinputref) exp).getindex();

} else {

return null; // not a simple projection

[外链图片转存中…(img-nm7kfq3w-1715312814344)]
[外链图片转存中…(img-lpaauzay-1715312814344)]
[外链图片转存中…(img-5yxi3qhj-1715312814344)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com