Don’t Hold My Data Hostage – A Case For Client Protocol Redesign 论文阅读 & Apache IoTDB TsBlock 对比

这篇具有很好参考价值的文章主要介绍了Don’t Hold My Data Hostage – A Case For Client Protocol Redesign 论文阅读 & Apache IoTDB TsBlock 对比。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Don’t Hold My Data Hostage – A Case For Client Protocol Redesign 是 VLDB 2017 的一篇论文,主要着眼于数据库客户端协议的设计。本文主要是个人对论文的一些理解,以及结合自己较熟悉的开源时序数据库 Apache IoTDB 进行了一些对比分析。如果有谬误之处,欢迎留言指正~

论文阅读

Introduction

将大量级数据从数据库传输到客户端程序的需求非常常见,比如统计分析或者机器学习应用需要大量样本数据来构建或者验证模型。但是 export 本身会比较耗时,当需要通过网络传输数据库数据时会更加耗时(数据库服务端和客户端程序不在同一服务器上)。

本论文首先在多个数据库系统上测试了通过 ODBC Connector 查询 lineitem table (SF10) 全量数据时的耗时,Baseline 是用 netcat 直接发送 csv 文件的耗时。实验时客户端程序和数据库服务端在统一服务器上,不涉及网络 I/O,因此下图结果主要衡量的是序列化/反序列化过程的 CPU 消耗。本文后续有专门章节探讨网络传输的影响。

论文实验结果表明,Result set serialization (RSS) 的性能可能对数据库查询接口性能有较大的影响。(下图中绝大部分耗时都在 RSS 上,连接数据库和查询实际执行耗时甚至可以忽略不计)

本论文主要工作如下:

  • 对主要数据库系统使用的 Result set serialization(RSS)进行了基准测试,并衡量它们在不同网络环境中传输大量数据时的表现。分析不同数据库系统如何进行 RSS,并且分析了这些 RSS 方法在传输大量数据时的缺陷。
  • 分析了设计 RSS 方法时需要考虑的因素,并且调研了一些可以用于设计高效 RSS 方法的技术,且对这些技术进行了基准测试并分析了其优缺点。
  • 提出了一种适用于导出大型结果集的基于列的序列化方法。在开源数据库系统 PostgreSQL 和 MonetDB 中实现了该方法,并且证明了其性能比 SOTA 高一个数量级。

State of the Art

Overview

每个支持远程客户端的数据库系统都实现了一个客户端协议(最常见的是基于 ODBC 和 JDBC API 实现)。通过这个协议,客户端可以向数据库服务器发送查询请求,然后从数据库获取查询结果。下图描述了服务器和客户端之间的典型通信场景。通信始于身份验证,然后客户端和服务器交换元信息(例如协议版本、数据库名称)。在这个初始握手之后,客户端可以向服务器发送查询。在计算查询结果之后:

  1. 服务器将数据序列化为结果集格式。
  2. 转换后的消息通过 Socket 发送给客户端。
  3. 客户端对结果集进行反序列化,实际使用数据。

结果集格式的设计决定了上述三个步骤花费的时间。**如果结果集格式较复杂,或者使用了较复杂的压缩算法,结果集的序列化和反序列化计算成本(包含了压缩/解压缩)会较高,但一般来说可以通过传输更少的数据节省网络开销。另一方面,使用更简单的客户端协议可能会需要发送更多字节的数据,但可以节省序列化/反序列化的计算成本。**因此,需要在发送的字节数和序列化/反序列化的开销之间进行 trade off。

Network Impact

网络情况会显著影响不同客户端协议的性能。较低的带宽意味着网络传输数据的代价更高,那么需要传输的数据量更少的协议可能效果就更好。

论文为了测试不同网络状况对不同协议的影响,在限制延迟/限制带宽的情况下记录了获取100万行 lineitem 表数据的耗时。文中使用 Linux 工具 netem 模拟不同的网络情况(netem 可以模拟带宽和延迟方面的网络限制)。

