数据压缩
对于大型数据库,使用数据压缩技术来节省内存和磁盘空间是非常重要的。正如以下部分所述,SmartEDB为内存中数据库和持久数据库提供了这一功能。
此外,在SmartEDB Active Replication Fabric网络中,服务器和设备之间的通信数据也可以进行压缩。在低带宽网络环境中,压缩网络流量尤为重要。为了全面解决SmartEDB所有网络组件的这个问题,压缩功能在SAL层作为mco_socket_t抽象的包装器实现。这使得我们可以对各种支持的套接字类型(如普通TCP、SSL、本地域、UDP等)应用压缩。实际的压缩工作是由第三方流压缩库Zlib完成的。
请注意,数据库压缩与加密不兼容;您需要选择使用其中一种功能,但不能同时启用两者。
内存数据库压缩
在某些情况下,启用内存压缩可以带来显著的好处:
- 数据非常稀疏:例如,存在大量固定大小的零填充字符串、大部分为空的数组,以及包含许多零值的标量字段等。
- 主内存不足:如果不进行压缩,数据库将无法完全装入主内存中。
- 无需持久化:当不需要或无法进行持久化存储(如没有磁盘)时,内存压缩是一个很好的选择。
当启用内存压缩时,所有数据都会以压缩格式存储在内存中,并在应用程序访问时自动解压缩。这有助于节省宝贵的内存资源,同时不影响数据的完整性和性能。
请注意,数据库参数compression_mask
和expected_compression_ratio
允许源代码许可方调整内存页的数据压缩过程。不过,还需要一些额外的配置设置。如果您希望启用内存页面压缩,请联系McObject技术支持,获取详细的实现指南。
另外需要注意的是,内存压缩不适用于混合数据库(即同时包含瞬态和持久类的数据库)。这是因为内存压缩需要使用特定的内存页面管理器,而混合数据库则依赖于磁盘页面管理器。这两个页面管理器分别实现在两个独立的库中,且一个应用程序只能链接其中一个页面管理器库。
持久数据库压缩
持久数据库压缩功能仅适用于类Unix系统(如Linux、MacOS、Solaris等)。它使用LZ压缩算法,该算法通过构建一个将整数代码映射到唯一字符序列的字典来实现高效压缩。(有关该算法的详细描述,请参阅以下链接:https://en.wikipedia.org/wiki/Lempel-Ziv-Welch)。
请注意,数据库压缩与加密不兼容;您需要选择使用其中一种功能,但不能同时启用两者。
另外需要注意的是,内存中压缩不适用于混合数据库(即同时包含瞬态和持久类的数据库),因为这需要用磁盘页面管理器替换内存页面管理器。这两个页面管理器分别实现在两个独立的库中,且一个应用程序只能链接其中一个页面管理器库。
此外,CRC检查可能无法与压缩同时启用,因为压缩本身已经实现了自己的CRC校验机制。
如果您有任何疑问或需要进一步的帮助,请随时联系McObject技术支持团队。
RLE压缩序列
对于序列数据,可以使用“运行长度编码”(RLE)压缩来优化存储空间。有关实现细节,请参阅RLE压缩页面。
RLE压缩
序列数据的另一种优化方式是使用RLE(运行长度编码)压缩。通过RLE,我们不是存储“原始”序列,而是用一个值和重复计数来表示重复值。对于包含大量重复元素的序列,RLE不仅能提高性能,还能显著减少存储需求。例如,如果x = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},并且y = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2},那么计算x + y只需要进行一次加法操作,而不是像存储原始序列时那样需要进行10次加法。
#### RLE的优势
RLE的一个显著优势是它能更好地利用运行时堆栈。在非压缩实现中,每个tile中的元素数量是预定义的,并且对所有数据类型都相同,例如100个整数或100个双精度浮点数。这样做是为了避免在计算二进制表达式(如运算符)时进行额外验证和按需加载。启用压缩后,这些验证仍然必须执行,因此可以加载更多元素,比如200个整数或双精度浮点数。
#### 使用RLE的挑战
然而,使用RLE也有一些缺点。其实现逻辑较为复杂,这可能会对计算表达式的性能产生负面影响。此外,为了保存计数器,每个元素还需要额外的空间(通常是每个元素一个字节)。因此,如果序列主要由唯一元素组成,那么非压缩实现的性能会更好。
#### RLE的应用场景
对于处理包含大量重复元素的序列数据的应用程序,特别是证券数据,RLE将非常有益。几乎所有垂直数据存储系统(如Vertica、SciDB、Vectorwise等)都使用压缩技术,因为它们在垂直数据布局上的压缩效果远优于水平数据布局。根据我们的经验,在处理各种基准测试时,日内价格通常不会频繁变化,因此很可能存在大量重复数据。
#### 实现细节
RLE的实现位于与非压缩实现不同的库(target/mcoseqrle)中。因此,应用程序几乎完全独立于具体的序列存储实现。这里说“几乎”是因为尽管API保持一致,但RLE函数调用返回的tile包含额外的计数器。如果应用程序直接使用这些tile,则需要考虑到这一点。具体来说,在C API中,非压缩tile的处理代码如下所示:
while ( close_iterator.next(&close_iterator) == MCO_S_OK )
{
mco_size_t i, tile_size = close_iterator.tile_size;
for (i = 0; i < tile_size; i++)
{
close_sum += close_iterator.tile.arr_float[i];
}
}
当使用行程长度编码(RLE)时,相同的代码看起来会是这样:
while (close_iterator.next(&close_iterator) == MCO_S_OK)
{
mco_size_t i, tile_size = close_iterator.tile_size;
for (i = 0; i < tile_size; i++)
{
close_sum += close_iterator.tile.arr_float[i] * MCO_SEQ_RLE_COUNT(iterators.close.tile, i);
}
}
请注意,在实际应用中,上述两个代码片段很少会被使用,因为 mco_seq_add() 函数能更方便地计算总和,而且许多其他聚合函数可以直接对序列数据进行操作(示例请参见统计函数库),应用程序几乎不会(如果有的话)直接使用分块。
物联网通信的数据压缩
在SmartEDB Active Replication Fabric网络中,网络内的数据传输可以通过压缩来节省带宽。只有当服务器和设备双方都设置了适当的压缩级别时,它们之间的通信才会被压缩。所有通信组件必须使用相同的压缩级别值。不过,物联网服务器可以配置两个连接(两个监听端口),一个启用压缩,另一个不启用压缩。设备需要根据其自身的压缩设置选择连接到相应的端口。
要评估实际的压缩效率,您可以检查压缩比参数。这些参数显示了传入和传出通信中传输的数据的原始大小与压缩大小之间的比率。计算公式为:ratio = (original_size - compressed_size) * 100 / original_size。
例如,如果数据从100Kb压缩到30Kb,压缩率将为70%。这意味着压缩后的数据量减少了70%,显著提高了传输效率。0%表示最坏的情况或没有压缩,即原始大小等于压缩大小;而100%则表示理想压缩,即压缩后的大小几乎可以忽略不计。
本地语言API
数据压缩的API是特定于所使用的编程语言的。请通过以下链接查看针对您的开发环境的详细解释和示例。
开发语言 | 说明 |
---|---|
C | C 语言中的数据压缩 |
C++ | C++ 语言中的数据压缩 |
Java | Java 语言中的数据压缩 |
C# | C# 语言中的数据压缩 |