概念
格表示星形(或雪花)模式,而不是通用模式(A lattice represents a star (or snowflake) schema, not a general schema. )。
特别地,所有关系必须是多对一的,从星形中心的事实表向外发散。
该名称源自数学:格是一个部分有序集,其中任意两个元素都有唯一的最大下界和最小上界。
[HRU96]观察到数据立方体的可能物化集合形成一个格,并提出了选择一组好的物化集合的算法。Calcite的推荐算法源自此算法。
格定义使用SQL语句表示星形。SQL是一个有用的简写,可以表示多个表连接在一起,并为列名分配别名(这比发明一种新语言来表示关系、连接条件和基数更方便)。
与常规SQL不同,顺序很重要。如果在FROM子句中将A放在B之前,并在A和B之间进行连接,那么您正在说明从A到B存在多对一的外键关系。(例如,在示例格中,销售事实表出现在时间维度表和产品维度表之前。产品维度表出现在产品类别外部维度表之前,在雪花结构的一支下方。)
格暗示了约束。在A到B的关系中,A上有一个外键(即A的每个外键值都有一个对应的B的键值),B上有一个唯一键(即没有键值出现超过一次)。这些约束非常重要,因为它允许规划器删除不使用列的表的连接,并知道查询结果不会改变。
Calcite不检查这些约束。如果违反了这些约束,Calcite将返回错误的结果。
格是一个大的虚拟连接视图。它没有物化(因为由于去规范化,它将比星形模式大几倍),您可能不想对其进行查询(列太多了)。那么它有什么用?正如我们上面所说,(a)格声明了一些非常有用的主键和外键约束,(b)它帮助查询规划器将用户查询映射到过滤-连接-聚合物化视图(DW查询中最有用的类型的物化视图),(c)为Calcite提供了一个框架,用于收集关于数据量和用户查询的统计信息,(d)允许Calcite自动设计和填充物化视图。
大多数星形模式模型都要求您选择某一列是维度还是度量。在格中,每一列都是维度列。(也就是说,它可以成为查询星形模式特定维度的GROUP BY子句中的一列)。任何列也可以用作度量;您通过给出列和一个聚合函数来定义度量。
如果“unit_sales”更多地用作度量而不是维度,那没问题。Calcite的算法应该注意到它很少被聚合,并且不倾向于创建聚合在其上的瓦片。(我所说的“应该”是指“可能并且有一天会”。该算法目前在设计瓦片时不考虑查询历史。)
但是有人可能想知道少于5个项目的订单是否比多于100个项目的订单更有利可图。突然,“unit_sales”就成了维度。如果声明列为维度列几乎没有成本,我认为就让它们全部成为维度列吧。
该模型允许特定表多次使用,带有不同的表别名。您可以使用此功能来模拟例如OrderDate和ShipDate,其中使用两个别名到时间维度表。
大多数SQL系统要求视图中的列名是唯一的。这在格中很难实现,因为您通常会在连接中包含主键和外键列。
因此,Calcite允许您以两种方式引用列。如果列是唯一的,您可以使用其名称,[“unit_sales”]。
无论列在格中是否唯一,在其表中它都是唯一的,因此您可以使用其表别名限定它。例如:
[“sales”, “unit_sales”] [“ship_date”, “time_id”] [“order_date”, “time_id”]
“瓦片”是格中的一个物化表,具有特定的维度。
格JSON元素的“瓦片”属性定义了要物化的初始一组瓦片。
演示
创建包含格的模型:
{
"version": "1.0",
"defaultSchema": "foodmart",
"schemas": [
{
"type": "jdbc",
"name": "foodmart",
"jdbcUser": "FOODMART",
"jdbcPassword": "FOODMART",
"jdbcUrl": "jdbc:hsqldb:res:foodmart",
"jdbcSchema": "foodmart"
},
{
"name": "adhoc",
"lattices": [
{
"name": "star",
"sql": [
"select 1 from \"foodmart\".\"sales_fact_1997\" as \"s\"",
"join \"foodmart\".\"product\" as \"p\" using (\"product_id\")",
"join \"foodmart\".\"time_by_day\" as \"t\" using (\"time_id\")",
"join \"foodmart\".\"product_class\" as \"pc\" on \"p\".\"product_class_id\" = \"pc\".\"product_class_id\""
],
"auto": true,
"algorithm": true,
"rowCountEstimate": 86837,
"defaultMeasures": [
{
"agg": "count"
}
]
}
]
}
]
}
这是hsqldb-foodmart-lattice-model.json的简化版本,不包含“tiles”属性,因为我们将自动生成瓦片。
让我们登录sqlline并连接到此模式:
$ sqlline version 1.3.0
sqlline> !connect jdbc:calcite:model=core/src/test/resources/hsqldb-foodmart-lattice-model.json "sa" ""
您会注意到连接需要几秒钟。Calcite正在运行优化算法,并创建和填充物化视图。让我们运行一个查询并检查其计划:
sqlline> select "the_year","the_month", count(*) as c
. . . .> from "sales_fact_1997"
. . . .> join "time_by_day" using ("time_id")
. . . .> group by "the_year","the_month";
sqlline> explain plan for
. . . .> select "the_year","the_month", count(*) as c
. . . .> from "sales_fact_1997"
. . . .> join "time_by_day" using ("time_id")
. . . .> group by "the_year","the_month";
查询给出了正确的答案,但计划有些令人惊讶。它没有读取sales_fact_1997或time_by_day表,而是从一个名为m{16, 17, 27, 31, 32, 36, 37}的表中读取。这是在连接开始时创建的其中一个瓦片。
这是一个真实的表,您甚至可以直接查询它。它只有120行,因此是回答查询的更有效方法:
sqlline> !describe "adhoc"."m{16, 17, 27, 31, 32, 36, 37}"
sqlline> select count(*) as c
. . . .> from "adhoc"."m{16, 17, 27, 31, 32, 36, 37}";
让我们列出表,您将看到几个更多的瓦片。还有foodmart模式的表,以及系统表TABLES和COLUMNS,以及格本身,它显示为一个名为star的表。
sqlline> !tables
统计 Statistics
选择物化格的瓦片的算法取决于许多统计信息。它需要知道对于正在考虑物化的每个列(a、b、c)的组合,从star中选择count(distinct a, b, c)。
因此,该算法在具有许多行和列的模式上需要很长时间。
我们正在开发一个数据分析器来解决这个问题。
格建议者 Lattice suggester
如果您定义了一个格,Calcite将在该格内进行自我调整。但如果您没有定义一个格呢?
这就是格建议者的用途,它根据传入的查询构建格。创建一个具有“autoLattice”: true的模型:
{
"version": "1.0",
"defaultSchema": "foodmart",
"schemas": [
{
"type": "jdbc",
"name": "foodmart",
"jdbcUser": "FOODMART",
"jdbcPassword": "FOODMART",
"jdbcUrl": "jdbc:hsqldb:res:foodmart",
"jdbcSchema": "foodmart"
},
{
"name": "adhoc",
"autoLattice": true
}
]
}
这是hsqldb-foodmart-lattice-model.json的简化版本。
当您运行查询时,Calcite将开始基于这些查询构建格。每个格基于一个特定的事实表。
随着对该事实表的更多查询,它将演化格,将更多维度表连接到星型表,并添加度量。
然后,每个格将根据数据和查询进行自我优化。目标是创建相对较小但基于更频繁使用的属性和度量的汇总表(瓦片)。
这个功能仍处于实验阶段,但有潜力使数据库比以前更加“自我调整”。
进一步的方向
以下是一些尚未实现的想法:
- 构建瓦片的算法考虑了过去查询的日志。
- 物化视图管理器查看传入的查询并为它们构建瓦片。
- 物化视图管理器删除不活跃使用的瓦片。
- 格建议者根据传入的查询添加格,将现有格的瓦片转移到新格,并删除不再使用的格。
- 覆盖表的水平切片的瓦片;以及一种重写算法,可以通过拼接多个瓦片并访问原始数据来填补空洞来回答查询。
- 提供API来在基础数据发生变化时使瓦片或瓦片的水平切片无效化。
参考资料
https://calcite.apache.org/docs/lattice.html