Latency

下图反映了随着 Latency 变化时,不同系统获取100万行 lineitem 表数据耗时的变化。文中并没有给出此时 Throughput 是多少。

延迟越高,每次网络传输的固定耗时(无论实际传输数据量多少)就会更高。尽管可以预计较高的延迟会影响建立连接所需的时间,但大型结果集的传输不应受到延迟的影响,因为服务器可以在无需等待任何确认的情况下发送整个结果集。在记录耗时时,论文也排除了建立连接阶段的耗时,因此理论上较高的延迟应该不会对结果集传输所需的时间产生显著影响。

但是实际的实验结果与预测相反,所有系统的性能受到高延迟的严重影响。这是因为虽然服务器和客户端不会直接向对方发送确认消息,但底层的 TCP/IP **层在接收数据时会发送确认消息。**TCP 数据包会在底层缓冲区填满时发送,触发一个 ACK 消息。因此,发送更多数据的协议会触发更多的 ACK,并且在高延迟下受到更大的影响。

Throughput

下图反映了随着 Throughput 变化时,不同系统获取100万行 lineitem 表数据耗时的变化。文中并没有给出此时 Latency 是多少。

吞吐量反映了在给定时间内通过网络传输数据的能力或速率,较高的 Throughput 意味着能够更快地传输更多的数据。

在上图中我们可以看到 Throughput 对不同协议的影响。当 Throughput 下降时,发送大量数据的协议开始表现得比发送较少数据的协议更差。尽管 PostgreSQL 协议在高吞吐量下表现良好,但在较低吞吐量下,它的性能明显不如其他协议。当吞吐量降低时,压缩的效果就更明显,因为此时实际数据传输的耗时成为主要瓶颈,而数据(解)压缩的成本就没那么明显了。

Result Set Serialization

本节主要讨论不同数据库系统客户端协议使用的结果集格式。本节列出了 PostgreSQL 和 DBMSX 的客户端协议(论文中还有别的几种客户端协议,本文没有全部列出来)采用的结果集格式下,Table 2中数据的十六进制表示。实际数据使用的字节用绿色标记,其他额外开销则用白色标记,前导零用灰色标记。

PostgreSQL

PostgreSQL 的结果集中:

  • 每一行都作为单独的 prococal message 传输。每一行都包括 Message Type,Total Length,Field Count。
  • 从上图中可以看出 PostgreSQL 的结果集格式中,所有元数据都是定长的(比如 Total Length 固定是4字节),用于表示元数据的字节数可能比实际数据的字节数还多。
  • **有冗余信息。**比如理论上来说每一行的 Message Type 和 Field Count 应该是一样的,每一行都传输一遍造成了不必要的开销。

整体来说该协议非常简单,因此结果集序列化和反序列化时的计算成本很低,如果网络不是瓶颈的话,该协议的性能较好。

DBMS X

DBMS X 的协议非常简洁,结果集看上去也很紧凑。不过实际上这个格式比 PostgreSQL **的协议的计算开销大得多。**DBMS X 的结果集:

  • 每行都有一个 packet header(具体的作用不太清楚),后面是值。
  • 每列的值前面都有该值的长度作为前缀。并且这个长度是以可变长度整数的形式传输的。因此,对于较小的长度,长度字段只有一个字节。对于NULL 值,长度字段为0,不传输任何实际值。
  • DBMS X 对整数值采用了其定制的编码方式。

整体来说,可以认为该格式集在进行序列化/反序列化时的计算开销比较大,但是要传输的数据量会比较少。

Protocol Design Space

设计序列化格式时主要需要权衡序列化/反序列化计算成本和数据传输成本:

  • 如果计算成本不是瓶颈,复杂的结果集格式或者诸如 XZ 等重量级压缩方法能够大幅降低传输成本。
  • 如果数据传输成本不是瓶颈(例如在与数据库服务器相同的机器上运行客户端),那么减少计算的成本可能效果更好。

