数据存储——Parquet文件使用笔记


Parquet 整体说明

  • Parquet(Apache Parquet)是一种列式存储的二进制文件格式,专为大数据处理场景设计,具有高效压缩和编码特性
  • Parquet 尤其适合分析型工作负载,能显著提升查询速度并降低存储成本

Parquet 文件结构

  • Parquet文件由多个行组(Row Groups)组成,每个行组包含:
  • 列块(Column Chunks) :存储实际列数据
  • 元数据 :记录统计信息(如min/max),用于加速查询

parquet-tools 工具快速查看 parquet 文件

  • 工具安装包下载页面:https://github.com/hangxie/parquet-tools/releases
  • 下载后安装
  • 安装后即可 通过 parquet-tools 快速访问 parquet 文件
  • 注:M 系列 Mac 安装时直接下载 parquet-tools-v1.53.1-darwin-arm64.gz 版本解压后放到指定目录下就可以用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 下载文件
    wget https://github.com/hangxie/parquet-tools/releases/download/v1.53.1/parquet-tools-v1.53.1-darwin-arm64.gz

    # 解压文件
    gzip -d -c parquet-tools-v1.53.1-darwin-arm64.gz > parquet-tools-v1.53.1-darwin-arm64
    # Liunx 中也可以用 gunzip parquet-tools-v1.53.1-darwin-arm64.gz

    # 移动到指定目录
    sudo mv parquet-tools-v1.53.1-darwin-arm64 /usr/local/bin/parquet-tools

    # 添加执行权限
    chmod +x /usr/local/bin/parquet-tools

    # Mac 安全检查可能需要下面的命令(用管理员权限,彻底清空该文件所有扩展隐藏属性)
    sudo xattr -cr /usr/local/bin/parquet-tools

统计行数

  • 行数查看:
    1
    parquet-tools row-count file.parquet

查看结构

  • 结构查看

    1
    parquet-tools schema file.parquet
  • 常结合 jq 打印 json 可读版本

    1
    parquet-tools schema file.parquet | jq

查看某些行

  • 常常结合 json 格式来访问和查看:

    1
    2
    3
    # 抽取第一行并用 json 格式打印
    # 注:有的版本使用的是 parquet-tools head -n 来访问,具体需要看安装的版本,如 Go,Python,Java 等版本
    parquet-tools cat -l 2 test.parquet | jq '.'
  • parquet-tools cat -l 抽取到的一般是一个 list ,所以常用下面的方式查看具体的一个完整 json 对象:

    1
    2
    # 抽取第一行并用 json 格式打印
    parquet-tools cat -l 1 test.parquet | jq '.[0]'
  • 查看中间某些行(下面是第 11 到 15 行):

    1
    parquet-tools cat --offset 10 -l 5 file.parquet

拆分文件

  • 拆分文件(不好用)

    1
    2
    3
    4
    # 将文件按照最多 xxx 行一个文件拆分,命名形式为 part-000001.parquet 形式,若不指定
    parquet-tools split --record-count 1000 --name-format "part-%06d.parquet" test.parquet

    # parquet-tools 不支持仅输出 前 xxx 行,需要手动删除其余文件
    • --name-format 制定格式,不指定本参数时,默认格式是 “result-%06d.parquet” 形式
  • 建议使用下面的 Python 来读写前 N 行

    1
    2
    3
    import polars as pl
    df = pl.read_parquet('test.parquet')
    df.head(1000).write_parquet('sample.parquet')
    • 也可以将这个 python 代码写成可复用的脚本
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      #!/usr/bin/env python3
      """
      Extract the first N rows from a Parquet file and save as a new Parquet file.

      Usage:
      python extract_rows.py -i input.parquet -o output.parquet -n 1000
      """

      import argparse
      import polars as pl
      from pathlib import Path


      def main():
      parser = argparse.ArgumentParser(
      description="Extract the first N rows from a Parquet file."
      )
      parser.add_argument(
      "-i", "--input",
      required=True,
      help="Path to the input Parquet file."
      )
      parser.add_argument(
      "-o", "--output",
      required=True,
      help="Path to the output Parquet file. "
      )
      parser.add_argument(
      "-n", "--lines",
      type=int,
      required=True,
      help="Number of rows to extract from the beginning. "
      )
      args = parser.parse_args()

      input_path = Path(args.input)
      if not input_path.exists():
      print(f"Error: Input file '{input_path}' does not exist.")
      return 1

      try:
      df = pl.read_parquet(input_path)
      df.head(args.lines).write_parquet(args.output)
      print(f"Successfully extracted {args.lines} rows to '{args.output}'.")
      except Exception as e:
      print(f"Error: {e}")
      return 1

      return 0


      if __name__ == "__main__":
      exit(main())

合并文件

  • 合并文件
    1
    2
    3
    4
    5
    # 方式 A:逗号分隔
    parquet-tools merge merge.parquet -s test.parquet,train.parquet

    # 方式 B:多次 -s
    parquet-tools merge merge.parquet -s test.parquet -s train.parquet

Parquet 文件读取示例

  • Sparkdf.write.parquet("path") / spark.read.parquet("path")
  • Pandaspd.read_parquet("file.parquet", engine="pyarrow")
  • HiveCREATE TABLE ... STORED AS PARQUET

附录:文件读取和分析示例

  • 使用 Python 代码读取并解析文件格式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import pandas as pd

    def read_and_inspect_parquet(file_path):
    try:
    # 读取Parquet文件
    df = pd.read_parquet(file_path)

    print("成功读取Parquet文件!")
    print("\n===== 数据基本信息 =====")
    df.info() # 显示数据框的基本信息,包括列名、数据类型、非空值数量等

    for col in df.columns:
    print("col:", col)
    print(df[col][0])

    print("\n===== 前5行数据 =====")
    print(df.head()) # 显示前5行数据,查看数据格式

    print("\n===== 数据统计信息 =====")
    print(df.describe()) # 显示数值型列的统计信息

    return df

    except FileNotFoundError:
    print(f"错误: 找不到文件 '{file_path}'")
    except Exception as e:
    print(f"读取Parquet文件时发生错误: {str(e)}")
    return None

    if __name__ == "__main__":
    parquet_file_path = "./gsm8k/test.parquet"

    # 读取并查看Parquet文件
    dataframe = read_and_inspect_parquet(parquet_file_path)

附录:列式存储 vs 行式存储

  • 行式存储(如CSV、JSON):按行存储数据,适合整行读取的场景(如单条记录查询)
  • 列式存储(如Parquet):按列存储数据,适合只读取部分列或聚合分析的场景(如计算某列的平均值)