免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

GreptimeDB 数据源测试数据全解析:Parquet/ORC/CSV/JSON 测试文件的生成、结构与 Schema 推断

GreptimeDB 数据源测试数据全解析:Parquet/ORC/CSV/JSON 测试文件的生成、结构与 Schema 推断 GreptimeDB 数据源测试数据全解析Parquet/ORC/CSV/JSON 测试文件的生成、结构与 Schema 推断【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb导读本文聚焦 GreptimeDB 的common-datasource数据源组件src/common/datasource以其测试数据目录src/common/datasource/tests中的 README 说明为主干深入剖析 Parquet、ORC、CSV、JSON 四类测试数据文件的来源、内容结构、Schema 设计并结合源码讲解 GreptimeDB 如何对这些文件进行 Schema 推断与数据读取。读完本文你将掌握这些测试数据的生成方法与内部结构理解FileFormat抽象、schema_infer_max_record 等关键选项的作用并学会在本地复现 Schema 推断验证。一、测试数据目录全景GreptimeDB 的common-datasourcecrate 负责统一处理外部数据源文件的读写其测试数据按格式分目录存放src/common/datasource/tests/ ├── README.md # 测试数据说明本文主干 ├── csv/ # 6 个 CSV 测试文件 ├── json/ # 4 个 JSON 测试文件 ├── orc/ # ORC 测试文件 生成脚本 write.py README.md └── parquet/ # 1 个 Parquet 测试文件这些文件不仅是单元测试的输入也是FileFormat各实现CSV/JSON/Parquet/ORCSchema 推断逻辑的活标本。下文按照 README 的叙述顺序先看 Parquet再看 ORC最后补全 CSV/JSON 测试数据的内容细节。二、Parquet 测试数据从 CSV 转换而来2.1 文件来源根据 tests/README.md 的说明parquet/basic.parquet是由csv/basic.csv通过 bdt 工具一个 Parquet 生态的数据转换命令行工具转换生成的。原文Theparquet/basic.parquetwas converted fromcsv/basic.csvvia bdt.因此当你需要更新或重建这份 Parquet 测试数据时改动源文件 csv/basic.csv再使用 bdt 转换即可保持数据口径一致。需要说明的是从测试断言看basic.parquet实际只保留了num、str两列见下文 2.3 的测试验证转换时可能做了列裁剪。2.2 文件内部结构与数据README 展示了basic.parquet的内部数据Data------------ | num | str | ------------ | 5 | test | | 2 | hello | | 4 | foo | ------------以及其 Schema------------------------------------- | column_name | data_type | is_nullable | ------------------------------------- | num | Int64 | YES | | str | Utf8 | YES | -------------------------------------对应的源 CSV basic.csv 内容如下比 Parquet 多了ts、t、date三列时间相关字段num,str,ts,t,date 5,test,2023-04-01 00:00:00,10,2023-04-01 2,hello,2023-04-01 00:00:00,20,2023-04-01 4,foo,2023-04-01 00:00:00,30,2023-04-01可见该数据集刻意覆盖了三种基础类型整数Int64、字符串Utf8并且整列均可为空is_nullable YES用于验证可空列的推断路径。2.3 源码级验证Schema 推断测试common-datasource在 parquet.rs 中提供了与 README 完全对应的单元测试infer_schema_basic以tests/parquet为根目录构造 ObjectStore对basic.parquet调用ParquetFormat::default().infer_schema(...)断言推断结果为assert_eq!(vec![num: Int64: NULL, str: Utf8: NULL], formatted);这一断言与 README 中列出的 Schema 表完全吻合构成文档-数据-测试三方互证。Parquet 的 Schema 推断实现parquet.rs流程是通过 ObjectStore 读取文件元数据ParquetMetaData再从file_metadata.schema_descr()与key_value_metadata()调用parquet_to_arrow_schema转换为 Arrow Schema。整个推断只读取文件尾部的 footer 元数据开销极小。提示本仓库中 Parquet 文件在 file_format/tests.rs 的test_parquet_exec中还会被真实执行扫描断言输出 3 行数据5/test、2/hello、4/foo与 README 展示的 Data 表逐行一致。三、ORC 测试数据用 Python pyorc 自建ORC 测试数据的说明在同目录下的 orc/README.md 中属于同一套测试数据体系的组成部分。3.1 生成步骤ORC 文件不是手工编写的而是由生成脚本 write.py 使用 Python 的pyorc库程序化产出。README 给出了完整流程python3 -m venv venv venv/bin/pip install -U pip venv/bin/pip install -U pyorc ./venv/bin/python write.py cargo test其中write.py通过pyorc.Writer写入 5 行数据并设置compression_block_size32、compressionpyorc.CompressionKind.NONE不使用压缩、使用很小的块大小刻意让压缩跨值边界用于覆盖更复杂的读路径。生成后再以pyorc.Reader读回校验一遍确保文件本身合法可读。3.2 设计意图覆盖 ORC 各种编码与类型write.py的列名设计非常有讲究几乎每一列都对应 ORC 的一种典型编码路径double_a、aFloat64/Float32浮点类型bBoolean布尔类型str_direct、d、e、fUtf8字符串且内容有重复、递增、递减、长短混合等模式int_short_repeated、int_neg_short_repeatedInt32短重复编码int_delta、int_neg_deltaInt32增量编码正增量与负增量int_direct、int_neg_directInt32直接编码bigint_direct、bigint_neg_direct、bigint_otherInt64大整数直接编码utf8_increase、utf8_decreaseUtf8字符串长度递增/递减覆盖字典与直接编码timestamp_simpleTimestamp(Nanosecond, None)、date_simpleDate32时间与日期类型。完整 SchemaREADME 原文------------------------------------------------------------------ | column_name | data_type | is_nullable | ------------------------------------------------------------------ | double_a | Float64 | YES | | a | Float32 | YES | | b | Boolean | YES | | str_direct | Utf8 | YES | | d | Utf8 | YES | | e | Utf8 | YES | | f | Utf8 | YES | | int_short_repeated | Int32 | YES | | int_neg_short_repeated | Int32 | YES | | int_delta | Int32 | YES | | int_neg_delta | Int32 | YES | | int_direct | Int32 | YES | | int_neg_direct | Int32 | YES | | bigint_direct | Int64 | YES | | bigint_neg_direct | Int64 | YES | | bigint_other | Int64 | YES | | utf8_increase | Utf8 | YES | | utf8_decrease | Utf8 | YES | | timestamp_simple | Timestamp(Nanosecond, None) | YES | | date_simple | Date32 | YES | ------------------------------------------------------------------3.3 内部数据test.orc共 5 行中间一行第 3 行大量字段为 NULL用于验证可空列的推断与读取---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | double_a | a | b | str_direct | d | e | f | int_short_repeated | int_neg_short_repeated | int_delta | int_neg_delta | int_direct | int_neg_direct | bigint_direct | bigint_neg_direct | bigint_other | utf8_increase | utf8_decrease | timestamp_simple | date_simple | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 1.0 | 1.0 | true | a | a | ddd | aaaaa | 5 | -5 | 1 | 5 | 1 | -1 | 1 | -1 | 5 | a | eeeee | 2023-04-01T20:15:30.002 | 2023-04-01 | | 2.0 | 2.0 | false | cccccc | bb | cc | bbbbb | 5 | -5 | 2 | 4 | 6 | -6 | 6 | -6 | -5 | bb | dddd | 2021-08-22T07:26:44.525777 | 2023-03-01 | | 3.0 | | | | | | | | | | | | | | | 1 | ccc | ccc | 2023-01-01T00:00:00 | 2023-01-01 | | 4.0 | 4.0 | true | ddd | ccc | bb | ccccc | 5 | -5 | 4 | 2 | 3 | -3 | 3 | -3 | 5 | dddd | bb | 2023-02-01T00:00:00 | 2023-02-01 | | 5.0 | 5.0 | false | ee | ddd | a | ddddd | 5 | -5 | 5 | 1 | 2 | -2 | 2 | -2 | 5 | eeeee | a | 2023-03-01T00:00:00 | 2023-03-01 | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------3.4 源码级验证ORC Schema 推断orc.rs 的test_orc_infer_schema对test.orc执行OrcFormat.infer_schema断言得到与上表逐列一致的 20 个字段例如double_a: Float64: NULL、timestamp_simple: Timestamp(Nanosecond, None): NULL、date_simple: Date32: NULL。其实现通过ArrowReaderBuilder::try_new_async构建ArrowStreamReader再取其schema()完成推断orc.rs底层依赖orc-rust异步读取器并通过ReaderAdapter把 ObjectStore 的Reader适配为AsyncChunkReader。四、CSV 与 JSON 测试数据Schema 推断的边界试验场除 README 主述的 Parquet、ORC 外同目录的csv/与json/子目录还提供了 Schema 推断的边界用例它们同样是FileFormat实现的关键验证输入。4.1 基础数据文件csv/basic.csv5 列num、str、ts、t、date3 行其中ts为完整时间戳、t为秒数、date为日期字符串json/basic.json与 basic.csv 同构的 JSON Lines 数据{num:5,str:test,ts:2023-04-01T00:00:00,t:00:00:10,date:2023-04-01} {num:2,str:hello,ts:2023-04-01T00:00:00,t:00:00:20,date:2023-04-01} {num:4,str:foo,ts:2023-04-01T00:00:00,t:00:00:30,date:2023-04-01}这两个文件在 file_format/tests.rs 中分别被test_csv_openerL109-L149与test_json_openerL67-L107读取断言输出的 3 行数据完全一致说明 CSV 与 JSON 两条读路径在数据层面等价。4.2 schema_infer_max_record推断行数上限的妙用csv/max_infer.csv 前 6 行str/ts/t均为空、只有num和date有值后 3 行才填充完整num,str,ts,t,date 1,,,,2023-04-01 1,,,,2023-04-02 1,,,,2023-04-03 1,,,,2023-04-04 1,,,,2023-04-05 1,,,,2023-04-06 5,test,2023-04-01 00:00:00,10,2023-04-01 2,hello,2023-04-01 00:00:00,20,2023-04-01 4,foo,2023-04-01 00:00:00,30,2023-04-01csv.rs 的normalize_infer_schema测试将schema_infer_max_record设为 3只推断前 3 行此时空列被推断为Utf8str: Utf8: NULL、ts: Utf8: NULL、t: Utf8: NULL而date因前 3 行有值被推断为Date32。这就是 util.rs 中normalize_infer_schema的作用对推断结果为 Null 的列统一降级为Utf8避免全空列在后续流程中类型不确定。csv/schema_infer_limit.csv 则展示了行数上限对类型判定方向的改变a,b,c,d 1,2,3,4 1,2,3,4 1,2.0,3,4 1,2,4,test限制 3 行时d列全部是整数推断为Int64不限制默认 1000 行时第 4 行的test迫使d列推断为Utf8。对应测试见 csv.rs 的infer_schema_with_limitjson/schema_infer_limit.json 在 json.rs 中验证了同样的行数限制 增量字段推断行为限制 3 行时只推断出a、b、c三列d列不出现在 Schema 中。4.3 更贴近真实场景的用例csv/type_cast.csv模拟主机监控指标hostname、environment、usage_* 系列整数指标、带时区的时间戳ts用于验证带时区时间戳的解析csv/basic_format.csv时间字段使用04-01-2023这种自定义格式t列用10s配合timestamp_format/date_format/time_format选项验证自定义时间格式的推断csv/simple.csv13 列覆盖小整数、大整数含负数、Float32/Float64、长字符串等混合类型对应 csv.rs 的infer_schema_basic断言如c2: Int64: NULL、c11: Float64: NULL、c13: Utf8: NULLjson/simple.json12 行 JSON Lines覆盖 Int64、Float64、Boolean、Utf8 四类混合推断含100000000000000这样的大整数。五、文件格式的统一抽象Format 与 FileFormat上述测试数据服务于common-datasource的统一文件格式抽象file_format.rspub enum Format { Csv(CsvFormat), Json(JsonFormat), Parquet(ParquetFormat), Orc(OrcFormat), }Format::try_from(HashMapString, String)依据format选项默认PARQUET选择实现选项名大小写不敏感未知格式返回UnsupportedFormat错误。每种格式都实现FileFormattrait 的核心方法pub trait FileFormat: Send Sync std::fmt::Debug { async fn infer_schema(self, store: ObjectStore, path: str) - ResultArrowSchema; }所有实现都遵循同一模式先store.stat(path)获取文件长度再打开store.reader(path)读取文件内容最后按格式特有逻辑解析元数据得到 Arrow Schema。多个文件的 Schema 由infer_schemas通过ArrowSchema::try_merge合并file_format.rs。5.1 可配置选项一览各格式从选项映射中解析的参数常量定义于 file_format.rsCSV 解析逻辑见 csv.rs选项键适用格式含义默认值format全部文件格式csv / json / parquet / orc大小写不敏感PARQUETdelimitercsv字段分隔符单字节 ASCII,b,)has_header/headerscsv首行是否为表头两者同时给出且冲突时报错truestrict_headerscsv严格表头模式要求列数严格匹配且要求has_headertruefalseschema_infer_max_recordcsv / jsonSchema 推断读取的最大记录数1000skip_bad_recordscsv跳过解析/类型转换失败的坏记录走tolerant_csv_stream慢路径单条记录一个 batchfalsecompression_typecsv / json压缩类型gzip / bzip2 / xz / zstd不压缩timestamp_format/time_format/date_formatcsv / json时间/时刻/日期字段的自定义格式自动识别pattern全部文件匹配模式—需要特别说明的是skip_bad_records开启后走 csv.rs 的tolerant_csv_stream容错路径以一个 batch 一条记录的代价换取逐行容错可跳过的错误类型限定为 ParseError、CastError、ComputeError、InvalidArgumentErrorcsv.rs结构性错误如列数不一致仍会抛出。对应测试test_tolerant_csv_stream_continues_after_parse_errorcsv.rs验证了坏行被跳过、好行完整保留的行为。5.2 读写一体stream_to_file 与 file_to_stream除了 Schema 推断file_format.rs 的stream_to_file统一处理 RecordBatch 流写文件支持压缩与DEFAULT_WRITE_BUFFER_SIZE 8MB缓冲分块且一行未写则不落盘file_to_streamL304-L331统一处理文件读回。Parquet 写路径 parquet.rs 中stream_to_parquet默认使用 ZSTD 压缩并对时间戳列关闭字典编码、改用 DELTA_BINARY_PACKED——因为对递增时间戳列字典页反而比数据页更大。CSV/JSON 的压缩读写由 csv.rs 与 json.rs 的test_compressed_*测试覆盖逐字节校验了 gzip1f 8b、bzip2BZ、xzfd 37 7a 58 5a、zstd28 b5 2f fd四种魔数并验证压缩文件可解压读回且内容一致。六、如何在本地复现验证6.1 运行 Schema 推断相关测试以仓库根目录为工作目录执行cargo test -p common-datasource其中与本文数据文件直接相关的用例包括file_format::parquet::tests::infer_schema_basic验证basic.parquet推断为num: Int64: NULL, str: Utf8: NULLfile_format::orc::tests::test_orc_infer_schema验证test.orc推断出 20 个字段file_format::csv::tests::infer_schema_basic、normalize_infer_schema、infer_schema_with_limit验证 CSV 推断与行数上限file_format::json::tests::infer_schema_basic、infer_schema_with_limit验证 JSON 推断file_format::tests::test_csv_opener、test_json_opener、test_parquet_exec、test_orc_opener验证四种格式的真实数据读取结果。6.2 重建 ORC 测试数据按 orc/README.md 的流程在src/common/datasource/tests/orc/目录下创建虚拟环境并安装pyorc随后执行 write.py 即可重新生成test.orc最后跑cargo test回归验证。6.3 直接查看测试数据Parquet 与 ORC 是二进制格式可借助pyorc或 Arrow 生态工具查看其内容已在本 README 及本文中以 ASCII 表格完整呈现CSV 与 JSON 是纯文本可直接阅读 tests/csv 与 tests/json 目录下的文件。七、小结src/common/datasource/tests/目录虽然只是测试数据却是 GreptimeDB 多格式数据源能力的缩影Parquet 文件由 CSV 经 bdt 转换而来并仅保留核心两列ORC 文件由write.py用 pyorc 生成并精心设计以覆盖各类编码路径CSV/JSON 文件则专门构造了空值、类型漂移、行数上限等边界场景。它们在 parquet.rs、orc.rs、csv.rs、json.rs 的单元测试中承担着 Schema 推断与数据读取的双重验证职责也为我们理解 GreptimeDB 如何对接 Parquet/ORC/CSV/JSON 外部数据源提供了最直观的参考样例。扩展阅读文件格式抽象与选项解析src/common/datasource/src/file_format.rsSchema 归一化Null 类型降级为 Utf8src/common/datasource/src/util.rs各格式读写的集成测试src/common/datasource/src/file_format/tests.rs数据源组件总览src/common/datasource/src/lib.rs【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表