为了探讨不同设计选择对序列化格式性能的影响,论文对这些选择逐个进行了基准测试,主要记录了结果集(反)序列化和进行数据传输的耗时以及传输数据量的大小。论文在三个数据集上进行这些基准测试:

  • lineitem from the TPC-H benchmark:此表与真实世界的数据仓库事实表相似,包含16列,类型为 INTEGER、DECIMAL、DATE 和 VARCHAR。该数据集不包含缺失值。论文使用 SF10 的 lineitem 表,该表有6000万行,CSV 格式下大小为7.2GB。
  • **American Community Survey (ACS) :**该数据集包含数百万条人口普查数据,由274列组成,其中大部分为 INTEGER 类型。数据集16.1%的字段包含缺失值,共910万行,CSV 格式下大小为7.0GB。
  • **Airline On-Time Statistics:**该数据集描述了商业航空交通的准点情况。有109列,最多的类型是 DECIMAL 和 VARCHAR,55.2%的字段包含缺失值。该数据集有1000万行,CSV 格式下大小为3.6GB。

Row/Column-wise

大量数据库系统都采用按行序列化结果集的方法(不管其内部是行存还是列存),这可能主要是因为流行的 ODBC 和 JDBC 都主要提供按行访问的 API。

列式结果集理论上可以有更好的压缩率,并且对于本来就需要按列访问数据的应用程序来说,可以避免行->列的转换开销。使用纯列格式结果集的问题在于在发送下一列之前需要先传输当前的整个列,如果客户端想以逐行方式访问数据,则首先必须读取并缓存整个结果集。对于大型结果集,这种方式可能不可行。

论文中采用的结构是一种行列混合的向量结构,即一个 chunk 包含若干行的数据,而 chunk 内部按列编码。这样如果用户要按行访问数据,只需要缓存一个 chunk 的数据即可,并且内部按列编码的 chunk 也可以利用到列存的压缩和性能优化。

Chunk Size

chunk size 的选择对性能的影响较大:

  • 较大的 chunk size 意味着用户需要分配更大的 buffer 内存
  • 太小的 chunk size 可能导致一个 chunk 内行数太少,按列编码的优势就没了。

为了选择较优的 chunk size,论文在三个数据集上均做了实验。论文将 chunk 用 Snappy 压缩,然后记录了随 chunk size 变化,传输完整个数据集的耗时变化,以及实际传输数据量的变化(计算压缩率)。下图是实验结果:

chunk size 范围:2KB(保证 chunk 至少有一行数据)—100MB

结果分析:

  • chunk size <= 10KB 时,三个数据集上效果都较差,这可能是因为此时一个 chunk 行数太少,退化成了行式结构
  • 三个结果集在 chunk size 为10MB 时都有较好的表现,说明使用向量结构时,用户并不需要太大的 buffer 内存就可以获得较优的性能。
  • chunk size 继续增大(100 MB)时,性能出现了下降,论文中没有给出原因。

Data Compression

前提:网络传输成本和压缩/解压缩的计算成本需要进行 trade off,一般来说在网络受限的情况下才需要考虑压缩数据。

不同压缩算法的侧重点不同,一般来说压缩率越高,压缩/解压缩的计算成本越高。轻量级的压缩工具 Snappy 和 LZ4 注重快速压缩,但牺牲了压缩比。XZ 压缩速度很慢,但实现了非常紧凑的压缩。GZIP 在两者之间取得了平衡,实现了较好的压缩比,同时速度不会太慢。

压缩算法 压缩比 压缩速度
Snappy
LZ4
GZIP 较高 较快
XZ

在网络状况较好,传输数据成本较低的情况下,就没有必要过于关注压缩率,轻量级的压缩方法可能效果更好;相应的,网络状况较差,传输数据成本较高的情况下,压缩率更好的压缩方法可以传输更少的数据,也许效果更好。

