阿明是城东一家面包店的账房先生。店里的每个面包出炉、卖出,都得记一条账:几点几分出炉、什么面包、卖多少钱。一天下来好几千条,一年下来账本摞得比人还高。纸张越来越贵,仓库越来越满,东家急得直跺脚:“阿明,能不能让每页纸多装点?” 阿明翻着账本发现了秘密:记录几乎每隔固定时间才来一条,价格也总在差不多的地方浮动——今天是5.23元,昨天是5.24元。“既然变化这么小,何必每个字都写全?”他琢磨出一套“省字记账法”,让一本账本顶过去十本。 我们的电脑监控系统就像这家面包店:服务器每秒都在产生 CPU、内存、网络指标,一年存下几千亿条记录。如果每条都完整写下,磁盘再多也不够。幸运的是,时序数据有极强的规律——采样间隔稳定,数值变化微小。于是诞生了 Gorilla 这类压缩算法,用极小空间装下海量数据。今天,我们就翻开阿明的账本,看看他是怎么省字的。
掌握时序压缩原理,是搭建高性价比监控与 IoT 数据平台的必备技能,直接影响存储成本与查询性能。
目录
可视化演示
动态演示时序数据库压缩算法的核心机制,通过入门/进阶/实战/优化四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
时序数据库压缩算法:无损压缩 无损压缩的基本概念与信息论基础 霍夫曼编码与自适应编码 核心机制可视化 时序数据库压缩算法整体架构与关键组件
一、数据压缩背景——账本太厚了
阿明把旧账本摊在桌上,一页一页翻给东家看:“你看,每一条都要写'2024年3月10日下午3点04分32秒,法式长棍,5.23元'。一天两千条,光写'下午3点04分32秒'这几个字就够累的。”东家问:“那能怎么办?”阿明说:“我发现记的时间几乎都是整点间隔,价格也总在5.2元左右打转。只要把'不变的废话'省掉,账本就能薄一大截。”
这正是时序数据的写照。所谓时序数据,就是按时间顺序不断产生的记录——服务器的CPU使用率、机房的温度、股票的报价。它们数量极大,一台设备一年就能产生三千多万条样本;但它们又极有规律:采样间隔基本固定,数值变化幅度很小。“固定”和“微小”就是压缩算法眼中的金矿——规律越强,冗余越多,可压缩的空间就越大。
为了衡量省字效果,我们需要一个指标:压缩率。它等于原始体积除以压缩后体积,数值越大说明压得越狠。原始情况下,每条样本如果各用8字节存时间戳、8字节存数值,总共16字节;而经过压缩,平均可能不到2字节。
阿明把第一个月省下来的纸摞在旧账本旁边,东家眼睛亮了:“省下的纸钱,够再开一家分店了。”压缩率,就是阿明向老板证明自己价值的那个数字。
- 原始体积 \(S_{\text{原始}}\)未压缩时所有数据点的字节总数。假设有 \(N\) 个样本,每个样本时间戳占 8 字节、数值占 8 字节:
- 压缩后体积 \(S_{\text{压缩后}}\)用特殊编码(如 Gorilla)压缩后,所有样本占用的字节总数,包含少量块头元数据:
- 压缩率 \(\text{CR}\)原始体积与压缩后体积之比,是衡量算法好坏的核心指标:
二、Gorilla架构总览——省字账本的格式
阿明设计的不是零散的省字小技巧,而是一套完整的账本格式。他把账本分成一页一页的“块”,每页只管两个小时内的记录;每一页又分成左右两栏——左栏专门记时间,右栏专门记数值。“时间和价格是两码事,分开记,各有各的省字法。”
这套思路正是 Gorilla 压缩算法的核心架构。Gorilla是Facebook在2015年提出的时序压缩算法,后来被Prometheus等主流监控系统采用。它把一条样本拆成两个独立通道:时间戳走“Delta-of-Delta“通道,数值走”XOR”通道,两条通道各自压缩、各自解码,互不干扰。
分块是一个精妙的设计。一个块通常包含约两小时的数据,块头部记录起始时间、样本数量等元数据。查询时,系统先看块头判断“这块是不是我想要的时段”,不是就整块跳过。这样既能按时间范围快速定位,又避免了把整年数据压成一个大文件——万一中间坏了,至多丢一个块,不会殃及全部。
阿明的新账本,每一页都是独立的“小账房”:两栏各管各的、按页检索。东家想查某天下午的账,阿明直接翻到那一页,不用从年头翻到年尾。这就是分块的智慧。
- 块总大小一个块内所有编码后的数据(块头+时间戳通道+数值通道)的总字节数,压缩后通常只有几KB:
- 块内样本数 \(N\)这个块里一共装了多少条记录,由时间跨度除以采样间隔决定:
- 每点平均存储 \(b\)块总字节数除以样本数,得到每个样本平均摊到的字节数:
三、Delta-of-Delta——时间戳的省字大法
阿明发现,出炉记录的时间几乎总是“每隔60秒一条”。“反正都是整点,我何必写'下午3点04分32秒'?写上一条是3点03分32秒,这一条就写'和上一条隔60秒'。”更妙的是,下一页的新记录又和上一条隔60秒——连“隔60秒”三个字他都懒得写,只画一个勾表示“跟上一条一模一样”。
这就是Delta-of-Delta(差值再求差值,简称DOD)的思想。第一步先算一阶差分:相邻两个时间戳的间隔,比如60秒;第二步再算二阶差分:相邻两个“间隔”的间隔。如果采样一直稳定在60秒,那么每个间隔都是60,间隔之差就是0。编码时,0只需要花1个bit记录,而原始时间戳是64个bit——省下了63个bit。
当然,真实场景不会永远完美稳定。Gorilla为此设计了控制位:当差值确实是0时,用一个控制位标记;当差值很小(比如只偏了几秒)时,用控制位加几bit存下这个小偏差;只有当偏差很大时才花大代价完整写入。解码器在读数据时,先看控制位,就知道该继续读几位。
阿明的画勾妙招,放到监控系统里就是DOD编码。服务器每15秒采集一次CPU,间隔稳定得像钟表,DOD几乎全零,时间戳的存储成本趋近于零。
- 时间戳序列 \(t_i\)每条记录到达的时刻,是DOD的原始输入。原始时间戳是64位整数,直接存储代价高:
- 一阶差分 \(\delta_i\)相邻两个时间戳之间的间隔,比如固定的60秒。它把“绝对时刻”转化为“相对间隔”,数值通常很小:
- 二阶差分 \(\Delta_i\)相邻两个间隔的差。采样稳定时 \(\Delta_i = 0\),只需1bit即可编码:
- 控制位决策解码器根据控制位判断后续读法——\(\Delta=0\) 读1bit,小 \(\Delta\) 读若干bit,大 \(\Delta\) 读完整64bit:
四、XOR浮点压缩——数值的省字大法
时间省下来了,数值怎么办?阿明看着面包价格:今天5.23元,明天5.24元。“这两个数在纸面上只差一分钱,但在电脑里,它们是64个0和1排成的长串。我要找的是:这64位里,到底哪几位变了?”
浮点数在计算机里用64位二进制表示,稍有变化时,大多数位是相同的。Gorilla用“异或”运算(XOR,逐位比较、相同为0、不同为1)把新旧两个数值变成一串差异位。如果值完全没变,异或结果是全零,只存一个0bit!如果变了,异或结果里通常只有中间一小段是1,前后都是0。于是算法只记录“从第几位开始变”“变了多少位”和“中间那段有效位”,把前后的0统统跳过——这叫前导零和尾零跳变。
XOR压缩最擅长处理“数值微变”的场景:CPU从50.1%变成50.2%,温度从25.3变成25.4,股价从100.01变成100.02。这些看似不同的数,二进制层面高度相似,XOR一算,真正变化的bit少得可怜。
阿明管这叫“只记改了哪一笔,不重抄整本账”。价格从5.23变到5.24,账本上只需补一笔“第几位变了”,连价格都不用重新写。
- 浮点值的二进制表示 \(B(v_i)\)每个浮点数在内存中是64位(8字节)的0/1串,这是XOR运算的原材料:
- 异或运算 \(\oplus\)逐位比较两个二进制串,相同位记0,不同位记1,找出“到底哪里变了”:
- 全零判断若 \(C_i = 0\),说明两个值完全相同,只需记录1个bit的标记:
- 非零时的压缩若 \(C_i \neq 0\),头部大多是0、尾部大多是0,只保留中间连续变化的“有效位段”:
五、字典与行程编码——专治高频词和连笔
有些时候,变化的不只是数字,还有文字。阿明发现店里卖的“法式长棍面包”出现频率极高,每次写全称手指都酸。他灵机一动:在账本扉页建一张对照表——“法式长棍面包”写作“棍”,“全麦乡村面包”写作“麦”。后面记正文时写一个字就够,这叫字典编码。
还有另一种情况:面包价格连着十天没变,阿明干脆写“连续10日价同”,而不是每天重复记一遍5.23元。把连续相同的值合并成“值+重复次数”,这叫行程编码(Run-Length Encoding,简称RLE)。
这两种方法虽然不是Gorilla的独创,但在时序压缩中非常有用。监控系统的数据常带标签,比如主机名“web-server-01-prod“、区域”us-east-1”,这些字符串反复出现,用字典编码映射成短ID,能省下一大截。而像传感器状态(在线/离线)、开关量这类取值不多、连续不变的字段,RLE几乎是量身定做。
阿明在账本扉页建字典、在正文里画“连续十日”的横线,不到一个月,账本又薄了三分之一。东家笑得合不拢嘴:“这些省字法,单看都是小聪明,合在一起就是大智慧。”
- 待压缩序列假设一串连续相同的值 \(v\) 重复了 \(k\) 次,原始存储要写 \(k\) 个值:
- RLE 编码只存一个值和一个重复计数,总共两个部分:
- 字典编码映射把高频的字符串 \(s\) 映射到短整数 \(id\),后续只写 \(id\):
六、压缩选型对比——不同账本配不同策略
东家高兴之余问了个关键问题:“这套省字法,是不是所有分店都能直接用?”阿明摆摆手:“隔壁的蛋糕店是手工烘焙,出炉时间一点都不规律,价格也天天变。硬套我的方法,反而帮倒忙。”他开始帮每家分店量身定做记账策略——这就是压缩选型。
工程师面对时序压缩时,也该问“数据三问”。第一问:采样间隔稳定吗?稳定,DOD立功;不稳定,DOD的差值经常很大,压缩率大打折扣。第二问:数值变化幅度大吗?微变,XOR如鱼得水;剧烈跳变,XOR的前导零很少,省不了多少。第三问:字符串基数高吗?基数低(值域小、重复多),字典和RLE都是利器;基数高(几乎每个值都独一无二),字典反而会越压越大。
除了压缩率,还要权衡CPU开销。Gorilla的压缩和解压需要逐位运算,压缩率高但消耗CPU;追求极低延迟的系统可能选择更简单的编码。存储和计算永远是一场交易——省了空间,就要多花时间算;想要快,就得让数据多占点地方。
阿明给每家分店画了一张“记账策略表”:规律出炉的用时间压缩,价格稳定的用数值压缩,名字老长的用字典——没有最好的账本,只有最合适的账本。
- 存储成本项压缩后的数据占用的磁盘空间,由压缩率决定,压缩率越高此项越低:
- 计算成本项写入时压缩、查询时解码消耗的CPU时间,压缩算法越复杂此项越高:
- 总成本最小化选型的本质是让两项之和最小,而非只看其中一项:
七、工程实践落地——从账房走向全城分店
阿明的省字账本在总店试验了一个月,效果惊人:同样厚的一沓纸,原来记一周,现在能记三个月。东家一声令下,全城分店统一换新账本。监控系统界也发生了同样的故事——Prometheus从2.0版本开始,在TSDB中采用了Gorilla风格的压缩,成为云原生监控的事实标准。
实际落地时,写数据只是故事的一半,读数据同样重要。查询时,系统先扫描块头,快速跳过不相关的时间范围,定位到目标块;然后只解压需要的块,而不是把整年的数据全部还原。这套“先定位、再解压”的路径,让海量数据的查询依然快如闪电。
工程实践还告诉我们,压缩效果可以“源头优化”。如果采样时间能规整到固定间隔(比如统一整点或每秒),DOD的差值会大量归零;如果采集端能减少数值抖动,XOR的压缩率也会明显提升。很多团队发现,把采集器调到“规整采样+合理精度”,比换任何压缩算法都管用。
阿明的账本从总店走向全城,省下的纸张堆满了一间库房。他把口诀传给每个分店的账房:“先看规律,再选方法,最后别忘了怎么把账读回来。”时序压缩的实践,正是这套口诀的工程版。
- 块总bit数块大小 \(S_{\text{块}}\) 以字节为单位,乘以8得到总bit数:
- 每点平均bit数除以块内样本数 \(N\),得到每个样本实际消费了多少bit:
- 经验参考Gorilla在Facebook的真实监控数据上,每样本平均约1.37字节,远低于原始的16字节:
AI 应用场景
云原生监控平台(Prometheus)
阶段:数据存储与压缩
解决的问题:传统方式下,监控系统把每条指标(时间戳+数值)完整落盘,百万级目标、每秒上亿条时间序列会让磁盘在数日内耗尽。Prometheus采用Gorilla压缩后,同一份数据占用空间可降至原来的十分之一甚至更低,同时保留原始精度的无损查询能力。
具体案例:某电商平台的Kubernetes集群有2万个Pod,每15秒上报一次容器CPU、内存指标。启用Gorilla压缩后,原先每天消耗500GB磁盘的监控数据,压缩到约60GB,历史数据留存从7天延长到60天,为容量规划提供了长期依据。
IoT设备数据回传
阶段:边缘数据预处理
解决的问题:海量物联网设备通过窄带网络回传传感器数据,带宽和电量都是稀缺资源。传统方式直接上报完整JSON,流量大、费电。借鉴Gorilla思想,在边缘网关做DOD和XOR压缩后再回传,可减少80%以上传输量。
具体案例:某智慧农业项目在田间部署了5000个温湿度传感器,每30秒上报一次数据。网关先将时间戳做DOD编码(采样间隔固定,几乎全零)、温湿度做XOR编码(数值微变),再批量上报。每月节省流量费超过60%,电池续航延长近一倍。
金融行情数据归档
阶段:历史数据存储与回放
解决的问题:股票、期货的报价数据量极大且要求无损,传统方案按原始格式存储,十年行情数据需要数PB空间。由于报价变化多为微小波动(如100.01→100.02),XOR压缩能取得极高压缩率,同时解码速度满足历史回放需求。
具体案例:某量化基金需要保存全市场逐笔行情做策略回测。采用类Gorilla压缩后,单日行情从12GB压缩至1.5GB,十年数据归档占用从43TB减至5.5TB,且回测时按块解压,查询单日行情只需几十毫秒。
Python 演示
"""Gorilla时序压缩算法简化实现:DOD时间戳编码 + XOR浮点编码""" import struct def dod_encode(timestamps): """把时间戳序列编码为Delta-of-Delta差值序列""" encoded = [] prev_delta = 0 for i, ts in enumerate(timestamps): if i == 0: encoded.append(ts) # 第一个时间戳全量存 elif i == 1: delta = ts - timestamps[i - 1] # 第二个存一阶差分 encoded.append(delta) prev_delta = delta else: delta = ts - timestamps[i - 1] dod = delta - prev_delta # 之后存二阶差分 encoded.append(dod) prev_delta = delta return encoded def dod_decode(encoded): """从Delta-of-Delta差值序列还原时间戳""" timestamps = [encoded[0]] if len(encoded) == 1: return timestamps curr = encoded[0] + encoded[1] timestamps.append(curr) prev_delta = encoded[1] for dod in encoded[2:]: delta = dod + prev_delta curr += delta timestamps.append(curr) prev_delta = delta return timestamps def xor_encode(values): """把浮点序列编码为XOR差值字节串(控制位简化为0x00/0x01/0xFF)""" out = bytearray() prev_bits = None for v in values: bits = struct.unpack('>Q', struct.pack('>d', v))[0] if prev_bits is None: out.append(0xFF) # 首值全量标记 out.extend(struct.pack('>Q', bits)) # 存8字节原始值 else: xor = bits ^ prev_bits if xor == 0: out.append(0x00) # 值没变,只需1字节标记 else: out.append(0x01) # 有变化标记 out.extend(struct.pack('>Q', xor)) # 简化:直接存8字节XOR值 prev_bits = bits return bytes(out) def xor_decode(encoded): """从XOR差值字节串还原浮点序列""" values = [] i = 0 prev_bits = 0 while i < len(encoded): tag = encoded[i] if tag == 0xFF: prev_bits = struct.unpack('>Q', encoded[i + 1:i + 9])[0] i += 9 elif tag == 0x00: i += 1 # 值不变,直接复用prev_bits else: xor = struct.unpack('>Q', encoded[i + 1:i + 9])[0] prev_bits = prev_bits ^ xor i += 9 values.append(struct.unpack('>d', struct.pack('>Q', prev_bits))[0]) return values def main(): timestamps = [1000, 1060, 1120, 1180, 1240, 1300, 1360, 1420] values = [25.3, 25.3, 25.3, 25.4, 25.4, 25.5, 25.5, 25.6] ts_codes = dod_encode(timestamps) val_codes = xor_encode(values) raw_bytes = 8 * len(timestamps) + 8 * len(values) comp_bytes = len(ts_codes) * 8 + len(val_codes) print("原始时间戳:", timestamps) print("DOD编码后:", ts_codes) print("原始值:", values) print("XOR编码字节:", val_codes.hex()) print() print("原始占用: %d 字节" % raw_bytes) print("压缩占用: %d 字节" % comp_bytes) print("压缩率: %.2fx" % (raw_bytes / comp_bytes)) print() print("解码校验: 时间戳一致=%s, 值一致=%s" % ( dod_decode(ts_codes) == timestamps, xor_decode(val_codes) == values )) if __name__ == '__main__': main()
delta = ts - timestamps[i - 1]计算一阶差分:相邻时间戳的间隔,如“每隔60秒一条”。dod = delta - prev_delta二阶差分:间隔的间隔,稳定采样时恒为0。bits = struct.unpack('>Q', struct.pack('>d', v))[0]把浮点数拆成64位二进制整数,才能做XOR位运算。xor = bits ^ prev_bits异或找变化位:两个数哪几位不同,结果里就是1。if xor == 0:值为0表示和上一个完全相同,只需一个标记字节。out.append(0x01)` + `struct.pack('>Q', xor)值有变化时,先写变化标记,再写XOR差值。raw_bytes / comp_bytes计算压缩率,展示这套编码省下了多少空间。dod_decode(ts_codes) == timestamps解码回放校验,确认无损压缩不丢任何信息。
常见误区
误区我听说Gorilla能把数据压到十分之一,那一定是把一些数据点丢掉了,或者把精度降低了,监控数据不就失真了吗?
正解Gorilla是无损压缩,解压后能逐位还原原始时间戳和浮点值。它丢掉的不是数据,而是冗余——就像阿明把“下午3点04分32秒”简写成“跟上一条一样”,还原时照样能算出完整时刻。真正丢数据的是“有损采样”(比如每10条只留1条),那是另一个层面的事。
为何常见:大家熟悉图片压缩里的“有损JPEG”,容易把“压缩”默认理解为“牺牲质量”。但无损压缩在数据库领域是基本功,压缩率和无损并不矛盾。
误区Delta-of-Delta?我明白了,就是把相邻时间戳相减,存下间隔。比如每60秒一条,就存一个60。
正解存间隔只是一阶差分,DOD要在间隔的基础上再减一次——存的是“间隔的间隔”。固定间隔时,这个值恒为0,于是每个时间戳只需1bit。如果只存间隔,每个时间戳仍然要几十bit,压缩效果天差地别。
为何常见:从“时间戳”到“差分”这一步已经显得很聪明,容易让人以为公式到此为止,忽略了“再差分一次才是精妙所在”。
误区XOR不是加密算法里常用的运算吗?Gorilla拿它对数据加密,那压缩后的数据还怎么读?安全性会不会有问题?
正解这里的XOR不是加密,而是“找出两个浮点值的二进制差异”。加密的目的是让别人看不懂,压缩的目的是让存储变小。Gorilla用XOR算出新旧值的变化位,再只保存变化的那一小段;解码时拿变化位和上一位做XOR,就能还原出新值,整个过程无损且不涉及任何安全性。
为何常见:程序员都知道XOR是位运算,也常在加密算法里见到它,于是把“用途”和“加密”绑定。实际上XOR既能加解密,也能做差异检测,要看上下文。
误区时序压缩就用Gorilla,它是Facebook出品、Prometheus都在用,肯定万能。我直接套上准没错。
正解Gorilla的强项是“稳定间隔+微变浮点”,比如CPU、温度、内存指标。如果数据是随机大跳变(网络抖动延迟)、间隔不规律(事件触发型日志)、或是高基数字符串(每次请求的URL都不同),Gorilla的DOD和XOR都省不了多少。此时RLE、字典编码甚至通用压缩可能更合适。选型要看数据特征,不是看谁名声大。
为何常见:技术社区对主流算法的宣传容易让人产生“默认最优”的错觉,忽略了任何算法都有其适用边界。
面试题
Q1. Gorilla压缩算法最适合以下哪类数据特征?
- A.随机波动的日志文本
- B.周期性时间戳与微变浮点← 正确答案
- C.高度加密的二进制流
- D.大型JSON嵌套结构
答案:B。Gorilla的两大核心——DOD和XOR——分别针对“时间戳稳定”和“浮点微变”设计。周期性的时间戳让DOD大量归零,微变的浮点让XOR只有少数bit变化,这正是账房先生最擅长的“规律性账本”。随机日志、加密流和JSON都不具备这种规律。
Q2. "Delta-of-Delta"编码所压缩的数据是?
- A.相邻时间戳求差值再求差值← 正确答案
- B.对时间戳做哈希散列处理
- C.直接压缩时间戳的文本表示
- D.按分钟取整然后丢弃秒数
答案:A。Delta-of-Delta字面意思就是“差分的差分”。先算相邻时间戳的间隔(一阶差分),再算间隔与间隔的差(二阶差分)。固定采样间隔时,二阶差分恒为0,只需1bit即可存储,这是时间戳压缩的核心。
Q3. 当相邻两个浮点值做XOR运算的结果为0时,Gorilla会怎么做?
- A.存储完整八字节的异或结果
- B.仅记录一个0位表示数值不变← 正确答案
- C.立即丢弃该采样数据点
- D.改用字典编码重新存储
答案:B。XOR为0说明两个浮点的64位二进制完全相同,即数值没有变化。这时不需要存任何XOR结果,只需一个bit(或简化实现中的一字节标记)告知解码器“沿用上一个值”。丢弃数据点会造成时间戳与数值错位,无损压缩绝不允许。
Q4. Gorilla压缩算法的组成部分包括以下哪些?
- A.基于二阶差分的时间戳编码← 正确答案
- B.基于异或运算的浮点编码← 正确答案
- C.基于哈夫曼树的熵编码
- D.按块独立存储的压缩结构← 正确答案
答案:ABD。Gorilla由三大件构成:DOD编码时间戳(A)、XOR编码浮点值(B)、分块存储以便按时间定位(D)。哈夫曼树是通用熵编码,Gorilla并未使用。分块结构让每个块独立压缩和解码,查询时能跳过无关块。
Q5. 大量监控标签值(如主机名"web-server-01-prod")反复出现,哪种压缩方法最合适?
- A.二阶差分的时间戳编码
- B.字典编码映射为短索引← 正确答案
- C.异或运算的浮点编码
- D.行程编码压缩连续重复
答案:B。标签是字符串,且基数有限、重复度高,字典编码先把每个字符串映射成短整数,正文只存短整数,压缩效果显著。DOD面向时间戳、XOR面向浮点、RLE面向连续相同值,都不适合处理这种“整体重复但未必连续”的字符串。
Q6. Gorilla为什么将数据划分为多个块(block)独立压缩?
- A.便于按时间范围快速定位查询← 正确答案
- B.为了压缩率能更高一些
- C.为了兼容老版本的数据库
- D.为了减少CPU占用率
答案:A。每个块有独立的头部(起始时间、样本数),查询时先读块头判断时间范围,命中才解压该块,从而快速跳过无关数据。分块不能直接提高压缩率,也不影响CPU占用,更与版本兼容无关。
Q7. 以下哪些因素会显著影响时序压缩算法的压缩率?
- A.采样间隔是否长期稳定← 正确答案
- B.浮点数值变化的幅度大小← 正确答案
- C.数据库的版本号高低
- D.相同数值连续出现的长度← 正确答案
答案:ABD。采样稳定→DOD归零多;数值微变→XOR有效位段短;连续相同值→RLE效率高。这三个维度是数据特征的核心。数据库版本号是软件属性,与数据本身的熵无关,不影响压缩率。
Q8. XOR压缩中记录“前导零数”和“尾零数”的作用是?
- A.跳过不变的bit只存变化段← 正确答案
- B.对浮点值进行加密混淆
- C.计算平均值做聚合统计
- D.验证数据的完整性校验
答案:A。相邻浮点值差异小,异或结果的高位和低位大多是0,只有中间一小段是1。记录前导零和尾零的数量,就是为了告诉解码器“哪些位不用存”,只保存中间的有效位段,从而大幅缩减存储。
Q9. 当XOR的控制位显示“前导零数量与上一个值不同”时,编码器需要额外写入哪些信息?
- A.前导零数与有效位段长度← 正确答案
- B.原始浮点数的完整二进制
- C.上一个样本的完整存储副本
- D.当前时间戳的完整数值
答案:A。Gorilla的一个优化是:如果前导零和尾零数和上次一样,就用更短的控制位;如果不同,则需多写几个bit来记录新的前导零数、尾零数和有效位段长度。写原始浮点或上一个样本的副本就失去了压缩意义。
Q10. 关于时序数据压缩的选型,下列说法正确的有?
- A.稳定采样间隔优先用DOD压缩← 正确答案
- B.微变浮点数值优先用XOR压缩← 正确答案
- C.高基数字符串适合用字典编码
- D.连续不变的状态值适合用RLE← 正确答案
答案:ABD。DOD吃定稳定间隔,XOR吃定微变数值,RLE吃定连续重复,这三组搭配是经典组合。字典编码只在低基数(值域小、重复多)时高效,高基数字符串几乎每个都不同,建字典反而增加额外开销,所以C错误。