传统OLAP系统在业务中往往扮演着相对静态的角色,通过分析海量数据(如预先计算的View、模型等)获得业务洞察等),并将这些海量数据分析得出的结果通过另外一个系统(如HBase、Redis、MySQL等)提供在线数据服务。这里的服务(Serving)和分析(Analytical)是一个独立的过程。不同的是,实际的业务决策过程往往是一个不断优化的在线过程。服务的过程中会产生大量的新数据,我们需要对这些新数据进行复杂的分析。分析产生的洞察力实时反馈给服务,使业务决策更加实时,从而创造更大的商业价值。Hologres定位为一站式实时数据仓库,整合分析能力(Analytical)和在线服务(Serving),减少数据碎片化和移动。本文内容将围绕Hologres的服务能力(核心是枚举能力),介绍Hologres有哪些服务能力以及背后的实现原理。通常,我们所说的枚举场景指的是Key/Value查询场景,在在线服务中被广泛使用。由于枚举场景的广泛需求,市面上有多种定位于支持高吞吐、低延迟枚举场景的KV数据库,比如大家熟知的HBase,就是通过一套自定义的方式来提供枚举APIs在许多业务场景中取得更好结果的能力。但是HBase在实际使用中也存在一定的不足,这也使得很多业务从HBase迁移到Hologres。主要有以下几点:当数据规模达到一定程度时,HBase的性能会下降。它不能满足大规模的point-check计算,同时它的稳定性也变得不尽如人意。需要经验丰富的运维支持。HBase提供了自定义的API,上手需要一定的成本。Hologres通过SQL直接提供高吞吐量、低延迟的枚举服务。与其他提供自定义API的KV系统相比,SQL接口无疑更加简单易用。HBase采用SchemaFree设计,没有数据类型。也给数据质量的检查和纠错带来了复杂性,导致查错难、纠错难。Hologres几乎拥有兼容Postgres的所有主流数据类型,可以通过Insert/Select/Update/Delete标准SQL语句查看和更新??数据。Hologres中的枚举场景是指基于行存储表主键(PK)的查询。--建行表开始;创建表public.holotest("a"textNOTNULL,"b"textNOTNULL,"c"textNOTNULL,"d"textNOTNULL,"e"textNOTNULL,PRIMARYKEY(a,b));CALLSET_TABLE_PROPERTY('public.holotest','orientation','row');CALLSET_TABLE_PROPERTY('public.holotest','time_to_live_in_seconds','3153600000');COMMIT;--通过SQL查询hologresselect*fromtablewherepk=?;--一次查询一个点select*fromtablewherepkin(?,?,?,?,?);--一次查询多个点。查询场景的技术实现难点一般情况下,一条SQL语句的执行需要先通过SQLParser解析成AST(抽象语法树),再通过QueryOptimizer处理生成Plan(可执行计划),最后执行Plan得到计算结果。但是,如果想通过SQL实现高吞吐、低延迟、稳定的查询服务,必须克服以下困难:SQL接口如何在不破坏PostgreSQL生态的情况下实现高QPS?如何减少甚至避免SQL解析和优化器的开销?高效的客户端SDK如何与后端存储交互?如何以低消耗实现高并发交互如何减少消息传递过程中的开销如何感知后端压力并协同实现最佳吞吐量和延迟如何让后端存储在高性能下更稳定?如何最大限度地利用cpu资源如何减少各种内存的分配和拷贝,避免热键等导致系统不稳定的问题如何减少冷数据IO的影响在克服了以上三类困难之后,整体的工作方式可以很简单:在接入层(FrontEnd),直接通过ClientSDK与后端存储通信。下面将介绍Hologres是如何克服以上三大难点,实现高通量和低延迟的枚举。减少和避免SQL解析和优化器的开销。查询优化器执行快捷方式。由于query查询足够简单,Hologres的QueryOptimizer进行了相应的shortcut。query查询并没有进入Optimizer的完整流程。Query进入FrontEnd后,会被FixedPlanner处理,生成对应的FixedPlan(枚举的物理计划)。FixedPlanner非常轻量,不需要经过等价变换、逻辑优化、物理优化等任何步骤。它只是基于AST树进行一些简单的分析,并构造出相应的FixedPlan,尽可能避免优化器的开销。PreparedStatementQueryOptimizer虽然对query查询进行了捷径,但是Query进入FrontEnd后的解析开销依然存在,并没有完全避免QueryOptimizer的开销。Hologres与Postgres兼容。Postgres的前后端通信协议包括扩展协议和简单协议:简单协议:是一种一次性交互协议。解析、执行,返回结果给Client。在简单的协议中,服务器不可避免地至少需要解析接收到的SQL以了解其语义。扩展协议:Client和Server的交互是分多个阶段完成的,整体大致可以分为两个阶段。第一阶段:客户端在服务器端定义一个带有名称的语句,并生成该语句对应的通用计划(不绑定具体参数的通用计划)。第二阶段:用户通过发送特定参数执行第一阶段定义的Statement。第二阶段可以重复多次,每次都带上第一阶段定义的Statement名称和执行需要的参数,使用第一阶段生成的通用计划执行。由于第二阶段可以通过Statement名称和附带的参数重复执行第一阶段准备的通用计划,因此Frontend中第二段的开销几乎为0。为此,Hologres基于Postgres扩展协议,支持PreparedStatements,使得Frontend检查查询的开销接近于零。高性能内部通信BHClient是Hologres实现的一套与后端存储直接通信的高效PrivateClientSDK。主要有以下优点:1)Reactor模型,全流程无锁异步操作BHClient的工作原理与reactor模型类似,每个targetshard对应一个eventloop,以“无限循环”的方式处理对shard的请求。由于HOS对调度执行单元的抽象,即使在分片很多的情况下,这种工作方式的基础消耗也足够低。2)高效的数据交换协议二进制行,通过定制一套内部数据通信协议二进制行,减少整个交互链路上的内存分配和复制。3)背压和batchbatchingBHClient可以感知后端的压力,进行自适应背压和batchbatching,在不影响原有延迟的情况下,提高系统吞吐量。稳定可靠的后端存储1)LSM(LogStructuredMergeTree)Hologres行存储表使用LSM进行存储。与传统的B+树相比,LSM可以提供更高的写入吞吐量,因为它不会出现任何随机Write,AppendOnly的操作保证了它只会顺序写入磁盘。一个linestoragetablet上会有一个memtable和多个immutablememtable。数据更新将写入memtable。当memtable满了,它会变成一个immtable的memtable。不可变内存表将被刷新到键排序的SST(排序字符串表)文件中。SST文件一旦生成就无法修改,不会出现随机性。写操作。SST文件在文件系统中分层组织。除了第0层的SST文件乱序且有重叠外,其他层的SST文件都是有序的,没有重叠。所以在查询的时候,需要将0层的文件逐一遍历,其他层的文件二分查找即可。底层的SST文件通过Compaction转化为新的SST文件上层,所以下层的数据比上层的数据新,所以一旦在a上找到满足条件的key一定的层级,就不用再去更高层级查询了。2)基于C++纯异步开发,Hologres并不是唯一使用LSM组织和存储数据的系统。LSM在Google的《BigTable》论文中被提出后,很多系统都借鉴了它,比如HBase。Hologres是用C++开发的。与Java相比,原生语言让我们能够追求更极致的性能。同时基于HOS(HologresOperationSystem)提供的异步接口进行纯异步开发。HOS通过抽象ExecutionContext来管理CPU的调度和执行,可以最大限度地利用硬件资源,实现吞吐量最大化。3)IO优化和丰富的Cache机制Hologres实现了非常丰富的Cache机制rowcache、blockcache、iteratorcache、metacache等,以加快热点数据的搜索速度,减少IO访问,避免分配新的内存。当IO不可避免时,Hologres会合并并发IO,并使用wait/notice机制保证IO只被访问一次,减少IO处理量。通过生成文件级字典和压缩,降低了文件物理存储成本和IO访问。综上所述,Hologres致力于一站式实时数据仓库。除了能够处理复杂的OLAP分析场景,还支持超高QPS在线查询服务。通过使用标准的PostgresSDK接口,可以获得低延迟、高吞吐量的在线服务能力,简化学习成本,提高开发效率。