论文做了一个实验,验证不同压缩算法在不同 throughput 限制下的表现。

上表是通过网络传输 SF10 lineitem,不同压缩算法在不同网络带宽下的耗时(论文中没有明确给出此时用的是什么系统,什么客户端协议,猜测应该是论文自己实现的协议)。

结果分析:

  • 客户端和数据库服务端在同一节点时,不压缩的耗时最少(T local)
  • 千兆网下,轻量级的压缩算法 Snappy 和 LZ4 效果会比不压缩好,但重量级压缩算法表现仍较差。
  • 网速极慢(10M)时,GZIP 的效果才会好于轻量级压缩算法,但是重量级压缩算法仍然效果不好,因为其花费了太多时间在压缩/解压缩上。

压缩算法对传输耗时的影响与网络情况强相关,而让用户根据网络来手动配置会比较麻烦,论文中在其自己的实现中采取方法为:

  • 如果客户端和数据库服务端在同一节点,则不适用压缩算法
  • 如果客户端和数据库服务端在同一节点,使用轻量级压缩算法,因为用户局域网网络情况一般不会太差。

Column-Specific Compression

除了通用的压缩方法之外(压缩整条 message),还可以对单独的列进行压缩。例如,可以对数值列使用游程编码或差分编码。数据库中的一些统计信息也对列压缩有帮助。例如,通过最小/最大值索引可以为特定的列选择位压缩长度,而无需扫描该列。

用这些特定的列压缩算法对列进行压缩,可能可以用更短的压缩时间同时获得更高的压缩率,尤其是整数列,用一些向量化的压缩算法(binpacking 和 PFOR)可以有很好的压缩效果。

论文中测试了针对整数列的压缩算法的效果。只查询三个数据集中的整数列,然后测试不同压缩算法下的传输耗时和压缩效果,下表是实验结果:

三个数据集的结果分析:

  • Lineitem:PFOR 和 binpacking 算法都比 Snappy 在更低的计算成本下实现了更高的压缩比,且在所有网络情况下都比 Snappy 表现更好。将 PFOR 和 binpacking 与 Snappy 结合(先按列压缩,然后压缩整条 message),可以实现更高的压缩比。然而,客户端和服务端在同一主机时,仍然是不压缩表现最好。
  • ACS:ACS 数据集包含了大量的整数列。在传输该数据集时,Snappy 效果明显更好。这是因为该数据集整数列数过多,导致在每个传输的 chunk 的行数较少,导致针对每一列进行的压缩算法在小数据块上被频繁调用,从而导致性能下降。Snappy 压缩方法不会针对单个列进行操作,而是压缩整个消息,因此不会受到这个影响,即使在压缩大量整数列时也可以表现良好。
  • Ontime:PFOR 和 binpacking 在这个数据集上表现都很差,但是 Spappy 的效果较好。

总体来看,按列进行压缩在某些数据集上有不错的表现,但是在全体数据集上可能表现不佳,因此论文的实现方法决定不使用按列压缩。

Data Serialization

实验论证了论文自定义的序列化方法比用 ProtoBuf 好,因为 ProtoBuf 的压缩效果较自定义方法差,但是没有具体分析。

String Handling

常见的字符串编码方法有:

  • Null-Termination:用一个 zero byte 标示字符串结尾
    • 优点:只需要为每个字符串额外多记录一个 byte,但该字节始终是相同的值,易于压缩,开销较小。
    • 缺点:必须扫描当前整个字符串才能读取下一个字符串。
  • Length-Prefixing:在字符串头部先记录其长度
    • 优点:可以提前读出字符串长度,然后跳过不需要读的字符串
    • 缺点:需要额外位数来记录长度,可能会让消息长度变大(尤其是有大量短字符串的时候)。如果用 varint 来记录字符串长度,又会增加计算开销。
  • Fixed-Width:固定长度(预留最大长度)
    • 优点:如果每个字符串具有相同的大小,则不会有不必要的填充开销。
    • 缺点:如果字符串长度不一,在存在少量长字符串和大量短字符串(或 NULL 字符串)时,可能会引入大量不必要的填充开销。

