虚拟多维数据集-代替OLAP

当您做相反的事情并得到相同的结果时...

承担分析(计算/聚合)数据处理的任务,您必须在响应性,速度和便利性之间找到折衷方案。


一些系统建立了良好的索引并被发现,其他系统则可以快速计算和汇总数据,而其他系统则很简单。 在某个地方,必须组织所有随之而来的困难的数据的预加载和索引编制,在某个地方,在计算过程中直接使用的内置或外部物理存储和数据库的基础上,为用户提供源和聚合数据模型的抽象。 无论如何,从程序员到分析师,用户都必须做一个相对较大的工作,从准备原始数据和编译查询,计算模型开始,到在小部件上可视化结果,当然,“性感”-漂亮,反应灵敏且易于理解-否则所有完成的工作都会花光。 通常,很幸运,在选择解决方案的过程中,我们注意到乍一看如何将一个简单易懂的任务发展成为一个令人毛骨悚然的怪物,这与可用的手段无济于事,我们需要紧急发明一些东西-一辆“有二十一点和妓女”的自行车©。 我们的自行车行驶得非常顺利,甚至可以克服颠簸,并且可以应对以前只能猜到的障碍。


下面将描述虚构的“鲁比克魔方”的原始内部设备的一侧-用于交互式数据可视化的计算处理。


一个简单的任务应该简单地解决,一个困难的任务也应该简单,但是要更长的时间...

开始创建具有较小力量的系统,我们从简单到复杂。 创建一个构造函数后,我们内部确信我们完全了解系统的目的,同时与不做太多事情的愿望和与使所有事物自动化的相反愿望抗争,为所有事物创建框架。 而且,我们一个很棒的框架已经准备就绪,甚至已经在生产中经过测试-jsBeans。 因此,我们开始开发下一个数据处理系统,该系统已经发展壮大,现在同时又是一个自给自足的产品-设计器和开发整个数据处理系统平台的平台。 有条件的是,在本文中我们将其称为“ Rubik's Cube”,以便不做广告,而是描述我们认为有趣的解决方案。


立方体,切片,测量


主要任务-具有不相关数据集(包括异构外部数据库和文件),以从源数据的互连元素及其分析处理的结果组成的多维模型,以在动态仪表板和互连的小部件上进行可视化。


简单地说,例如,作为可点击的仪表板:


学校评定的仪表板示例


在我们的系统中,这样的多维数据模型称为“多维数据集”,并且从字面上代表了称为“切片”的可变数据集的抽象集合,这些数据集通过常见的输出(显示)字段/列或称为“维度”的内部字段互连,并用于过滤和将切片彼此链接。


可以将切片表示为具有参数和可变请求主体的虚拟表或视图( CTE ),具体取决于过滤条件。 最主要的是,输出数据会根据上下文搜索条件(在窗口小部件内部)和全局过滤器而变化,该条件是通过在窗口小部件上选择值并使用基本逻辑功能(AND / OR / NOT)及其组合来构建的。


全局过滤器允许您“旋转魔方”,如视频所示



如果一个切片的输出字段同时是另一个切片中的测量值,且名称相同,则系统会将该字段的值视为“事实”(如果我们在谈论OLAP ),以全局过滤器的形式设置,该过滤器会在计算和汇总期间更改原始数据集。 结果,存在小部件的动态交互,其中显示的指示符的值取决于所选的元素和过滤器。


切片是一组可以通过“测量”(初始值或分析计算结果)更改的数据; 具有输出字段/列,支持的测量值列表和一组具有默认值的参数的特征; 由可视化编辑器中相对优雅的查询描述,该查询支持过滤,排序,分组/聚合,交集(JOIN),并集(UNION),递归和其他操作。


彼此用作源的切片描述了多维数据集的内部结构,例如:


多维数据集结构示例


编辑器中的切片器示例:


切片器请求编辑器示例


切片支持在输出字段中显式指定的两个度量,并继承查询源的度量-这意味着即使其他切片源的更改也可以更改切片的输出。 换句话说,切片的结果不仅可以通过输出字段进行过滤,还可以通过内部字段(在查询深度内的某个地方,直到主数据库表)对源的度量进行过滤。


