本文档介绍了 OceanBase 数据库中半结构化存储的最佳实践,重点阐述如何通过半结构化编码存储技术降低 JSON 数据的存储成本。通过合理的配置和使用方法,用户可以在保证数据完整性的同时,显著减少存储空间占用。
目前 JSON 等半结构化的数据,存在下面两个主要问题:
-
存储效率低下
-
以二进制串形式存储,无法利用整数压缩等编码算法降低存储成本;
-
冗余大量元数据信息,如每条 JSON 数据中重复存储 Key 字符串。
-
查询性能受限
-
查询特定字段时必须读取完整数据,无法实现部分读取;
-
查询性能受限于数据的完整读取开销。
为解决这些问题,OceanBase 引入了半结构化编码存储技术。该技术通过将 JSON 数据结构化存储,不仅显著提升存储效率,还提供了高效的字段查询能力。
基本原理
OceanBase 的半结构化编码存储技术通过将 JSON 数据拆分为多个子列进行存储,每个子列单独编码,从而提高编码压缩率,降低整体存储空间需求。
示例说明如下:
原始JSON数据:
{"id": 11111001, "name": "white", "score": 85.2}{"id": 11111002, "name": "brown", "score": 93.5}{"id": 11111003, "name": "mike", "score": 67.8}{"id": 11111004, "name": "trump", "score": 72.0}
-
拆分后存储形式:
| id | name | score || -------- | ----- | ----- || 11111001 | white | 85.2 || 11111002 | brown | 93.5 || 11111003 | mike | 67.8 || 11111004 | trump | 72.0 |
通过这种方式,系统可以对每列数据应用特定类型的高效编码方案,显著提高压缩率。
使用方式
半结构化编码存储使用方式及条件查询性能优化说明请参见使用半结构化编码。
适用场景
推荐场景
-
存储空间敏感型应用:适用于存储空间受限或存储成本敏感的场景。
-
频繁基于 JSON 字段进行条件过滤的场景:如配置管理系统、产品目录系统、用户画像系统和事件追踪系统等。通过半结构化编码,可以显著提升查询性能。
-
数据写入后较少完整读取 JSON 内容的场景:适用于主要进行数据写入和部分字段查询的场景,如日志采集系统、监控数据存储和事件追踪系统等。通过半结构化编码,可以优化部分字段查询。
例如:
{"c1": "v1", "c2": "v2"}{"c1": "v1", "c3": ["v121", "v22"]}{"c1": "v3", "c2": "v5"}{"c1": "v4", "c4": ["v121", "v22"]}
在处理此类数据时,由于字段 c1 出现频率很高,系统可将其识别为高频字段,并自动提取为单独列(column),采用高效的编码方式进行存储。这种方式有助于显著降低磁盘占用,并提升查询性能。
OBKV-HBase 时序场景:针对时序场景,OceanBase 基于 JSON 半结构化存储提供了优化的 OBKV-HBase 时序数据模式,有效解决原生模型中 K、T 字段重复存储的问题,优化写入性能和磁盘占用,详见下文。
不推荐场景
-
频繁完整读取 JSON 数据的场景:由于被拆分为多个子列存储,当读取 JSON 时,又需要把这些子列合并为原始的 JSON 输出,相比于不编码的 JSON,读取性能还是会不小的影响。所以如果会频繁读取 JSON 数据,则不建议开启。
-
JSON 数据完全异构的场景:因为 JSON 数据通常具有异构性,缺乏统一的结构,这种非结构化的数据在编码后难以实现有效的压缩,因此不建议开启。
触发限制
由于存在一些不适合的场景,半结构化编码并非在所有场景都会触发,以下情况不会触发:
拆分后存储空间大于拆分前:如果没有压缩收益,则没必要编码。
无法提取公共 Schema:JSON 数据大多数异构性,会导致编码效果差,所以不触发。
存在行外(outrow)存储的数据:如果使用本特性,需保证 95% 左右的数据都为行内存储。
无法提取公共 schema 的示例如下:
# 字段完全不同{"c1": "v1", "c2": "v2"}{"x1": "v1", "x2": "v2"}
性能对比
TPC-H 测试数据
我们对半结构化编码存储的性能进行了全面测试,包括存储空间和条件查询性能两个维度。
测试使用了标准的 TPC-H 数据集,并与多种主流数据库系统进行了对比,以验证半结构化编码的实际效果。
关键发现如下:
半结构化存储比原始 JSON 存储减少 42% 空间。
条件过滤查询性能提升显著(百倍级别)。
导入和合并性能略有下降(约 12-15%)。
OBKV-HBase 时序模型
注意:此功能从 V4.3.5 BP2 开始支持
模型优化
针对时序场景,OceanBase 基于 JSON 半结构化存储提供了优化的 OBKV-HBase 时序数据模式,有效解决原生模型中 K、T 字段重复存储的问题。
数据转换示例如下:
原始数据:
{"K": "name1", "T": 1732206353081, "cf1:QualifierA": "v1"}{"K": "name1", "T": 1732206353081, "cf1:QualifierB": "v2"}{"K": "name2", "T": 1732206353082, "cf1:QualifierC": "v3"}{"K": "name2", "T": 1732206353082, "cf1:QualifierD": "v4"}
优化后 OBKV-HBase 存储格式:
| K | Q | T | V || ------- | ---------- | ------------- | ---- || name1 | QualifierA | 1732206353081 | v1 || name1 | QualifierB | 1732206353081 | v2 || name2 | QualifierC | 1732206353082 | v3 || name2 | QualifierD | 1732206353082 | v4 |
在时序场景下,原始数据中 K(行键)和 T(时间戳)字段会大量重复,特别是在一次 put 操作写入多个单元格时,K 和 T 字段完全相同,导致严重的磁盘空间浪费。
同时,如果每个 V 值中包含大量重复的 Qualifier(列限定符),也会进一步增加存储开销。
通过半结构化编码,我们可以:
提取公共的 Schema;
优化 Qualifier 的存储;
减少重复数据的存储。
基于这些优化,我们调整了 OBKV-HBase 的存储格式。以示例数据为例,重新生成后的数据结构如下:
-- S 为该 cell 插入的当前服务端时间| K | T | S | V || ------- | ------------- | ------------- | ---------------------------------- || name1 | 1732206353081 | 1732206353081 | {"QualifierA":v1, "QualifierB":v2} || name2 | 1732206353082 | 1732206353082 | {"QualifierC":v3, "QualifierD":v4} |
性能对比
我们对 OBKV-HBase 时序模型存储效率进行了测试:
半结构化编码存储技术具有广泛的适用性和扩展性,未来 OceanBase 可能会扩展到 GIS/Array/Vector 等其他多模类型使用。
这些扩展将基于现有的半结构化编码技术,进一步提升 OceanBase 多模数据处理方面的能力,为用户提供更全面的数据存储和查询解决方案。
附录
存储空间查询方法
这里介绍了通用的存储空间查询方法。
系统租户查询
-- 获取 tablet_idSELECT tablet_id FROM oceanbase.CDB_OB_TABLE_LOCATIONSWHERE tenant_id=xxxx AND table_name='xxxxx';-- 查询存储空间SELECT size/1024.0/1024.0 FROM oceanbase.GV$OB_SSTABLESWHERE tenant_id=xxxx AND tablet_id=xxxxx;
普通租户查询
-- 获取 tablet_idSELECT tablet_id FROM oceanbase.DBA_OB_TABLE_LOCATIONSWHERE table_name='xxxxx';-- 查询存储空间SELECT size/1024.0/1024.0 FROM oceanbase.GV$OB_SSTABLESWHERE tablet_id=xxxxx;
术语解释
-
半结构化编码:将 JSON 数据拆分为子列单独编码的存储技术。
-
白盒过滤:直接基于编码数据进行的条件过滤操作。
-
outrow 存储:大字段数据单独存储的机制。
-
inrow 存储:数据与主记录一起存储的机制。