论文为了探究三种字符串编码方法的性能,记录了使用三种字符串编码方式下,传输 lineitem 表部分字符串列的耗时:

  • 表8是传输 l_returnflag(1个字符) 列的耗时结果。可以看到,Fixed-Width 在传输短字符串列时表现非常好。Null-Termination 和 Lengh-Prefixing 由于每个字符串都多传输了一个字节,所以传输的字节数增加了一倍。
  • 表9是传输 l_comment(不定长,最长44个字符) 列的耗时结果。可以看到,传输该列时,所有方法的性能都相当。但是,Fixed-Width 传输的字节数明显更高。这是因为许多字符串不完全是44个字符长,因此必须进行填充。
  • 论文还做了后续实验,发现 Fixed-Width 传输的字节数随字符串最大长度的上升而明显急剧上升,而其较好的压缩效果并不能弥补多传输数据的损失。

实验结论:

  • Null-Termination 有更好的压缩率,大部分情况下的最优选项。
  • 字符串长度都为1时(VARCHAR(1)) ,Fixed-Width 效果最佳。

Implementation and Results

MonetDB Implementation

上图是用论文提出的 MonetDB 客户端协议表示表2数据的结果。论文提出的协议将 chunk 按列序列化。

  • 每个 chunk 的开头是该 chunk 包含数据的行数,然后按照结果集 header 中列的顺序依次序列化每一列。(理论上结果集 header 肯定也记录了每一列的数据类型)
  • MonetDB 内部没有使用 Null Mask,直接用特殊值来表示 Null Value,因此论文针对 MonetDB 的协议也没有记录 Null Mask,直接沿用特殊值的做法。
  • 定长数据类型的列之前可以不存列长度(就是 rowCount * 数据类型长度),变长数据类型的列之前记录了该列的长度,这样客户端就可以通过这个长度跳过不想读取的列。

在客户端进行 authentication 的时候可以设置 chunk 的最大大小(bytes),这样 chunk 的大小和其包含的数据行数就无关了,可以更灵活地决定 buffer 的大小。大部分情况下都可以算出一行数据大致有多少字节,这样就可以计算出一次发送的 chunk 中包含多少行数据。MonetDB 是列存格式,因此每个列的数据都可以直接按顺序复制到缓冲区中。如果为特定列启用了列特定的压缩,则数据将直接压缩到缓冲区中。如果启用了 chunk 级别的压缩,则在传输之前将对整个 chunk 进行压缩。

PostgreSQL Implementation

上图是用论文提出的 PostgreSQL 客户端协议表示表2数据的结果。论文提出的协议同样将 chunk 按列序列化。

  • 每个 chunk 的开头是该 chunk 包含数据的行数,然后按照结果集 header 中列的顺序依次序列化每一列。
  • 每一列数据的开头是一个 Null Mask(Null Mask 的长度应该与 row count 有关),Null Mask 记录了对应行的数据是否为 null。猜测 PostgreSQL 的协议采用 Null Mask 应该是因为 PostgreSQL 内部不像 MonetDB 一样使用特殊值表示 Null。
  • 由于启用了 Null Mask,所以哪怕定长数据类型的列的长度现在也是不确定的了(不知道有多少数据是 null)。因此每一列前都记录了该列的长度,客户端可以通过这个长度跳过不想读取的列。
  • 由于按列存 Null Mask,对于没有任何缺失值的列,可以不用存 Null Mask。比如标记了 NOT NULL 标志的列或数据库统计信息表明列不包含缺失值时,就可以不用存 Null Mask。哪些列没有缺失值可以在结果集 header 里面记录,这样可以减少记录 Null Mask 的开销。

