(2.8)ICDE 2023|Wind-Bell Index:面向图数据库的超快速边查询

ICDE 2023|Wind-Bell Index:面向图数据库的超快速边查询

为了高效存储和处理图,存图数据库得到了快速发展。然而,大多数图数据库采用的基础数据结构都是邻接表,虽然在稀疏图中可以发挥不错的效果,但存在一些关键问题:(1)大部分图都是呈幂律分布,在此分布下,邻接表的表现很差(2)无法通过顶点和终点查询到边。本次为大家带来数据库领域顶级会议ICDE2023的论文《Wind-Bell Index: Towards Ultra-Fast Edge Query for Graph Databases》。

背景

近些年,图数据库因其可以存储和处理图类型的数据得到了快速发展。考虑到实际中稀疏图的普遍性,大多数图数据库所采用的数据结构都是邻接表。然而邻接表不能通过给定的起点和终点直接查询到一条边,而是要在相同起点下的链表下逐个查询,产生很大的时间开销。而且,边的偏斜分布对查询有着至关重要的影响,且大多数图的点的度数都是呈现幂律分布,即度数大的点只占少数,大部分点的度数都是很小的。这导致采用邻接表的图数据库在查询起点为度数大的点时所耗费的时间长,度数小的点所耗费的时间短,但是度数大的点一般会被更频繁的访问,所以就导致平均查询时间较长。针对此问题,先前的研究与改进大多集中于某一特定问题上,如挖掘相似性,切分子图,找最短路等。

该论文提出了一种存图的新数据结构Wind-Bell,实现了边的快速查询以及不错的空间成本。该论文的主要贡献如下:

  1. 设计了可以加速边查询速度的数据结构Wind-Bell,相比于初始的Neo4j的JAVA API方法快了几百倍
  2. 通过数学证明了其算法的复杂度,在理论上证明了效率与可行性
  3. 将该数据结构在图数据库Neo4j上实现,并提供了边查询的新接口

数据结构

定义

可将该数据结构形象地想象为风铃,它由两部分组成,一部分是顶棚矩阵,另一部分是悬挂链表。

顶棚矩阵

顶棚矩阵是一个邻接矩阵,计为A,矩阵的每个元素是一个一个的桶(用来存边),即A[i][j],每当要向wind-bell里存边的时候,它存储位置的行由起点决定,列由终点决定。用N个哈希函数对起点和终点进行计算,可以得到将边映射到N行N列的矩阵,即顶棚矩阵上,由此就是有N2

个桶来存储这些数据。

每个桶记录两个变量,一个是计数器CNT,用来计数当前桶内有多少个元素,另一个是指针PTR,用来指向悬挂链表的第一个元素。

悬挂链表

链表中的每个元素至少记录5个变量,分别为该边的起点、终点,指向上一条以及下一条边的指针,以及一个指向具有相同起点和终点的边的指针。由定义可以看出该链表为一个双向链表,且考虑到了重边的存在。当某一起点到指定终点具有多条边时,其会在悬挂链表中的某一元素处再次形成一个链表,以此来处理重边的情况。在这5个变量基础上,每个元素结点可额外记录其它信息如边的权值、名字等。采用双向链表可以更好地为图数据库提供灵活的操作。

插入

对于每条新插入的边,在插入前通过查询函数检查是否已经存在,如果存在的话,将该边更新为已存在元素;不然,就需要向wind-bell里插入新元素。

首先用N个哈希函数计算出N2

个候选桶,将该边放入其中一个桶中,并更新被选中的桶的CNT。

可以灵活地调整N,如果强调数据存储平衡,可以使用更大的N来保证每次插入元素时可以选择插入元素最少的桶;如果希望有更快的查询速度,可以使用较小的N来减少每次查询时所需要遍历的桶的数量。


示例(见图一)

设置N=2。当要插入边<18.6>时(图中绿色部分),首先用2个哈希函数对其顶点和终点进行计算,得到4个候选桶(这个新边可以放入其中的任何一个桶中),接下来遍历这些桶,选择具有最小计数CNT的桶来放入这条新边,更新这个桶的CNT,并使其PTR更新,指向为新插入的这条边。

查询

为了查询指定起点与终点的边,首先使用N个哈希函数计算出可能存在该边的N2

个候选桶,接着遍历这些桶判断该边是否存在,如果是的话就返回该元素,不然则返回空指针。

示例(见图一)

假设要查询边<20,21>(图中橙色部分),我们首先对其起点和终点分别使用哈希函数进行计算,得到了4个候选桶,接着遍历这些桶,来检查边<20,21>是否存在其中。

优化

为了实现更好的存储平衡,论文提出了一种优化策略并提供了调整操作。

N2

个桶中CNT最大的桶满足下面两个条件时,该桶将会被在插入过程中被调整

  1. 该桶的长度超过指定阈值
  2. 该桶与这N2

    个桶中的最短桶的CNT比值超过指定阈值

在执行调整时操作时,将该桶内的最后一个元素,即最早放入的元素移出,并对其重新进行插入操作。为了更快、更便捷地执行这一操作,每个桶需要额外维护一个指针,指向桶内的最后一个元素,从而可以直接找到最早放入桶内的元素,并对其重新执行插入操作。添加队尾指针将额外增加顶棚矩阵一半的空间开销,但是顶棚矩阵的空间开销只占wind-bell整个空间开销中很小的一部分。而且,调整操作的时间复杂度是常数,不会影响整个操作的时间复杂度。换句话说,这是以一个很小的空间代价节约很多的时间开销。

