Apache Fury行式存储格式深度解析
incubator-fury 项目地址: https://gitcode.com/gh_mirrors/in/incubator-fury
什么是行式存储格式
行式存储格式是一种将数据按行进行序列化和存储的二进制格式。与传统的序列化方式相比,行式存储格式具有更高的序列化/反序列化效率,支持零拷贝读取,并且能够实现跨语言数据交换。
核心特性
- 高性能序列化:相比传统Java序列化,性能提升5-50倍
- 零拷贝访问:可以直接访问二进制数据而不需要完全反序列化
- 跨语言支持:Java、Python、C++等多语言互通
- Arrow兼容:可与Apache Arrow生态系统无缝集成
- 部分反序列化:可选择性地只反序列化需要的字段
Java实现详解
基本使用
首先定义数据模型类:
public class Bar {
String f1;
List<Long> f2;
}
public class Foo {
int f1;
List<Integer> f2;
Map<String, Integer> f3;
List<Bar> f4;
}
创建编码器和序列化数据:
RowEncoder<Foo> encoder = Encoders.bean(Foo.class);
Foo foo = new Foo();
// 填充数据...
BinaryRow binaryRow = encoder.toRow(foo);
零拷贝读取
// 零拷贝读取List<Integer> f2
BinaryArray binaryArray2 = binaryRow.getArray(1);
// 零拷贝读取List<Bar> f4中的第11个元素
BinaryRow barStruct = binaryArray4.getStruct(10);
// 零拷贝读取嵌套结构中的数据
barStruct.getArray(1).getLong(5); // 读取第11个Bar元素的f2列表中的第6个long值
部分反序列化
RowEncoder<Bar> barEncoder = Encoders.bean(Bar.class);
// 只反序列化单个Bar对象
Bar newBar = barEncoder.fromRow(barStruct);
Python实现详解
基本使用
定义数据模型:
@dataclass
class Bar:
f1: str
f2: List[pa.int64]
@dataclass
class Foo:
f1: pa.int32
f2: List[pa.int32]
f3: Dict[str, pa.int32]
f4: List[Bar]
序列化和反序列化:
encoder = pyfury.encoder(Foo)
foo = Foo(f1=10, f2=list(range(1000_000)),
f3={f"k{i}": i for i in range(1000_000)},
f4=[Bar(f1=f"s{i}", f2=list(range(10))) for i in range(1000_000)])
# 序列化为二进制
binary: bytes = encoder.to_row(foo).to_bytes()
# 反序列化
foo_row = pyfury.RowData(encoder.schema, binary)
性能对比
与Python标准库pickle的对比测试:
# Fury格式处理
print(f"start: {datetime.datetime.now()}")
foo_row = pyfury.RowData(encoder.schema, binary)
print(foo_row.f2[100000], foo_row.f4[100000].f1, foo_row.f4[200000].f2[5])
print(f"end: {datetime.datetime.now()}")
# pickle处理
binary = pickle.dumps(foo)
print(f"pickle start: {datetime.datetime.now()}")
new_foo = pickle.loads(binary)
print(new_foo.f2[100000], new_foo.f4[100000].f1, new_foo.f4[200000].f2[5])
print(f"pickle end: {datetime.datetime.now()}")
测试结果显示Fury格式在大型数据集处理上性能显著优于pickle。
Apache Arrow集成
Fury格式与Apache Arrow深度集成,可以高效地在两者之间转换数据。
Java实现
Schema schema = TypeInference.inferSchema(BeanA.class);
ArrowWriter arrowWriter = ArrowUtils.createArrowWriter(schema);
Encoder<BeanA> encoder = Encoders.rowEncoder(BeanA.class);
for (int i = 0; i < 10; i++) {
BeanA beanA = BeanA.createBeanA(2);
arrowWriter.write(encoder.toRow(beanA));
}
return arrowWriter.finishAsRecordBatch();
Python实现
import pyfury
encoder = pyfury.encoder(Foo)
# 转换为Arrow RecordBatch
encoder.to_arrow_record_batch([foo] * 10000)
# 转换为Arrow Table
encoder.to_arrow_table([foo] * 10000)
C++实现
std::shared_ptr<ArrowWriter> arrow_writer;
EXPECT_TRUE(
ArrowWriter::Make(schema, ::arrow::default_memory_pool(), &arrow_writer)
.ok());
for (auto &row : rows) {
EXPECT_TRUE(arrow_writer->Write(row).ok());
}
std::shared_ptr<::arrow::RecordBatch> record_batch;
EXPECT_TRUE(arrow_writer->Finish(&record_batch).ok());
最佳实践
- 大数据集处理:对于包含数百万元素的大型集合,Fury行式格式性能优势明显
- 跨语言数据交换:在Java和Python服务间交换数据时,使用Fury格式可避免序列化开销
- 数据分析场景:与Arrow集成后,可直接用于数据分析流程
- 选择性读取:对于大型嵌套结构,只反序列化需要的部分数据
性能优化建议
- 对于频繁访问的字段,使用零拷贝方式直接读取
- 对于大型集合,考虑分批处理
- 在跨语言场景中,预先定义好统一的数据模型
- 利用Arrow集成实现与现有数据分析生态系统的无缝对接
Fury行式存储格式为高性能数据序列化和跨语言数据交换提供了优秀的解决方案,特别适合大数据量、低延迟要求的应用场景。
incubator-fury 项目地址: https://gitcode.com/gh_mirrors/in/incubator-fury
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考