最近接触到一些海量数据存储的需求,为了解决这样的需求,一个想法是对数据进行一定程度的聚合。在应用层的聚合方式,这里不展开。但是让我联想到的是以前学习 prometheus tsdb的时候接触到的压缩技术。即使本质上来讲,应用层的数据聚合,就是一种数据压缩技术。而 tsdb 使用的 gorilla 技术令人印象深刻。有兴趣的可以详细看一下 prometheus 作者的这篇博客, 以及其使用的技术 gorilla 的 paper. 简而言之 prometheus 的 tsdb 简洁强大,受益于其高效的压缩【gorilla 平均能压缩 16 byte samples to an average of 1.37 bytes】和查询效率,其单机的设计并没有影响他在众多场景中的广泛使用。
从这个例子可以看出,好的压缩技术对于 data intensive applications 是非常关键的【建议新版 ddia 加一章】。总结来说,对数据库数据的压缩,至少有以下的好处:
关于为什么要压缩,一篇比较老的论文 【Data Compression and Database Performance】给出了一些实际低数据,证明即使在传统的 (row based) 数据库领域,压缩往往也是很有好处的。更不用说各种列式数据库或者时序数据库了【由于其数据特点,往往更适合压缩】
由于数据库的特殊场景,和 generic 的数据压缩技术相比,应用的数据库中还需要考虑下面几点
下面会介绍先一些常用的传统技术,然后介绍在各种数据库中使用的压缩方式。
这种技术很简单,即把不同的字段值转成一组数字,用数字来存储,这样就是大幅的压缩空间。比如下面这个例子。
# 原始数据
China
America
China
China
Japan
China
# 使用字典将数据转成 ID
China -> 1
America -> 2
Japan -> 3
# 压缩后的数据
1
2
1
1
3
1
这种技术很常用,尤其是对于文本的数据压缩,还可以利用类似 Huffman encoding 的技术对出现的 value 按照出现频率进行变长编码,可以进一步的优化空间。
这种技术和下面的游程编码很类似,不同的是一般只对相同前缀进行压缩。比如对于一组文本,本身在存储的时候已经排序:user, used, useful, useless, 他们的前缀都相同,那么在存储的时候只需要存固定前缀和 r, d, ful, less。对于一些常见的场景,比如时间字符串,phone number,固定前缀是很常见的。
这种技术古老常见,看下面的例子
# 原始数据
AAAABBAACCC
# 压缩数据
A4B2A2C3
# 也可能是分开存储
数据:ABAC
开始位置: 0468
将数据分成多个 block,如果一个 block 里面的数据都一样,那么就替换成一个数字,比如下面的例子
# 原始数据
4444 4333 3333 1100
# 压缩数据
4 4333 3 1100
元数据 bit vector: 1010 其中1表示用一个数字做了整个 block 的替换
一种稀疏数据的压缩方式,思想就是先移除出现最多的数据,然后新增一个 bit vector 表示出现最多的数据的位置,比如下面的例子
# 原始数据
444332114444112
# 压缩数据
33211112
元数据:4 111000001111000 1 表示4出现在对应的位置
目前 HBase 可以支持的压缩方式有 GZ(GZIP)、LZO、LZ4 以及 Snappy。 它们之间的区别如下:
下面的表格来自阿里云:
业务类型 | 无压缩表大小 | LZO(压缩率/解压速度MB/s) | ZSTD(压缩率/解压速度MB/s) | LZ4(压缩率/解压速度MB/s) |
---|---|---|---|---|
监控类 | 419.75T | 5.82/372 | 13.09/256 | 5.19/463.8 |
日志类 | 77.26T | 4.11/333 | 6.0/287 | 4.16/496.1 |
风控类 | 147.83T | 4.29/297.7 | 5.93/270 | 4.19/441.38 |
消费记录 | 108.04T | 5.93/316.8 | 10.51/288.3 | 5.55/520.3 |
在MongoDB 中,WiredTiger为集合提供三个压缩选项:
其中使用的 通用压缩算法其实和别的数据库差不多,比如:
列/时序压缩算法:适合按列存储数据,尤其适合时序场景,我们已经看到了 gorilla 在prometheus tsdb 中的成功应用,在适合的场景下,这种算法的压缩效果可能会达到惊人的 10% 甚至 1%,这部分的压缩方式和设计思想尤其值得我们学习。
1, 2, 3, 3, 2, 4
, 会被压缩成 1(base), 1, 1, 0, -1, 2
1589636543 1589636553 1589636563 1589636573 1589636583 1589636594 1589636603
, 会被压缩成 1589636543(Base) 10(Delta) 0 0 0 1 -1 ...
类似 Delta、DoubleDelta 的算法存在的一个问题是,取数据需要一整块全部取出来,才能恢复出数据,有点类似视频压缩中,需要关键帧,仅仅用 p 帧无法恢复数据。
在 clickhouse 中往往是几种算法组合使用。效果可以参考这篇文章。
其实在上面的 clickhouse 里面已经介绍了 prometheus 使用的压缩算法,即 DoubleDelta,不过作为一个比较简洁的基于 lsm tree 的时序数据库实现,其中的比如 compaction 方式、文件存储方式、标签(label)的存储方式值得学习。相关的几个有用的参考:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。