由于 PostgreSQL 是行存格式,将其转换为列式结果集格式有一些潜在的问题:

  • 由于有 Null Mask,即使某列数据是固定长度类型,也无法提前知道该列的长度。为避免在存在许多缺失值时浪费大量空间,论文的做法是首先在迭代行时将每个列的数据复制到临时缓冲区中。等缓冲区填满再将每个列的数据复制到流缓冲区中然后传输给客户端。
  • 将行式数据转换成列式格式的时候可能会随机访问(读第一行的第一列,第二行的第一列…如果 cache 不够大的话,可能还会导致 cache 的抖动)。

Evaluation

为了验证文中提出的协议的效果,论文在三个结果集上进行了实验,记录了客户端从服务端拉取完整结果集数据需要的时间(实验设置超时时间是 1h):

  • 实验环境:Linux VM running Ubuntu 16.04; 16G main memory; 8 cores;
  • 网络环境:使用 netem 分别模拟局域网和广域网网速情况。
    • 局域网:1000 Mb/s throughput and 0.3ms latency
    • 广域网:100 Mb/s throughput and 25ms latency,1.0% uniform random packet loss
  • baseline:使用 netcat 传输 CSV 格式的结果集的耗时,论文还记录了使用 GZIP 和 Snappy 压缩结果集后传输的耗时

表10是数据集 SF10 lineitem 上的结果(+C 表示启用 chunk 压缩):

  • 论文提出的两种协议在 Local 和 Lan 场景下都明显优于其他协议(快一个数量级)。
  • Local 场景下不启用 chunk 压缩耗时更短,因为此时传输数据成本很低;Lan 场景下启用 chunk 压缩耗时更少,此时压缩和解压缩的计算成本小于网络传输的成本。
  • 不启用 chunk 压缩的情况下,论文提出的两种协议需要传输的数据量是最小的,因为它们按列序列化结果集,不额外记录任何 per-row header;启用 chunk 压缩的情况下,论文提出的两种协议需要传输的数据量仍然是最小的,这是因为在压缩前这两种协议需要传输的数据量就小,且按列压缩可能效果优于按行压缩。

表11是数据集 ACS 上的结果(+C 表示启用压缩):

  • 论文提出的两种协议在 Local 和 Lan 场景下仍然都明显优于其他协议(快一个数量级)。
  • 仍然是 Local 场景下不启用 chunk 耗时更短,Lan 场景下启用 chunk 压缩耗时更少。
  • MySQL 的协议传输数据量最少。这是因为 ACS 数据集主要是整数列,一个整数列占4字节,但整数的实际值通常小于 100。MySQL 使用 text 编码,在表示小整数的时候效果更好,因为小于 10 的数字只需要两个字节来编码(一个字节用于长度字段,一个字节用于文本字符)。
  • 尽管 MySQL 的协议在启用 chunk 压缩的时候压缩效果很明显,但是总体耗时在网络变差的情况下没有明显下降。这是因为 MySQL 采用的是 GZIP,压缩效果较好但是压缩计算成本太高,占到了总体耗时的大部分,数据传输耗时的下降反而影响不大了。

表12是数据集 Ontime 上的结果(+C 表示启用压缩):

  • 论文提出的两种协议在 Local 和 Lan 场景下仍然都明显优于其他协议(快一个数量级)。
  • 仍然是 Local 场景下不启用 chunk 耗时更短,Lan 场景下启用 chunk 压缩耗时更少。
  • PostgreSQL++ 在该数据集上的表现比在其他两个数据集上要好一些,这应该是因为 Ontime 数据集相比其它两个数据集要多很多缺失值,PostgreSQL++ 使用了 Null Mask,可以少传输这些缺失值。

