内存数据库如何使内存的优势发挥作用?
#sql #database #bigdata

内存数据库的数据访问速度可能比主要依赖磁盘存储数据的普通数据库的数量级高几个数量级,因此,内存数据库可以大大提高计算性能和计算性能,并且更适合于高持续性和低延迟业务场景。

但是,当前大多数内存数据库仍然使用SQL模型,而SQL缺少一些必要的数据类型和操作,并且无法充分利用内存功能来实现一些高性能算法。仅将数据和操作从外部存储转移到内存确实可以实现更好的性能,但是它不能充分利用内存的功能,因此无法获得极端性能。

现在让我们看看哪些算法和存储机制适合内存的特征,并可以进一步提高内存数据库的计算速度。

指针式重复使用

我们知道可以通过地址(指针)访问内存。但是,SQL没有内存指针表示的数据对象,并且通常需要在返回结果集时复制数据以形成新的数据表。这不仅会消耗更多的CPU时间(用于复制数据),而且占据更昂贵的内存空间(用于存储复制的数据),导致内存使用率降低。

除了SQL模型中的内存数据库外,SPARK中的RDD中存在相同的问题,而且情况更加严重。为了保持RDD的不变特征,Spark将在每个计算步骤之后复制一个新的RDD,这将导致记忆和CPU中的大量浪费。因此,即使消耗了大量资源,Spark仍然无法实现高性能。相比之下,通常优化了SQL模型内存数据库,并且SQL语句中的计算将尽可能使用内存中的地址,因此此类数据库通常比Spark更好。

但是,由于理论上的局限性,在实现SQL逻辑时,必须复制返回的结果集。如果涉及多步程序操作,则基于上一步的结果集(临时表)需要进一步计算,这将使SQL的缺点非常明显。

实际上,如果没有更改数据结构,我们可以直接使用原始数据的地址来形成结果集,而无需复制数据本身。这样,我们只需要节省一个地址(指针),从而减少CPU和内存消耗。

SPL扩展了SQL的数据类型,并支持 Pointer-style 重用机制。例如,在按订单日期(ODATE)的范围(ODATE)过滤订单表之后,我们想找出订单金额(金额1)大于1000的订单,并且分别大于1000的订单,并分别大于1000然后计算两种订单的交叉点,联合和差异,最后按客户编号(CID)对差异进行分类。 SPL代码大致如下:

    A   B
1   =orders.select(odate>=date(2000,1,1) && odate<=date(2022,1,1))
2   =A1.select(amount1>1000)    =A1.select(amount2>1000)
3   =A2^B2  =A2&B2
4   =A2\B2  =B2\A2
5   =A4.sort(cid)   =B4.sort(cid)

在此代码中,尽管有多个步骤,并且某些中间结果也被多次使用,因为使用的是订单表中记录的指针,但增加的内存职业非常有限,并且时间消耗的时间避免复制记录。

外钥匙预关联

外国密钥关联是指使用一个表(事实表)的非主要密钥字段将另一个键(维度表)关联。例如,使用订单表中的客户号码和产品编号分别将客户表和产品表的主要键关联。实际上,这种关联可能涉及多达七个/八个甚至十二个表,并且也可能存在多层关联。通常,SQL数据库使用哈希联接算法来执行内存连接,并且该算法需要计算和比较哈希值,在此过程中,内存将被占用以存储中间结果。当有许多相关表时,计算性能将急剧下降。

实际上,我们还可以使用内存指针参考机制进行预缔合,也就是说,在系统为时,将事实表中的关联字段的值转换为记录指针的值初始化。由于维度表的关联字段是主要键,因此关联的记录是唯一的,并且将外键值转换为记录指针不会导致错误。在随后的计算中,当它需要引用维度表字段时,我们可以使用指针直接参考,而无需计算和比较哈希值,而需要存储中间结果,从而获得更好的性能。由于SQL没有数据类型的记录指针,因此无法实现前关联。

SPL在原则上支持和实现此 机制。例如,用于与客户表和产品表进行预订购表的代码大致如下:

    A
1   =file("customer.btx").import@b().keys@i(cid)
2   =file("product.btx").import@b().keys@i(pid)
3   =file("orders.btx").import@b().switch(cid,A1;pid,A2)
4   >env(orders,A3)

A1和A2分别加载客户表和产品表。

a3:加载订单表,然后将客户编号CID和产品编号PID转换为相应尺寸表中的记录指针。

a4:将预先相关的订单表存储到后续计算的全局变量中。

