Parquet与Avro列读取性能测试不符宣称优势,求技术解析
Parquet是一种列存格式,官方宣称其相比其他格式的核心优势是快速读取指定列。为此,我在本地环境使用Spark(Java 8)对Parquet和Avro文件的读取速度进行对比测试。
测试模型
public class Order implements Serializable { private int number; public String client; private String claim; // getters/setters }
我生成了包含100万条相同记录的Parquet和Avro文件:number为递增唯一整数序列,client为20个随机重复值的集合,claim为唯一值集合。
测试代码
Parquet读取代码
public void readParquet(){ Dataset<Row> parquetFileDF = spark.read().parquet("orders_parquet"); parquetFileDF.createOrReplaceTempView("orders"); long beginTime2 = System.nanoTime(); spark.sql("SELECT id FROM orders"); long endTime2 = System.nanoTime(); System.out.println("parquet select " + (endTime2 - beginTime2)); }
Avro读取代码
public void readAvro(){ Dataset<Row> parquetFileDF = spark.read().format("avro").load("orders_avro"); long beginTime2 = System.nanoTime(); parquetFileDF.select("client"); long endTime2 = System.nanoTime(); System.out.println("avro select " + (endTime2 - beginTime2)); }
测试结果
首次测试结果显示,Spark读取Parquet文件的client列耗时50109200纳秒(50毫秒),读取Avro文件的client列耗时12253000纳秒(12毫秒)。后续测试结果一致,Avro文件读取速度比Parquet快4倍,这与Parquet宣称的读取优势不符。请问我是否遗漏了某些关键细节?
关键遗漏细节分析
Spark惰性执行的影响:你当前的代码只执行了
select或SQL查询这类转换操作,并没有触发Spark的行动操作(比如collect()、count()、write())。Spark的转换操作是惰性的,只会生成逻辑执行计划,不会真正读取磁盘上的数据。你测量的只是计划生成的时间,不是实际数据读取的耗时。Parquet测试代码的字段错误:Parquet测试中SQL语句是
SELECT id FROM orders,但你的Order类里并没有id字段,只有number字段。这个错误会导致SQL执行异常,或者Spark无法找到对应列,实际并没有触发真实的数据读取流程,时间测量结果完全无效。压缩与存储特性差异:
- Parquet默认启用Snappy压缩,压缩会增加CPU解码开销,而如果Avro未配置压缩,在本地IO不是瓶颈的场景下,未压缩的Avro读取会更快。
- Parquet作为列存格式,需要解析额外的列元数据,在100万条这种小数据集下,元数据解析的固定开销会抵消列存的优势,只有在大数据集(比如千万级以上)或者读取少量列的场景下,Parquet的列裁剪优势才会凸显。
编码策略的开销:你的
client字段是20个重复值,Parquet会自动对这类重复值进行字典编码,编码和解码过程会带来额外的CPU开销;而Avro作为行存格式,在小数据量下读取重复值的开销更低。
内容的提问来源于stack exchange,提问作者Jelly