**总体来看,MonetDB++ 的效果在三个结果集上都是最优的。**虽然 PostgreSQL++ 的结果集格式和 MonetDB++ 的很相似,但因为 MonetDB 本身就是列存格式,因此每列的数据都可以直接按顺序复制到缓冲区中,不用像 PostgreSQL++ 一样按行读取数据再组装成列。这样能减少不少计算开销,因此 MonetDB++ 效果最好。

Future Work

  1. 论文当前的协议实现使用简单的规则判断来确定使用哪种压缩方法来压缩 chunk(服务端和客户端在同一主机则不压缩,反之采用 Snappy)。实际上可以通过网络情况来动态判断使用哪种压缩方法,这样可以对压缩成本进行调整并动态适应不断变化的网络条件。
  2. 可以尝试根据列中的数据分布使用不同的列压缩方法(比如可以通过数据库已有的统计信息来判断数据分布,以此选择列压缩方法)。也可以先压缩一列作为样本,试验哪种方法有最高的压缩比,然后选择压缩算法。
  3. 并行序列化结果集。在可并行查询的场景下,线程可以在计算结果时立即开始将结果集序列化到线程本地缓冲区中,而无需等待整个查询完成。

Apache IoTDB TsBlock 对比

如前文所述,Don’t Hold My Data Hostage – A Case For Client Protocol Redesign 中阐述了设计数据库客户端协议应该注意的点,并且在 PostgreSQL 和 MonetDB 中实现了基于 chunk 的新的结果集格式的协议。

Apache IoTDB 的客户端协议在序列化/反序列化结果集时使用的 TsBlock 结构,和这篇论文中的 chunk 结构非常相似,只是在对 chunk 的部分元数据处理上有一些不同。

论文提出的 chunk 结构:

IoTDB 中 TsBlock 在序列化时的格式:

格式说明:

  • 在进行序列化时,TsBlock 的 metadata 和除字符串以外的数据类型都是定长的。
  • val col cnt 指 TsBlock 的列的数量,val col types 是各列的数据类型,pos cnt 是数据的行数,encodings 是各列的编码形式,time col 和 val col 都可以认为是值列。
  • TsBlock 中像 PostgreSQL 一样用了 Null Mask 来标识缺失值。在序列化某一个值列前,如果该列没有缺失值,会序列化一个标记位表示该列没有缺失值;否则会先序列化一个 bitmap 来记录该列那些行是缺失的,bitmap 只需要占用 [rowCount / 8 + 1] 字节即可。因此 val col 应该理解成 bitmap + value column。

与论文中结构的对比:

  • 与论文中实现不同的是,TsBlock 中的 val col cnt、val col type、encodings 等 metadata 在每个 chunk 中都序列化了一遍,而不是通过 result set header 传递,这其实是不必要的开销。但是由于 TsBlock 不止作为客户端协议传输数据的载体,也是查询引擎内部传输数据的载体,目前 val col cnt、val col type、encodings 仍然需要在 TsBlock 中被序列化。
  • TsBlock 序列化时,没有在每一列前序列化该列的长度,所以客户端不能跳过不想读取的列,只能把整个 TsBlock 反序列化出来再读取想要的数据。

TsBlock 默认的最大行数是 1000,对列数没有限制,按照论文中的实验结果,一个 chunk 的大小在 1MB-10MB 间时启用压缩效果最好,那一个 TsBlock 大约需要有 150+ 列才能大于 1MB。

在 IoTDB 目前遇到的场景中,尚未发现论文中提到的 RSS + Transfer 是瓶颈的情况,所以 IoTDB 在传输 TsBlock 时,并没有将 TsBlock 压缩后再传输。

为了验证压缩 TsBlock 是否会有效果,下面做一个小实验。

压缩 TsBlock 实验

配置