系统运行时,按产品供应商过滤订单,然后将客户与客户所在的城市进行分组。代码大致如下:

A
1   =orders.select(pid.supplier=="raqsoft.com").groups(cid.city;sum(pid.price*quantity))

由于顺序表中的PID已转换为产品表中的记录指针,因此可直接使用操作员来参考产品表中的记录。这样,不仅写作更容易,而且计算性能要快得多。

当仅关联两个或三个表时,预先关联和哈希联接之间的差异不是很明显,因为该关联不是最终目标,并且在关联之后将有许多其他操作,并且会有许多其他操作协会操作本身消耗的时间比例相对较小。相比城市与该国相关联,依此类推),预先关联的性能优势将更加明显。

序列编号定位

与外部存储相比,内存的另一个重要特征是它支持高速随机访问,可以通过指定的序列号(即位置)快速从内存表中获取数据。执行搜索计算时,如果要进行搜索的值正好是内存表中目标值的序列数,或者很容易通过到达搜索的值计算目标值的序列数,我们可以使用序列编号直接获取目标记录。此方法可以直接采用搜索结果,而无需进行任何比较,并且其性能不仅比遍历方法好得多,而且比使用索引的搜索算法更好。

不幸的是,SQL基于无序集,并且不能按序列编号采取成员,而是只能按序列编号进行搜索。如果没有索引,则只能通过遍历进行搜索,这将非常慢。即使有索引,它也需要计算哈希值或使用二进制搜索方法,并且速度不如直接定位快。此外,创建索引也将占据很多昂贵的内存。如果数据表中没有序列编号,则需要先排序然后创建序列编号,则性能会更糟。

SPL基于有序集,并提供序列号定位函数。例如,有一个订单表,其中订单号是从1开始的自然号码。在搜索订单号I时,我们只需要在订单表中获取第i-thest记录即可。在另一个示例中,有一个数据表t,从2000年到2022年,每天存储一个数据,现在我们想查询指定日期的记录。尽管日期不是目标值的序列数,但我们可以首先计算从开始日期到指定日期的天数,但该数字是目标值的序列数。之后,我们只需要在表T中使用计算的序列编号记录。使用序列号定位方法在表T中搜索记录的代码大致相同:

    A
1   =date(2022,12,31)-date(1999,12,31)
2   =T_orginal.align@b(to(A1),dt-date(1999,12,31))
3   =env(T,A5)
4   =T(date(2021,4,20)-date(1999,12,31))

a1:计算2000年至2022年的总天数,即8401天。

a2:使用原始表t的记录来计算起始日期以来的天数,然后将其与设置为(a1)的自然数量对齐,即[1,2,3,, 8401]。空置日期将以空填充,并且Align的选项@b表示二进制搜索将在对齐过程中搜索位置,这将使对齐操作更快。

a3:将计算的结果放入全局变量t。

a4:要搜索2021年4月20日的记录,首先在此日期和开始日期之间找到天数(7781天),然后直接在表t。

中拍摄第7781张记录。

a1至a3是对齐计算,用于处理空置日期,可以在系统初始化阶段进行。在搜索计算过程中,可以使用A4中的序列号定位代码获得搜索结果,并且可以将实际搜索日期作为参数传递。

群集维度表

当数据量如此之大以至于一台计算机的内存无法保持时,它需要使用群集加载数据。许多内存数据库还支持分布式计算,该计算通常以将数据划分为多个段的方式,然后将它们分别加载到群集的不同节点的内存中。

JOIN是分布式计算的一项麻烦任务,因为它将涉及多个节点之间的数据传输。在严重的情况下,传输造成的延迟将抵消通过集群共享计算数量获得的收益,以及群集变大但无法提高性能的现象可能会发生。

SQL系统下的分布式数据库通常是将单个机器的哈希联接方法扩展到群集。具体来说,每个节点将根据哈希值将自己的数据分配给其他节点,以确保关联的数据在同一节点上,然后在每个节点上执行单个计算机加入操作。但是,这种方法可能会导致数据分布不幸时严重失衡。在这种情况下,它需要使用外部存储来缓冲分布式数据,否则系统可能由于存储外而崩溃。但是,就我们所知,内存数据库的主要特征只是将数据加载到内存中进行计算,并且一旦使用外部存储来缓冲数据,计算性能就会严重减慢。

实际上,通过外键关联的事实表和尺寸表之间存在很大的区别。事实表通常相对较大,需要将其加载到每个节点的内存中。幸运的是,事实表也更适合分割,每个段的数据彼此独立,节点不需要互相访问。相反,将随机访问尺寸表的记录,事实表的任何段都可以关联所有维度表记录。因此,我们可以利用事实表和维度表之间的差异来加快集群的外键关联。