系统会在执行时根据当前的全局过滤器和输入参数自动扩展和更改查询结构,然后根据多维数据集模型,声明的度量和切片将查询结构更深地拖动到查询中。


从字面上看,当用户在多个小部件上提交或选择了值时,一个简单的全局过滤器的示例:


仪表板上的全局过滤器示例


全局过滤器存储在JSON请求中:


请求主体中全局过滤器的JSON示例


该请求已经以准备好的形式到达了主要来源(数据库),已经经历了几个主要阶段:


  • 考虑到当前的全局过滤器,请求组装,包括选择和嵌入最佳切片(当过滤器不存在或简单时,您可以选择简单/快速切片;当过滤器很复杂时-具有复杂结构的切片和其他测量);
  • 嵌入全局过滤器并将过滤器添加到查询和子查询的主体;
  • 嵌入宏和模板查询表达式;
  • 查询优化,包括删除未使用的字段和表达式;
  • 查询主数据库的详细信息的其他操作(例如,如果我们谈论的是SQL并且数据库不包含WITH,则将嵌入命名查询)。

最后一步是将请求转换为主要来源的格式,例如在SQL中:


带有集成过滤器的最终请求示例


当来源不同时


通常,当您必须使用单个数据仓库时,一切都是简单明了的。 但是,当它们中的几个存在根本不同时,您就必须为每个特定任务应用不同的技巧。 而且,您始终希望拥有一个通用的解决方案,该解决方案始终是最合适的,最好是开箱即用,并且只需进行少量修改即可。 为此,另一种抽象的要求是:在数据仓库上,首先,实现查询格式和语言的协调,其次,确保数据相互依赖性,至少在查询中对另一个来源的值进行查询的其他过滤条件级别。


为此,我们开发了一种通用查询语言,该语言既适合表示多维数据集数据的虚拟模型,又适合通过将请求转换为所需的格式和语言来处理有条件的任意存储。 碰巧的是,查询语言原本旨在简单地映射和过滤来自各种来源的数据,但很容易发展成为一种成熟的数据搜索和处理语言,它使人们可以在几页和许多子查询中构造从最简单到最复杂的计算结构。


来源可以分为三种类型:


  1. 需要下载到系统的数据文件;
  2. 支持完整数据处理和其他操作的数据库;
  3. 仅支持带或不带过滤的数据提取的存储,包括各种外部服务。

第一种类型的一切都明确无误-导入模块集成在系统中,该模块解析各种输入格式并将结果浸入存储库中。 对于导入,还开发了特殊的构造函数,应单独讨论。


第二种是独立的数据库,对于这些数据库,您只需要将原始请求转换为请求的所需格式和语言(方言)即可。


第三种类型至少需要后处理数据。 而且所有类型同时也可能需要后处理-相交,并集,聚合和最终计算。 当需要在一个数据库中执行数据处理时,要考虑到另一个外部的过滤结果,就会发生这种情况。


最简单的示例是在一个数据库中执行模糊搜索时,并且在输出时,有必要考虑搜索结果来获取存储在另一台服务器上另一个数据库中的指标的集合。


为了实现这种方案的工作,我们的系统中实现了一个简单的算法-初始解释由多个解释器同时准备,每个解释器可以在不兼容时拒绝执行请求,或者返回带有数据的迭代器,或者转换请求并由另一个解释器启动下一个请求准备链的工作。 。 结果,对于单个请求,我们从一个到多个惰性迭代器,它们形成相同的结果,但是以不同的方式从中选择最佳的迭代器(根据开发人员在配置中定义的各种标准)。


迭代器选择策略在配置或查询参数中指定。 当前,支持以下几种主要策略:


  • 首先,任何,最后;
  • 按数据库的优先级类型;
  • 根据构成迭代器的链的优先级;
  • 通过“请求权重”的权重函数;
  • 根据第一个结果-并行启动所有迭代器,并且预期第一个结果,因此,使用最快的迭代器,其余的则关闭。

通过对一个输入请求进行这样的组合,我们可以使用不同的源和不同的执行策略来执行其几个选项-选择将在其中执行请求的主要部分的主数据库/目标数据库以及结果的最终组装。


如果目标DBMS支持外部源的连接,则可以创建一个反向电路,在该电路中DBMS连接到系统API,以接收来自系统的少量数据,例如,“就地”过滤大量数据。 这样的集成对最终用户和分析人员是透明的-多维数据集模型不变,并且所有操作均由系统自动执行。


