从0到1掌握Apache Arrow Rust:高性能列式数据处理实战指南
你是否还在为Rust数据处理性能瓶颈发愁?是否在寻找跨语言数据交互的高效方案?本文将带你全面掌握Apache Arrow Rust实现(arrow-rs)的核心技术,从基础架构到高级应用,让你在30分钟内从零构建高性能列式数据处理系统。读完本文,你将获得:
- 理解Arrow内存模型与零拷贝机制的底层原理
- 掌握10+核心数据结构的创建与操作技巧
- 实现CSV/Parquet/IPC多种格式的高效读写
- 构建分布式数据服务的完整解决方案
- 性能优化与生产环境部署的最佳实践
Apache Arrow生态与架构解析
Apache Arrow是一个跨语言的列式内存数据标准,旨在解决大数据处理中的数据表示和交换问题。arrow-rs作为其官方Rust实现,以安全、高效和零拷贝为核心设计理念,为Rust生态提供了强大的列式数据处理能力。
核心技术优势
Arrow的革命性在于其定义的内存格式规范,使得不同系统和语言之间可以共享数据而无需序列化开销。其核心优势包括:
特性 | 传统行式存储 | Arrow列式存储 | 性能提升倍数 |
---|---|---|---|
内存效率 | 低(重复存储字段名) | 高(按列连续存储) | 3-5倍 |
缓存利用率 | 低(选择性查询加载多余数据) | 高(仅加载所需列) | 2-4倍 |
向量化计算 | 困难 | 原生支持 | 5-10倍 |
跨语言交互 | 需要序列化/反序列化 | 零拷贝共享 | 10-100倍 |
模块化架构设计
arrow-rs采用高度模块化的设计,各个功能被组织在不同的crate中,用户可以根据需求灵活选择:
核心crate功能说明:
- arrow-array: 提供所有Arrow数组类型的安全实现,如
Int32Array
、StringArray
等 - arrow-schema: 定义数据类型系统和模式信息
- arrow-buffer: 内存缓冲区管理,实现高效的内存操作
- arrow-compute: 包含各类向量化计算内核,如算术运算、比较、排序等
- arrow-ipc: 实现Arrow IPC协议,支持跨进程数据交换
- parquet: 提供Parquet文件格式的读写支持
- arrow-flight: 实现Arrow Flight RPC协议,支持分布式数据服务
环境搭建与项目配置
快速开始
要在项目中使用arrow-rs,只需在Cargo.toml
中添加依赖:
[dependencies]
arrow = { version = "52.1.0", features = ["csv", "ipc", "json", "prettyprint"] }
parquet = { version = "52.1.0", features = ["arrow"] }
arrow-flight = "52.1.0"
主要特性说明:
csv
: 启用CSV读写功能ipc
: 启用IPC协议支持json
: 启用JSON读写功能prettyprint
: 启用数据格式化打印功能ipc_compression
: 启用IPC压缩支持(需额外添加)
源码编译与测试
如果需要从源码构建:
# 克隆仓库
git clone https://gitcode.com/gh_mirrors/arro/arrow-rs
cd arrow-rs
# 构建项目
cargo build
# 运行测试
cargo test
# 构建示例
cargo build --examples
# 运行CSV读取示例
cargo run --example read_csv
核心数据结构详解
Arrow内存模型
Arrow采用高效的内存布局,所有数组都由以下部分组成:
- 长度(Length): 数组元素总数
- 空值掩码(Null Bitmap): 标识哪些元素是空值
- 数据缓冲区(Data Buffers): 存储实际数据
基础数组操作
创建数组
Arrow提供了多种创建数组的方式:
use arrow_array::{Int32Array, StringArray, BooleanArray, ArrayRef};
use std::sync::Arc;
// 从Vec创建Int32Array(不含空值)
let int_array = Int32Array::from(vec![1, 2, 3, 4, 5]);
// 从Option<Vec>创建Int32Array(含空值)
let int_array_with_nulls = Int32Array::from(vec![Some(1), None, Some(3), None, Some(5)]);
// 创建StringArray
let str_array = StringArray::from(vec!["foo", "bar", "baz"]);
// 创建BooleanArray
let bool_array = BooleanArray::from(vec![true, false, true, true]);
// 创建ArrayRef(类型擦除的数组引用)
let array_ref: ArrayRef = Arc::new(int_array);
数组访问与操作
// 访问元素
assert_eq!(int_array.value(0), 1);
assert_eq!(int_array_with_nulls.is_null(1), true);
assert_eq!(str_array.value(1), "bar");
// 数组切片
let sliced = int_array.slice(1, 3); // 从索引1开始,取3个元素
assert_eq!(sliced.len(), 3);
assert_eq!(sliced.value(0), 2);
// 数组迭代
let values: Vec<Option<i32>> = int_array_with_nulls.iter().collect();
assert_eq!(values, vec![Some(1), None, Some(3), None, Some(5)]);
// 数组转换
let to_vec: Vec<i32> = int_array.iter().map(|v| v.unwrap()).collect();
assert_eq!(to_vec, vec![1, 2, 3, 4, 5]);
复合数据类型
结构体数组(StructArray)
use arrow_array::{StructArray, Int32Array, StringArray};
use arrow_schema::{Schema, Field, DataType};
use std::sync::Arc;
// 定义结构体字段
let fields = vec![
Field::new("id", DataType::Int32, false),
Field::new("name", DataType::Utf8, false),
Field::new("age", DataType::Int32, true),
];
// 创建子数组
let ids = Arc::new(Int32Array::from(vec![1, 2, 3]));
let names = Arc::new(StringArray::from(vec!["Alice", "Bob", "Charlie"]));
let ages = Arc::new(Int32Array::from(vec![Some(30), None, Some(25)]));
// 创建结构体数组
let struct_array = StructArray::from((
fields,
vec![ids, names, ages],
));
// 访问结构体字段
let ids = struct_array.column(0).as_any().downcast_ref::<Int32Array>().unwrap();
assert_eq!(ids.value(0), 1);
// 访问结构体元素
let element = struct_array.value(1);
assert_eq!(element.get(0).as_any().downcast_ref::<Int32Array>().unwrap().value(0), 2);
assert_eq!(element.get(1).as_any().downcast_ref::<StringArray>().unwrap().value(0), "Bob");
列表数组(ListArray)
use arrow_array::{ListArray, Int32Array};
use arrow_schema::{Field, DataType};
use std::sync::Arc;
// 创建列表元素数组
let values = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5, 6]));
// 创建偏移量数组 [0, 2, 2, 5, 6]
let offsets = vec![0, 2, 2, 5, 6];
let offsets = Arc::new(Int32Array::from(offsets));
// 创建列表数组
let list_array = ListArray::try_new(
Field::new("list", DataType::Int32, true),
offsets,
values,
None, // 无空值
).unwrap();
// 访问列表元素
assert_eq!(list_array.value(0).len(), 2); // [1, 2]
assert_eq!(list_array.value(1).len(), 0); // []
assert_eq!(list_array.value(2).len(), 3); // [3, 4, 5]
数据读写操作
CSV文件处理
读取CSV文件
use std::fs::File;
use std::sync::Arc;
use arrow::csv::ReaderBuilder;
use arrow::datatypes::{DataType, Field, Schema};
use arrow::util::pretty::print_batches;
fn read_csv() -> Result<(), Box<dyn std::error::Error>> {
// 定义schema
let schema = Schema::new(vec![
Field::new("city", DataType::Utf8, false),
Field::new("lat", DataType::Float64, false),
Field::new("lng", DataType::Float64, false),
]);
// 打开文件
let file = File::open("uk_cities.csv")?;
// 创建CSV读取器
let mut reader = ReaderBuilder::new(Arc::new(schema))
.has_headers(true)
.with_batch_size(1024)
.build(file)?;
// 读取所有批次
let mut batches = Vec::new();
while let Some(batch) = reader.next()? {
batches.push(batch);
}
// 打印结果
print_batches(&batches)?;
Ok(())
}
写入CSV文件
use std::fs::File;
use std::sync::Arc;
use arrow::array::{Int32Array, StringArray};
use arrow::csv::Writer;
use arrow::datatypes::{DataType, Field, Schema};
use arrow::record_batch::RecordBatch;
fn write_csv() -> Result<(), Box<dyn std::error::Error>> {
// 创建数据
let ids = Arc::new(Int32Array::from(vec![1, 2, 3, 4]));
let names = Arc::new(StringArray::from(vec!["Alice", "Bob", "Charlie", "Diana"]));
let ages = Arc::new(Int32Array::from(vec![30, 25, 35, 28]));
// 创建schema
let schema = Schema::new(vec![
Field::new("id", DataType::Int32, false),
Field::new("name", DataType::Utf8, false),
Field::new("age", DataType::Int32, false),
]);
// 创建RecordBatch
let batch = RecordBatch::try_new(
Arc::new(schema.clone()),
vec![ids, names, ages],
)?;
// 创建CSV写入器
let file = File::create("people.csv")?;
let mut writer = Writer::new(file);
// 写入数据
writer.write(&batch)?;
writer.finish()?;
Ok(())
}
Parquet文件处理
Parquet是一种高效的列式存储格式,适合大规模数据分析。arrow-rs提供了完整的Parquet读写支持。
use std::fs::File;
use arrow::record_batch::RecordBatch;
use parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder;
use parquet::arrow::arrow_writer::ArrowWriter;
use parquet::file::properties::WriterProperties;
// 读取Parquet文件
fn read_parquet(path: &str) -> Result<Vec<RecordBatch>, Box<dyn std::error::Error>> {
let file = File::open(path)?;
// 创建Parquet读取器
let parquet_reader = ParquetRecordBatchReaderBuilder::try_new(file)?
.with_batch_size(8192) // 设置批处理大小
.build()?;
// 读取所有批次
let mut batches = Vec::new();
for batch in parquet_reader {
batches.push(batch?);
}
Ok(batches)
}
// 写入Parquet文件
fn write_parquet(
path: &str,
batches: &[RecordBatch],
compression: bool
) -> Result<(), Box<dyn std::error::Error>> {
let file = File::create(path)?;
// 配置写入属性
let mut writer_props_builder = WriterProperties::builder();
if compression {
writer_props_builder = writer_props_builder.with_compression(parquet::basic::Compression::SNAPPY);
}
let writer_props = writer_props_builder.build();
// 创建Parquet写入器
let mut writer = ArrowWriter::try_new(
file,
batches[0].schema(),
Some(writer_props),
)?;
// 写入所有批次
for batch in batches {
writer.write(batch)?;
}
writer.close()?;
Ok(())
}
IPC协议与数据交换
Arrow IPC协议允许在不同进程或系统间高效交换数据,支持零拷贝传输。
use std::fs::File;
use arrow::ipc::writer::FileWriter;
use arrow::ipc::reader::FileReader;
use arrow::record_batch::RecordBatch;
// 写入IPC文件
fn write_ipc(
path: &str,
batches: &[RecordBatch]
) -> Result<(), Box<dyn std::error::Error>> {
let file = File::create(path)?;
// 创建IPC写入器
let mut writer = FileWriter::try_new(file, batches[0].schema())?;
// 写入所有批次
for batch in batches {
writer.write(batch)?;
}
writer.finish()?;
Ok(())
}
// 读取IPC文件
fn read_ipc(path: &str) -> Result<Vec<RecordBatch>, Box<dyn std::error::Error>> {
let file = File::open(path)?;
// 创建IPC读取器
let mut reader = FileReader::try_new(file)?;
// 读取所有批次
let mut batches = Vec::new();
while let Some(batch) = reader.next()? {
batches.push(batch);
}
Ok(batches)
}
启用压缩支持:
arrow = { version = "52.1.0", features = ["ipc_compression"] }
// 创建带压缩的IPC写入器
let props = WriterProperties::builder()
.with_compression(CompressionType::LZ4)
.build();
let mut writer = FileWriter::try_new_with_properties(file, batches[0].schema(), props)?;
计算内核与数据处理
基础计算操作
arrow-rs提供了丰富的向量化计算内核,支持各类算术、比较和逻辑运算。
use arrow::array::{Int32Array, Float64Array};
use arrow::compute::kernels::numeric;
use arrow::compute::kernels::cmp;
// 算术运算
let a = Int32Array::from(vec![1, 2, 3, 4, 5]);
let b = Int32Array::from(vec![5, 4, 3, 2, 1]);
let sum = numeric::add(&a, &b).unwrap();
assert_eq!(sum, Int32Array::from(vec![6, 6, 6, 6, 6]));
let diff = numeric::sub(&a, &b).unwrap();
assert_eq!(diff, Int32Array::from(vec![-4, -2, 0, 2, 4]));
// 比较运算
let eq = cmp::eq(&a, &b).unwrap();
assert_eq!(eq, BooleanArray::from(vec![false, false, true, false, false]));
let gt = cmp::gt(&a, &b).unwrap();
assert_eq!(gt, BooleanArray::from(vec![false, false, false, true, true]));
聚合操作
use arrow::array::{Int32Array, Float64Array};
use arrow::compute::kernels::aggregate;
let data = Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
// 基本聚合
let sum = aggregate::sum(&data).unwrap();
assert_eq!(sum, Some(55));
let min = aggregate::min(&data).unwrap();
assert_eq!(min, Some(1));
let max = aggregate::max(&data).unwrap();
assert_eq!(max, Some(10));
let mean = aggregate::mean(&data).unwrap();
assert_eq!(mean, Some(5.5));
// 带空值的聚合
let data_with_nulls = Int32Array::from(vec![Some(1), None, Some(3), Some(4), None, Some(6)]);
let sum = aggregate::sum(&data_with_nulls).unwrap();
assert_eq!(sum, Some(14)); // 1 + 3 + 4 + 6 = 14
过滤与排序
use arrow::array::{Int32Array, BooleanArray};
use arrow::compute::kernels::{filter, sort};
let data = Int32Array::from(vec![5, 3, 8, 1, 9, 2, 7, 4, 6]);
// 过滤数据
let mask = BooleanArray::from(vec![
false, true, false, true, true, false, true, false, true
]);
let filtered = filter::filter(&data, &mask).unwrap();
assert_eq!(filtered, Int32Array::from(vec![3, 1, 9, 7, 6]));
// 排序数据
let sorted = sort::sort(&data, None).unwrap();
assert_eq!(sorted, Int32Array::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]));
// 带排序选项
let sort_options = sort::SortOptions {
descending: true,
nulls_first: false,
};
let sorted_desc = sort::sort_with_options(&data, None, sort_options).unwrap();
assert_eq!(sorted_desc, Int32Array::from(vec![9, 8, 7, 6, 5, 4, 3, 2, 1]));
分布式数据服务:Arrow Flight
Arrow Flight是一个基于gRPC的RPC协议,专为高性能数据传输设计。下面是一个简单的Flight服务实现:
Flight服务端
use std::sync::Arc;
use tonic::transport::Server;
use tonic::{Request, Response, Status};
use futures::stream;
use arrow_flight::{
flight_service_server::FlightService, flight_service_server::FlightServiceServer,
Action, ActionType, Criteria, Empty, FlightData, FlightDescriptor,
FlightInfo, HandshakeRequest, HandshakeResponse, PollInfo, PutResult,
SchemaResult, Ticket
};
use arrow::record_batch::RecordBatch;
use arrow::array::{Int32Array, StringArray};
use arrow::schema::{Schema, Field, DataType};
#[derive(Clone)]
struct FlightServerImpl {
// 服务端可以保存数据或连接到数据库
data: Arc<Vec<RecordBatch>>,
schema: Schema,
}
#[tonic::async_trait]
impl FlightService for FlightServerImpl {
type HandshakeStream = stream::BoxStream<'static, Result<HandshakeResponse, Status>>;
type ListFlightsStream = stream::BoxStream<'static, Result<FlightInfo, Status>>;
type DoGetStream = stream::BoxStream<'static, Result<FlightData, Status>>;
type DoPutStream = stream::BoxStream<'static, Result<PutResult, Status>>;
type DoActionStream = stream::BoxStream<'static, Result<arrow_flight::Result, Status>>;
type ListActionsStream = stream::BoxStream<'static, Result<ActionType, Status>>;
type DoExchangeStream = stream::BoxStream<'static, Result<FlightData, Status>>;
async fn get_schema(
&self,
request: Request<FlightDescriptor>,
) -> Result<Response<SchemaResult>, Status> {
// 构建SchemaResult
let schema_bytes = self.schema.to_ipc().map_err(|e| Status::internal(e.to_string()))?;
Ok(Response::new(SchemaResult {
schema: schema_bytes.into(),
..Default::default()
}))
}
async fn do_get(
&self,
request: Request<Ticket>,
) -> Result<Response<Self::DoGetStream>, Status> {
let ticket = request.into_inner();
let ticket = String::from_utf8(ticket.ticket).map_err(|e| Status::invalid_argument(e.to_string()))?;
// 根据ticket决定返回什么数据
// 这里简单返回预设数据
let data = self.data.clone();
// 将RecordBatch转换为FlightData流
let mut flight_data = Vec::new();
// 首先发送Schema
let schema_data = self.schema.to_ipc().unwrap();
flight_data.push(FlightData {
data_header: schema_data.into(),
..Default::default()
});
// 然后发送数据
for batch in data.iter() {
let ipc_batch = batch.to_ipc().unwrap();
flight_data.push(FlightData {
data_body: ipc_batch.body.into(),
..Default::default()
});
}
// 创建数据流
let output = stream::iter(flight_data.into_iter().map(Ok));
Ok(Response::new(Box::pin(output)))
}
// 实现其他必要方法...
async fn handshake(...) -> Result<...> {
Err(Status::unimplemented("handshake"))
}
async fn list_flights(...) -> Result<...> {
Err(Status::unimplemented("list_flights"))
}
// 其他方法类似...
}
async fn run_server() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:50051".parse()?;
// 创建示例数据
let schema = Schema::new(vec![
Field::new("id", DataType::Int32, false),
Field::new("name", DataType::Utf8, false),
]);
let ids = Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5]));
let names = Arc::new(StringArray::from(vec!["Alice", "Bob", "Charlie", "Diana", "Eve"]));
let batch = RecordBatch::try_new(
Arc::new(schema.clone()),
vec![ids, names],
).unwrap();
let data = Arc::new(vec![batch]);
let server = FlightServerImpl {
data,
schema,
};
let svc = FlightServiceServer::new(server);
println!("Flight server listening on {}", addr);
Server::builder().add_service(svc).serve(addr).await?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
run_server().await
}
Flight客户端
use std::sync::Arc;
use arrow_flight::flight_service_client::FlightServiceClient;
use arrow_flight::{Ticket, FlightDescriptor};
use arrow::ipc::convert::fb_to_schema;
use arrow::util::pretty::print_batches;
async fn run_client() -> Result<(), Box<dyn std::error::Error>> {
// 连接到Flight服务
let mut client = FlightServiceClient::connect("http://[::1]:50051").await?;
// 请求数据
let ticket = Ticket {
ticket: "get_data".as_bytes().to_vec(),
};
let request = tonic::Request::new(ticket);
// 获取数据流
let mut stream = client.do_get(request).await?.into_inner();
// 处理响应
let mut batches = Vec::new();
let mut schema = None;
while let Some(result) = stream.message().await? {
if schema.is_none() {
// 第一个消息包含Schema
let schema_data = result.data_header;
schema = Some(fb_to_schema(&schema_data)?);
} else {
// 后续消息包含数据
let batch_data = result.data_body;
let batch = arrow::ipc::convert::fb_to_batch(
&batch_data,
schema.as_ref().unwrap(),
)?;
batches.push(batch);
}
}
// 打印结果
print_batches(&batches)?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
run_client().await
}
性能优化与最佳实践
内存管理
- 利用零拷贝:尽可能使用切片操作(
slice()
)而非复制数据 - 批处理大小:选择合适的批处理大小(通常8192-65536行)
- 避免中间分配:使用
MutableArray
和MutableBuffer
构建数据 - 释放不需要的内存:及时 drop 不再需要的数组和缓冲区
// 高效构建数组
use arrow::array::MutableInt32Array;
let mut mutable_array = MutableInt32Array::with_capacity(1024);
for i in 0..1024 {
mutable_array.push(Some(i as i32));
}
let array = mutable_array.freeze(); // 转换为不可变数组,无内存拷贝
并行处理
Arrow数组是线程安全的,可以安全地在多线程环境中使用:
use std::thread;
use arrow::array::Int32Array;
use arrow::compute::sum;
fn parallel_sum(array: &Int32Array, num_threads: usize) -> i64 {
let chunk_size = (array.len() + num_threads - 1) / num_threads;
let mut handles = Vec::new();
for i in 0..num_threads {
let start = i * chunk_size;
let end = std::cmp::min((i + 1) * chunk_size, array.len());
let slice = array.slice(start, end - start);
handles.push(thread::spawn(move || {
sum(&slice).unwrap().unwrap_or(0)
}));
}
let mut total = 0;
for handle in handles {
total += handle.join().unwrap();
}
total
}
数据类型选择
选择合适的数据类型对性能至关重要:
- 数值类型:使用最小可行的类型(如i32而非i64,f32而非f64)
- 字符串类型:考虑使用字典编码(
DictionaryArray
)减少重复字符串存储 - 时间类型:使用适当的时间单位(如毫秒精度足够时不要使用纳秒)
- 小数类型:使用
DecimalArray
而非字符串存储小数
// 使用字典编码优化字符串存储
use arrow_array::{DictionaryArray, StringArray};
use arrow::datatypes::{DataType, Int32Type};
let strings = StringArray::from(vec![
"apple", "banana", "apple", "orange", "banana", "apple"
]);
// 创建字典数组
let dict_array = DictionaryArray::<Int32Type>::from(strings);
// 字典数组通常比原字符串数组占用更少内存
assert!(dict_array.get_array_memory_size() < strings.get_array_memory_size());
应用场景与案例分析
场景一:数据处理管道
代码示例:
// 数据处理管道示例
fn data_pipeline() -> Result<(), Box<dyn std::error::Error>> {
// 1. 读取CSV数据
let batches = read_csv("input.csv")?;
// 2. 数据清洗与转换
let cleaned_batches = clean_data(&batches)?;
// 3. 数据分析
let analysis_result = analyze_data(&cleaned_batches)?;
// 4. 写入Parquet文件
write_parquet("output.parquet", &cleaned_batches, true)?;
// 5. 发送到Flight服务
send_to_flight_service(&analysis_result)?;
Ok(())
}
场景二:数据库查询引擎
Arrow可以作为内存计算引擎的基础,支持高效的查询处理:
// 简化的查询处理示例
fn query_engine_example() -> Result<(), Box<dyn std::error::Error>> {
// 1. 从Parquet文件加载数据
let batches = read_parquet("large_dataset.parquet")?;
// 2. 过滤数据
let filtered = filter_data(&batches, "age > 30 AND salary > 50000")?;
// 3. 分组聚合
let aggregated = group_by(&filtered, &["department"], &["AVG(salary)", "COUNT(*)"])?;
// 4. 排序结果
let sorted = sort_data(&aggregated, "AVG(salary) DESC")?;
// 5. 输出结果
print_batches(&sorted)?;
Ok(())
}
总结与展望
Apache Arrow Rust实现(arrow-rs)为高性能数据处理提供了强大支持,其核心优势包括:
- 高效内存模型:采用列式存储和零拷贝设计,最大化内存利用率和缓存效率
- 丰富数据类型:完整支持所有Arrow数据类型,包括复杂嵌套类型
- 全面计算能力:提供大量向量化计算内核,支持高效数据处理
- 多格式支持:内置CSV、JSON、Parquet等多种格式的读写能力
- 分布式计算:通过Arrow Flight协议支持高性能数据传输
随着数据处理需求的不断增长,arrow-rs将继续发展,未来可能的改进方向包括:
- 更完善的异步IO:进一步优化异步数据读写性能
- GPU加速:利用GPU进行并行计算,提升大规模数据处理能力
- 更多数据格式支持:添加对ORC、Feather等格式的原生支持
- 机器学习集成:更好地与TensorFlow、PyTorch等机器学习框架集成
通过掌握arrow-rs,开发者可以构建高效、可扩展的数据处理系统,满足从简单数据分析到大规模分布式计算的各类需求。
附录:常用API速查表
功能 | 模块 | 关键函数/结构体 |
---|---|---|
数组创建 | arrow_array | Int32Array::from, StringArray::from, StructArray::from |
数据类型 | arrow_schema | DataType, Field, Schema |
CSV读写 | arrow_csv | ReaderBuilder, Writer |
Parquet读写 | parquet | ParquetRecordBatchReaderBuilder, ArrowWriter |
IPC读写 | arrow_ipc | FileReader, FileWriter |
聚合操作 | arrow::compute | sum, min, max, mean, count |
过滤排序 | arrow::compute | filter, sort, take |
Flight服务 | arrow_flight | FlightService, FlightClient |
收藏本文,随时查阅Apache Arrow Rust实现的核心技术与最佳实践。关注更新,获取更多数据处理性能优化技巧!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考