三台服务器机器配置:

  • CPU:Intel I7-11700 8C16T
  • 内存:32 GB DDR 2400 MHZ + 8GB Swap 内存
  • 硬盘:3 块希捷 ST16000NM000J 机械硬盘,单盘 14 T,7200 RPM
  • 操作系统:Ubuntu 22.04.2 LTS

IoTDB 版本:

  • rel 1.2.0

数据集

使用 IoTDB Benchmark 写入数据:

  • 设置一个 DataBase,10个 device,每个 device 下 48个 sensors,可以认为一共有 480 列
  • INT32 : INT64 : FLOAT : DOUBLE : TEXT : BOOLEAN = 1 : 1 : 1 : 1 : 1 : 1
  • 共 500w 行数据,无缺失值

实验步骤

通过 IoTDB 的 Session 客户端发送查询,查询全量数据,查询语句为:

select * from root.**
  1. 部署 IoTDB 3C3D 集群
  2. 使用 Benchmark 写入数据
  3. 停 IoTDB 集群,清理系统缓存
  4. 启动 IoTDB,运行客户端程序执行一次全量查询进行 warm up
  5. 再次运行客户端程序,记录客户端 fetch 全量数据的耗时

共需进行8次实验,分别为:

  • 不进行压缩,客户端程序与服务端在同一节点
    • MaxTsBlockSize = 128KB
  • 不进行压缩,客户端程序与服务端不在同一节点(客户端获取数据需要进行网络传输)
    • MaxTsBlockSize = 128KB
  • 进行压缩,客户端程序与服务端在同一节点
    • MaxTsBlockSize = 128KB
    • MaxTsBlockSize = 1MB
    • MaxTsBlockSize = 10MB
  • 进行压缩,客户端程序与服务端不在同一节点(客户端获取数据需要进行网络传输)
    • MaxTsBlockSize = 128KB
    • MaxTsBlockSize = 1MB
    • MaxTsBlockSize = 10MB

实验结果

客户端和服务端在同一节点拉取数据耗时(s) 客户端和服务端在不同节点拉取数据耗时(s) 客户端接收的 ByteBuffer 总大小(MB)
不压缩,MaxTsBlockSize = 128KB 148 233 11669
使用 Snappy 压缩 TsBlock 再传输,MaxTsBlockSize = 128KB 136 141 1455
使用 Snappy 压缩 TsBlock 再传输,MaxTsBlockSize = 1MB 143 148 1674
使用 Snappy 压缩 TsBlock 再传输,MaxTsBlockSize = 10MB 137 149 1661

现象:

  • 当客户端和服务端在不同节点时,使用 Snappy 压缩 TsBlock 序列化的 ByteBuffer 后再传输耗时明显降低。可以看到客户端接收的 ByteBuffer 总大小在开启压缩后下降非常明显。
  • 开启压缩时,客户端和服务端在同一节点时的耗时会低于不在同一节点的耗时。
  • 调大 MaxTsBlockSize 对结果影响不大。

分析:文章来源地址https://www.toymoban.com/news/detail-659604.html

  • 千兆网局域网下使用 Snappy 压缩 TsBlock 序列化后的 ByteBuffer 再传输耗时更少,这与论文的预期符合,因为减少了网络开销。
  • 开启压缩时客户端和服务端在同一节点时的耗时会略低于但不明显低于不在同一节点时的耗时,因为即使客户端和服务端在同一节点也需要经过 RPC 传输数据,在同一节点应该只是相当于网速更快。
  • 调大 MaxTsBlockSize 对结果影响不大应该是由于算子有时间片限制,TsBlock 又有480列,很有可能在时间片内,能够生产的数据行数较少,因此每个 TsBlock 的行数较少,退化成了行式结构,故尽管调大了 MaxTsBlockSize 和 MaxTsBlockLineNumber 也不能使 TsBlock 变大。

到了这里,关于Don’t Hold My Data Hostage – A Case For Client Protocol Redesign 论文阅读 & Apache IoTDB TsBlock 对比的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包