存储引擎
TDengine 的核心竞争力在于其卓越的写入和查询性能。相较于传统的通用型数据库,TDengine 在诞生之初便专注于深入挖掘时序数据场景的独特性。它充分利用了时序数据的时间有序性、连续性和高并发特点,自主研发了一套专为时序数据定制的写入及存储算法。
这套算法针对时序数据的特性进行了精心的预处理和压缩,不仅大幅提高了数据的写入速度,还显著降低了存储空间的占用。这种优化设计确保了在面对大量实时数据持续涌入的场景时,TDengine 仍能保持超高的吞吐能力和极快的响应速度。
行列格式
行列格式是 TDengine 中用来表示数据的最重要的数据结构之一。业内已经有许多开源的标准化的行列格式库,如 Apache Arrow 等。但 TDengine 面临的场景更加聚焦,且对于性能的要求也更高。因此,设计并实现自己的行列格式库有助于 TDengine 充分利用场景特点,实现高性能、低空间占用的行列格式数据结构。行列格式的需求有以下几点。
- 支持未指定值(NONE)与空值(NULL)的区分。
- 支持 NONE、NULL 以及有值共存的不同场景。
- 对于稀疏数据和稠密数据的高效处理。
行格式
TDengine 中的行格式有两种编码格式—Tuple 编码格式和 Key-Value 编码格式。具体采用哪种编码格式是由数据的特征决定的,以求最高效地处理不同数据特征的场景。
- Tuple 编码格式
Tuple 编码格式主要用于非稀疏数据的场景,如所有列数据全部非 None 或少量 None 的场景。Tuple 编码的行直接根据表的 schema 提供的偏移量信息访问列数据,时间复杂度为O(1),访问速度快。如下图所示:
- Key-Value 编码格式
Key-Value 编码格式特别适合于稀疏数据的场景,即在表的 schema 中定义了大量列(例如数千列),但实际有值的列却非常少的情况。在这种情形下,如果采用传统的 Tuple 编码格式,会造成极大的空间浪费。相比之下,采用 Key-Value 编码格式可以显著减少行数据所占用的存储空间。如下图所示。
Key-Value 编码的行数据通过一个 offset 数组来索引各列的值,虽然这种方式的访问速度相对于直接访问列数据较慢,但它能显著减少存储空间的占用。在实际编码实现中,通过引入 flag 选项,进一步优化了空间占用。具体来说,当所有 offset 值均小于 256 时,Key-Value 编码行的 offset 数组采用 uint8_t 类型;若所有 offset 值均小于 65 536 时,则使用 uint16_t 类型;在其他情况下,则使用 uint32_t 类型。这样的设计使得空间利用率得到进一步提升。