在一个查询中查询多个集成数据库时的简化序列图


对于更复杂的情况,该系统在非凡的H2嵌入式数据库引擎上实现了内部内存中查询解释器,该引擎可即时集成任何受支持的数据库。 从字面上看,它是这样工作的-请求按源组分为几部分,发送执行,然后在H2中的内存中执行结果的汇编和最终处理。


乍一看,这种内部解释器级别的数据集成方案似乎“困难”,如果您必须处理大量输入数据并且需要在对来自外部源的集合进行交集和关联之后进行计算,那么这是正确的。 实际上,这种情况是部分均衡的-同时,请求是由不同版本的多个处理程序执行的,因此,解释器仅在最极端的情况下用作开箱即用的通用解决方案。 最终,任何集成都受到准备,通过网络传输和接收数据的典型运输成本的限制,而这是完全不同的任务。


技术方面


从技术角度(您可能无法避免)触及此主题,该系统还根据原理进行了设计-可以做更多的事情,但要尽可能简化。


数据处理系统是在jsBeans客户端-服务器框架之上实现的,它是一组附加模块和特定的组装项目。 jsBeans反过来用Java实现,可以用作应用服务器,大致上是一堆Rhino,Jetty和Akka,并且还包括我们团队开发的客户端-服务器Bean技术以及经过数年成功应用而组装的丰富组件库。


Rubik的多维数据集以许多js-bins(* .jsb文件)的形式完全并完全用JavaScript实现,其中有些仅在服务器上运行。 另一部分在客户端上,其余部分是一个组件,作为一个分布式整体起作用,它们的各个部分相互交互,对开发人员透明,但在开发人员的控制下。 Js-bins可以具有不同的生活策略,例如在有或没有用户会话的情况下等等。 Bean是同构的;它允许客户端和服务器将其作为常规类的虚拟实例来使用。 该bin由单个文件描述,包括三个部分-客户端上运行的字段和方法,服务器字段以及公用同步字段的一部分。


既然这篇文章已经变得冗长了,以免引起读者的兴趣,现在是时候完成本文了,目的是尽快描述JsBeans和我们基于它的项目的实现中的细节和最有趣的体系结构解决方案-构造的可视化子系统,分析过程,主题领域的本体设计器,语言查询,数据导入等


为什么这样


这从来没有发生过,再来一次...

最初,主要数据集很少。 主题领域和任务已完全指定。 看来,为什么要这样折磨? 任务看起来很简单,每个人都希望立即获得结果-尤其是当快速解决方案浮出水面时,正确的解决方案需要毅力和平衡的决定,并遵守原始设置。 我们沿着相反的方向,从复杂的长期解决方案,到简单而又快速的解决方案,沿着概括特定问题的道路前进。


主要条件是,即使新的主题领域和分析需求与以前的需求截然不同,新的仪表盘也必须快速构建。 显然,您甚至不会猜到未来需求的一半,该系统首先必须是易弯曲的。 完善组件库,分析算法,连接新类型的源是系统改编的组成部分。 换句话说,这群人工作了-分析师构建了查询和仪表板,程序员迅速意识到了对它们的新需求。 而且,作为程序员,我们最初试图在将来简化我们的工作,以免损害可用性。


然后,系统立即被创建为通用且自适应的-我们构建了一个“由构造函数构造的构造函数”,在先前创建的具有类似但更通用目的的框架之上开发了一个框架。


根据统一国家考试和奥林匹克竞赛的结果对莫斯科学校进行的评分是从莫斯科政府开放数据门户中卸载以上述方式构造的仪表板的一个示例。


Cube-Rubik是开发信息和分析系统的基本平台。 设计为jsBean的分支和逻辑延续。 它包括解决收集,处理,分析(计算和面向过程)和可视化问题的所有必要模块。


jsBeans是一个同构的全栈Web框架,它实现了客户端服务器JavaScript bean技术,并使用开放许可作为通用工具进行了开发。 在使用过程中,它已经证明自己非常好,在大多数情况下,非常适合我们面前的任务。

Source: https://habr.com/ru/post/zh-CN452154/


All Articles