如何在EC2 Java服务中解压Hadoop Parquet+Snappy压缩文件?
正确解压Hadoop Parquet+Snappy文件的方法
问题根源
你直接用SnappyDecompressor或SnappyInputStream读取Parquet文件的方式是错误的:Parquet的Snappy压缩是针对文件内部的列数据块做的压缩,并非整个文件被Snappy包裹,不能直接用Snappy的基础解压工具读取。另外UnsatisfiedLinkError是因为Hadoop的Snappy实现依赖平台本地库(native library),EC2环境中可能缺少对应库或配置不当。
正确解决方案
1. 引入正确的依赖
在你的Java项目中,需要引入Parquet的读取依赖(因为你用AvroParquetWriter写入,优先用parquet-avro),同时确保Hadoop相关依赖正确:
Maven依赖示例:
<dependencies> <!-- Parquet Avro 读取依赖 --> <dependency> <groupId>org.apache.parquet</groupId> <artifactId>parquet-avro</artifactId> <version>1.14.0</version> </dependency> <!-- Hadoop Common 用于处理S3和压缩 --> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>3.3.6</version> <exclusions> <!-- 排除冲突依赖,按需调整 --> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions> </dependency> <!-- AWS SDK 用于读取S3对象 --> <dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <version>2.25.0</version> </dependency> </dependencies>
2. 使用ParquetReader读取文件(自动处理Snappy解压)
不需要手动调用Snappy解压工具,用AvroParquetReader可以直接读取用AvroParquetWriter写入的Parquet文件,框架会自动识别并处理Snappy压缩:
import org.apache.parquet.avro.AvroParquetReader; import org.apache.parquet.hadoop.ParquetReader; import org.apache.parquet.hadoop.util.HadoopInputFile; import org.apache.hadoop.fs.Path; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import java.io.InputStream; import java.util.ArrayList; import java.util.List; // 假设你的Avro生成的类是MyRecord public class ParquetSnappyReader { public static List<MyRecord> readParquetFromS3(String bucketName, String key) throws Exception { // 初始化S3客户端 S3Client s3Client = S3Client.builder() .region(Region.US_EAST_1) // 替换为你的区域 .credentialsProvider(DefaultCredentialsProvider.create()) .build(); // 获取S3对象输入流 GetObjectRequest getObjectRequest = GetObjectRequest.builder() .bucket(bucketName) .key(key) .build(); InputStream s3InputStream = s3Client.getObject(getObjectRequest); // 将S3路径转换为Hadoop InputFile Path parquetPath = new Path("s3://" + bucketName + "/" + key); HadoopInputFile inputFile = HadoopInputFile.fromPath(parquetPath, new org.apache.hadoop.conf.Configuration()); // 创建AvroParquetReader并读取数据 try (ParquetReader<MyRecord> reader = AvroParquetReader.<MyRecord>builder(inputFile) .withConf(new org.apache.hadoop.conf.Configuration()) .build()) { List<MyRecord> records = new ArrayList<>(); MyRecord record; while ((record = reader.read()) != null) { records.add(record); } return records; } finally { s3InputStream.close(); s3Client.close(); } } }
3. 解决UnsatisfiedLinkError的补充方案
如果运行时仍然出现本地库相关错误,需要在EC2实例上安装Snappy本地库,并配置Hadoop的native library路径:
- 安装Snappy(以Amazon Linux 2为例):
sudo yum install snappy snappy-devel
- 在Java启动参数中指定Hadoop本地库路径(路径根据你的Hadoop版本和安装位置调整):
-Djava.library.path=/usr/lib64/hadoop/lib/native/
或者在代码中配置:
System.setProperty("java.library.path", "/usr/lib64/hadoop/lib/native/");
关键注意事项
- 不要直接用Snappy的基础解压工具处理Parquet文件,必须用Parquet专用读取框架。
- 确保依赖版本匹配:parquet-avro、hadoop-common的版本尽量和Flink作业中使用的版本一致,避免兼容性问题。
- EC2实例的IAM权限需要允许读取目标S3 bucket的对象。
内容的提问来源于stack exchange,提问作者priyadhingra19
相关产品推荐
相关产品推荐