如果尺寸表相对较小,则将维度表的所有数据加载到每个节点的存储器。通过这种方式,我们可以继续在每个节点中预先合并事实表段和完整维表,从而在关联过程中完全避免网络传输。

如果尺寸表也很大,以至于一台计算机的内存无法保持,则必须将其加载到所有节点的内存中。在这种情况下,没有任何节点可以保留完整的尺寸表,因此在外部密钥关联计算过程中生成网络传输是不可避免的。但是,传输内容不是很大,仅涉及事实表外键和维度表之间相关记录的字段,而事实表的其他字段则无需传输。可以直接实现计算,并且在此过程中没有生成缓冲数据。

SPL原理将维度表与事实表区分开,并实现上述算法,在集群的情况下,可以显着提高外国密钥关联的计算性能。

备用轮模式的容错

在群集系统方面,必须考虑容错,并且内存数据的容错与外部存储的容错不同。外部存储通常使用复制数据的方法,即相同的数据具有多个副本,一旦某个节点失败,仍然可以在其他节点上找到。该机制的存储利用率非常低,仅1/K(k是副本数)。

对于内存中的数据,这种复制图案的容错方法不起作用,因为硬盘是如此便宜,以至于几乎可以无限地扩展其容量,而内存更昂贵,并且在那里。是其容量扩展的上限。因此,对于内存,仅1/K的利用率是不可接受的。

内存的容忍度需要专门的手段与外部存储不同。 SPL提供了备用轮模式的容差机制,该机制将数据分为n段并分别将它们加载到n个节点的内存中。同时,准备k个空闲节点作为备用节点。这样,当一个运行节点失败时,将立即启动某个备用节点,以立即加载失败的节点的数据,并将一个完整数据与其他节点一起重新构造一个群集,以继续提供服务。故障排除后,失败的节点将返回正常状态,可以用作备用节点。整个过程与更换汽车的备用轮的方式非常相似。

该机制的存储器利用率最高可达N/(N+K),该率远高于1/K的复制图案容错机制。在此机制中,将要加载到内存的数据量通常不是很大,并且当节点失败时,即时加载时间并不多,因此可以快速恢复群集服务。

评论和摘要

对于内存数据库的计算系统,只有通过充分利用内存功能,我们才能获得极端性能。从数据计算的角度来看,内存的主要优点包括:它支持指针参考和高速随机访问,并且其并发阅读能力功能强大,而其缺点是:高成本和有限的容量扩展。

不幸的是,SQL计算系统缺乏一些必要的数据类型和操作,例如,它缺少数据类型的记录指针;它不支持有序操作;它定义了加入过于一般;它不能区分联接类型,因此,上述内存特征不能完全用于实现一些高性能算法。通常,基于SQL的内存数据库只是复制外部存储的数据和操作,这将引起各种问题。例如:基于记录的复制消耗过多的CPU和内存;搜索和加入表演并没有达到极端。另一方面,对于集群:内存利用率太低;大量网络传输量导致节点数量增加,但性能下降。它需要外部存储以在多机械加入的情况下缓冲数据。

SPL是一种开源数据计算引擎,扩展了数据类型和操作定义,可以充分利用内存的功能来实现各种高性能算法,并使性能极端。具体而言,指针式重复使用机制利用了内存特有的参考机制,这不仅可以节省内存空间,而且可以使速度更快。 pre-sosociation 方法还使用指针参考机制在初始化阶段实现了耗时的外键关联,并且相关结果可以直接用于随后的计算中,因此计算速度可以直接使用。得到显着改善。 序列编号定位**算法使用有序特征,并为高速随机访问内存的优势提供了完整的效果。该算法无需进行任何计算和比较,而是可以直接通过序列编号读取记录,因此其性能优于搜索算法(例如哈希索引)。 **群集维度表有效地避免或减少网络传输,并避免外部存储对数据进行缓冲。在确保高可用性的前提下,在群集的情况下,备用轮模式的容错机制可有效提高记忆利用率。

此外,SPL还提供了其他方法,例如串行字节,序列编号索引,数据类型压缩等。程序员可以根据特定方案以有针对性的方式使用这些方法。这样,可以充分发挥内存的优势,从而有效地提高内存数据计算的性能。

起源:https://blog.scudata.com/how-does-the-in-memory-database-bring-memorys-advantage-into-play/
SPL源代码:https://github.com/SPLWare/esProc