示例

定义N2=4,

每个桶最长长度阈值为10,阈值比为2。假设要插入边<79,19>,首先需要依据起点和终点计算出4个候选桶,然后选择其中CNT最小的桶放入该边。操作完成后,检查最长桶是否满足调整条件,发现其满足调整的两个条件,于是将其最早放入的元素<20,19>,通过队尾指针调用其重新执行插入操作。

时间复杂度证明

假设一共有m个结点,S条边,结点的出度与入度分别标记为Xi

Yi

,结点的入度和出度独立同分布于X

。定义顶棚矩阵的大小为K*K(K<=m).假设有Gi

个起点Xi[1..Gi]

映射到i行,Hj

个终点Yj[1..Hj]

映射到j列(0<=Gi

, Hj

<=m).每次插入一个元素时,选择N行N列去找到N^2个候选桶,插入到其中CNT最小的桶里。为简便计算,在本节中,定义N=1。

显然,邻接表的单次插入开销为(下面一系列公式中,E代表取均值,D代表取方差)

想要证明对于wind-bell,满足当X分布满足limm→+∞DXm=0

EX>μ>0

.时,有下式成立

为了证明这一理论,首先给出两个前提。

第一,注意到X分布可能随着结点的数量改变,不能直接使用任何现有的SLLN(强大数定理)。然而,注意到limm→+∞DXm=0

EX>μ>0

.因此可以使用切比雪夫不等式并得到:

换句话说,mE(X)

可以近似替代边的数量S。(实际上,入度和出度必然满足相加为边的数量,可以使用独立同分布的情况来简化证明)。

第二,观察到如果有limm→+∞Km=0

,那么有

基于上述两个前提,可以很容易地证明之前提到的结论。因为Xi

Yj

独立,所以其加和同样独立。因此有

KK=m

的条件下,可以证明wind-bell的存储访问时间总是低于邻接表。当K=m

时,有

定义x=EX2/EX2,(xm)

,令F(x)

=T1

/T2

;可以得到

因此认为F(x)

从1增加到m-1

,从m-1

减小到m

。所以

意味着wind-bell相比于邻接表有更好的查询效率。

进一步地,如果X

服从幂律分布

通过简单的数学技巧,可以得到更精确的理论结果(m→∞

也可以通过下图来说明在m=106

时,wind-bell比邻接表快200倍。

实验

实验设置

初始参数设置为N=2

,顶棚矩阵大小为100*100

,数据大小在106

量级,最大数据设置为1.3*106

采用CAIDA网络数据:CAIDA数据通过五元组识别每个IP轨迹流,源与目标IP地址,源与目标端口,协议。对每个13字节的五元组,用额外8个字节来记录时间戳。实验使用IP轨迹的源点与目标点作为图的起点和终点,自行使用随机函数(80%位于(0,10000),其余分布在(0,1000000))创造时间戳。

实验使用的CAIDA数据集包含1067013条边以及59304个不同的结点

计算平台

18核CPUs (36线程, Intel(R) Core i9-10980XE CPU @ 3.00 GHz)以及128GB DRAM 存储器

度量

比较平均查询时间(AQT)、平均插入时间(AIT),存储率(LR),平均链表长度(ALL),最长链表长度(LLL),平均内存使用(AMU)

实验结果

在时间上,Wind-bell在各种情况下都实现了查询的高速,比朴素Neo4j快了几百倍甚至10000倍。原因在于wind-bell避免了在数据库中找结点以及遍历大度数结点的长链表来确定是否存在边。而且,维护wind-bell存储率的平衡是卓越表现的另一个原因。

在空间上,优化后的wind-bell实现更好的存储率,并在各个度量上优于朴素wind-bell,这证明论文的优化方法有效。

五、总结

论文研究了图的存储结构,构造了图的一种查询速度极快的数据结构Wind-Bell,为邻接矩阵以及邻接表的组合形式,在边的查询速度以及空间开销上都有卓越的表现。

本文作者

贾轲

重庆大学2022级计算机科学与技术(卓越)专业在读,重庆大学START团队成员。

主要研究方向:数据处理、数据库系统

相关推荐

  1. 2024/1/24 基本应用

    2024-02-13 12:24:01       54 阅读
  2. Day 25 数据库查询

    2024-02-13 12:24:01       32 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-13 12:24:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-13 12:24:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-02-13 12:24:01       82 阅读
  4. Python语言-面向对象

    2024-02-13 12:24:01       91 阅读

热门阅读

  1. 797. 差分

    2024-02-13 12:24:01       54 阅读
  2. react18中,useState 和 useEffect有什么区别

    2024-02-13 12:24:01       50 阅读
  3. 掘根宝典之C++友元函数与运算符重载

    2024-02-13 12:24:01       45 阅读
  4. 23种设计模式之抽象工厂模式

    2024-02-13 12:24:01       52 阅读
  5. 2月8号作业

    2024-02-13 12:24:01       47 阅读
  6. 14.4 OpenGL图元装配和光栅化:点

    2024-02-13 12:24:01       45 阅读
  7. C# 【WPF】之 INotifyPropertyChanged的简单封装

    2024-02-13 12:24:01       67 阅读
  8. Android录音功能的实现及踩坑记录

    2024-02-13 12:24:01       50 阅读
  9. [日常使用] Shell常用命令

    2024-02-13 12:24:01       46 阅读