六个月前,我写了一篇文章,介绍了为什么我们用 Apache Doris 取代 ClickHouse 作为我们数据管理系统的 OLAP 引擎。当时,我们正在为 SQL 语句的自动生成而苦苦挣扎。日子一天天过去,我们已经取得了足够大的进展,足以为你们提供参考,所以我又来了。
我们采用大型语言模型 (LLM) 来增强基于 Doris 的 OLAP 服务。
我们的动机是让我们的内部员工免于 SQL 编写的陡峭学习曲线。因此,我们使用LLM作为中间。它将自然语言问题转换为SQL语句,并将SQL发送到OLAP引擎执行。
就像所有与人工智能相关的经历一样,我们也遇到了一些摩擦:
LLM 不理解数据术语,如“字段”、“行”、“列”和“表”。相反,他们可以完美地翻译“公司收入”和“DAU”等业务术语,这些术语基本上就是字段/行/列的含义。这意味着只有分析师在输入问题时使用正确的单词来引用他们所需的指标,它才能发挥良好的作用。
我们使用的法学硕士推理速度很慢。需要10多秒才能响应。由于它是按代币收费的,所以成本效益就成了问题。
尽管法学硕士接受了大量公共数据集的培训,但它对利基知识的了解不足。在我们的例子中,法学硕士对独立歌曲非常不熟悉,所以即使这些歌曲包含在我们的数据库中,法学硕士也无法正确识别它们。
有时,我们的输入问题需要充分且最新的法律、政治、财务和监管信息,而这些信息很难包含在训练数据集或知识库中。我们需要将法学硕士与更广泛的信息库连接起来,以便执行更多样化的任务。
我们将这些问题一一解决。
对于问题 1,我们在 LLM 和 OLAP 引擎之间引入了一个语义层。该层将业务术语转换为相应的数据字段。它可以从各种自然语言措辞中识别数据过滤条件,将其与所涉及的指标相关联,然后生成SQL语句。
除此之外,语义层还可以优化计算逻辑。当分析师输入一个涉及复杂查询的问题时,比如多表连接,语义层可以将其拆分为多个单表查询,以减少语义失真。
为了提高使用LLM的成本效益,我们评估了所有场景的计算复杂度,例如度量计算、详细记录检索和用户细分。然后,我们创建规则并将 LLM 解析步骤专用于复杂的任务。这意味着对于简单的计算任务,它将跳过解析。
例如,当分析师输入“告诉我主要音乐平台的收入”时,LLM 会识别出这个问题只涉及几个指标或维度,因此不会进一步解析它,而是直接发送给 SQL 生成和执行。这可以很大程度上缩短查询响应时间并减少API开销。
为了使 LLM 具备利基知识,我们在 LLM 的上游添加了一个架构映射器。 Schema Mapper将输入问题映射到外部知识库,然后LLM将进行解析。
我们不断测试和优化模式映射器。我们对外部知识库中的内容进行分类和评分,并进行各种级别的映射(全文映射和模糊映射)以实现更好的语义解析。
我们通过插件将LLM连接到更多领域的信息,针对不同类型的插件我们有不同的集成方式:
嵌入本地文件:当我们需要“教授”LLM最新的监管政策(通常是文本文件)时,这特别有用。首先,系统对本地文本文件进行向量化,执行语义搜索以在本地文件中查找匹配或相似的术语,提取相关内容并将其放入LLM解析窗口中以生成输出。
第三方插件:市场上充满了专为各个领域设计的第三方插件。有了它们,法学硕士就能够处理广泛的主题。每个插件都有自己的提示和调用功能。一旦输入问题出现提示,就会调用相关插件。
完成以上四点优化后,SuperSonic框架就形成了。
现在让我带您了解这个框架:
分析师输入一个问题。
模式映射器将问题映射到外部知识库。
如果外部知识库中有匹配字段,该问题将不会被LLM解析。相反,度量计算公式将触发 OLAP 引擎开始查询。如果没有匹配的字段,该问题将进入LLM。
根据预定义的规则,法学硕士对问题的复杂程度进行评级。如果是简单查询,则直接进入OLAP引擎;如果是复杂的查询,则会进行语义解析并转换为DSL语句。
在语义层,DSL语句将根据其查询场景进行拆分。例如,如果是多表连接查询,这一层会生成多条单表查询SQL语句。
例子:
为了回答某首歌曲是否可以在综艺节目中表演,系统会检索OLAP数据仓库以获取该歌曲的详细信息,并通过商业用途查询第三方插件的结果呈现出来。
至于这个框架的OLAP部分,经过几轮的架构演进,就是我们现在的OLAP pipeline的样子。
原始数据被分类为由分析师自定义的标签和指标。标签和指标统一管理,避免定义不一致。然后,它们被组合成用于各种查询的各种标签集和度量集。
我们从架构优化经验中为您总结了两个主要要点。
1. 简化链接
在采用 Apache Doris 之前,我们曾经使用 ClickHouse 来加速标签和指标的计算,使用 Elasticsearch 来处理维度数据。这是两个分析引擎,需要我们使查询语句适应它们。这是一个高维护成本。
因此,我们用 Apache Doris 替换了 ClickHouse,并利用Elasticsearch Catalog功能将 Elasticsearch 数据连接到 Doris。这样,我们就让 Doris 成为我们的统一查询网关。
2. 拆分平板
在 OLAP 架构的早期版本中,我们过去常常将数据放入平面表中,这使事情变得棘手。一方面,平面表吸收了上游的所有写入延迟,这增加了数据实时性的巨大损失。另一方面,平面表中 50% 的数据是维度数据,很少更新。每一个新的平板表都会带来一些庞大的维度数据,消耗大量的存储空间。
因此,我们将平面表分为指标表和维度表。由于它们的更新速度不同,我们将它们放入不同的数据模型中。
指标表:我们将指标数据排列在Apache Doris的Aggregate Key模型中,这意味着新数据将通过SUM、MAX、MIN等方式与旧数据合并。
维度表:这些表属于Apache Doris的Unique Key模型,这意味着新的数据记录将取代旧的数据记录。这可以大大提高我们查询场景中的性能。
您可能会问,这是否会导致查询出现问题,因为大多数查询都需要来自两种类型表的数据?别担心,我们通过 Doris 的 Rollup 功能来解决这个问题。在基表的基础上,我们可以选择创建Rollup视图所需的维度,这将自动执行GROUP BY
。这使我们无需为每个 Rollup 视图定义标签,并大大加快查询速度。
根据我们使用 Apache Doris 的经验,我们还发现其他一些方便的功能,所以我也在这里为您列出它们:
1. 物化视图
物化视图是预先计算的数据集。当您经常需要访问某些维度的数据时,这是一种加速查询的方法。在这些场景中,我们根据原始标签和指标定义派生标签和指标。例如,我们通过组合指标 1、指标 2 和指标 3 创建派生指标: sum(m1+m2+m3)
。然后,我们可以为它创建一个物化视图。根据Doris的发布时间表,2.1版本将支持多表物化视图,我们对此非常期待。
2.Flink-Doris-Connector
这是为了数据摄取中的 Exactly-Once 保证。 Flink-Doris-Connector 实现了检查点机制和两阶段提交,并允许从关系数据库到 Doris 的数据自动同步。
3. 压实
当聚合任务数量或数据量对 Flink 来说不堪重负时,数据压缩可能会出现巨大的延迟。我们通过垂直压缩和分段压缩来解决这个问题。 Vertical Compaction支持仅加载部分列,因此可以减少压缩平板时的存储消耗。 Segment Compaction可以避免数据写入时生成过多的Segment,并且允许在写入的同时进行Compaction。
为了降低成本和提高服务可用性,我们计划测试新发布的 Doris 存储计算分离和跨集群复制,我们欢迎有关 SuperSonic 框架和 Apache Doris 项目的任何想法和意见。