Amazon AthenaでCSVとParquetのスキャン量を比較してみた

Amazon AthenaでCSVとParquetのスキャン量を比較してみた

2026.01.09

はじめに

コンサルティング部のぐっさんです。さむいです。

前回の記事では、 Apache Parquet の仕組みについて確認しました。
今回は実際に AWS Athena を使って、CSV と Parquet でファイルサイズやSQLクエリのスキャン量にどんな違いがあるかを検証してみます。

そこまで大きなデータは用意していないので、クエリ実行の速さよりはスキャン量が実際にどれくらい変わるのかに注目しています。

https://dev.classmethod.jp/articles/apache-parquet-python/

一言まとめ

同じデータでも、Parquet 形式を使うとスキャン量が減り、コスト削減と高速化が期待できます。

検証の流れ

  1. テストデータ(10,000件)を Pythonを使用してCSV / Parquet で作成
  2. S3 にアップロード
  3. Athena でテーブルを作成
  4. 同じクエリを実行して比較

Amazon Athena とは

S3 上のデータに対して SQL でクエリできるサーバーレスサービスです。
料金はスキャンしたデータ量に応じた従量課金($5/TB)なので、巨大なデータを扱う場合はスキャン量を減らすことがそのままコスト削減につながります。

補足 Athena はオープンソースの Trino(旧 Presto)をベースにしており、アドホック分析(その場でサクッとクエリ)に強いのが特徴です。大量データのETL/バッチ処理には Athena for Apache Spark や AWS Glue が使われます。

とても詳しい入門記事はこちらです!

https://dev.classmethod.jp/articles/introduction-2024-amazon-athena/


検証データ

冬なので、(いつか見たいという願望を含め)架空のオーロラ観測データ(10,000件)をそれっぽく作成してみます。
※地名は実際の都市や国を使用していますが、あくまでテストデータです。

aurora

データ構造:

カラム 説明
id int 観測ID
observation_date string 観測日
observation_hour int 観測時刻(時)
location string 観測地
country string
latitude float 緯度
longitude float 経度
temperature int 気温(℃)
kp_index int 地磁気活動指数(1-9)
duration_minutes int 観測時間(分)
color string オーロラの色
intensity int 強度(1-5)
cloud_cover int 雲量(%)
is_photographed bool 撮影したか

データ生成

後述するPython スクリプト(generate_aurora_data.py)を実行してデータを生成します。
架空データでCSVとParquetをまとめて作成し、それぞれのファイルサイズを出力するスクリプトにしています。

前回記事と同じ venv 環境を使用します。

# 仮想環境の有効化
source .venv/bin/activate

# データ生成スクリプトを実行
python generate_aurora_data.py

出力例

(.venv) % python generate_aurora_data.py
データ生成完了: 10000件

ファイルサイズ:
  CSV:     704,164 bytes (687.7 KB)
  Parquet: 180,910 bytes (176.7 KB)

先頭10件:
   id observation_date  observation_hour   location  country  latitude  longitude  temperature  kp_index  duration_minutes   color  intensity  cloud_cover  is_photographed
0   1       2025-09-29                20     Kiruna   Sweden      67.9       20.2          -12         5                72     red          2           94             True
1   2       2026-01-18                21     Kiruna   Sweden      67.9       20.2          -17         7                18   green          1           27             True
2   3       2026-02-02                20  Rovaniemi  Finland      66.5       25.7          -18         4               176    blue          4           28            False
3   4       2025-11-11                20  Rovaniemi  Finland      66.5       25.7          -11         3               118  purple          3           19             True
4   5       2025-09-27                21  Reykjavik  Iceland      64.1      -21.9          -23         2               101  purple          5           33             True
5   6       2025-12-27                 4     Kiruna   Sweden      67.9       20.2          -32         7                30    blue          3           80            False
6   7       2025-10-20                21  Rovaniemi  Finland      66.5       25.7          -34         4                84   green          2           12            False
7   8       2025-12-26                 1  Reykjavik  Iceland      64.1      -21.9          -30         6               100     red          3           89             True
8   9       2026-02-10                22  Rovaniemi  Finland      66.5       25.7          -18         4                51    pink          4           34             True
9  10       2025-11-23                20     Kiruna   Sweden      67.9       20.2          -28         1                90    pink          3            8             True

圧縮オプションなしでも約 74% 削減されています。これは Parquet フォーマット自体の効率(バイナリ形式、重複値の効率的な保存等)によるものです。

補足 pandas の to_parquet()compression パラメータでデフォルト snappy 圧縮が有効になっています。今回は圧縮なし(compression=None)にして、純粋な Parquet フォーマットとしての効率を確認しています。

generate_aurora_data.pyの中身はこちらです。

import pandas as pd
import random
from datetime import datetime, timedelta

# 再現性のためシード固定
random.seed(42)

# 観測地リスト
locations = [
    {'name': 'Fairbanks', 'country': 'USA', 'lat': 64.8, 'lon': -147.7},
    {'name': 'Tromso', 'country': 'Norway', 'lat': 69.6, 'lon': 19.0},
    {'name': 'Reykjavik', 'country': 'Iceland', 'lat': 64.1, 'lon': -21.9},
    {'name': 'Yellowknife', 'country': 'Canada', 'lat': 62.5, 'lon': -114.4},
    {'name': 'Rovaniemi', 'country': 'Finland', 'lat': 66.5, 'lon': 25.7},
    {'name': 'Kiruna', 'country': 'Sweden', 'lat': 67.9, 'lon': 20.2},
]

colors = ['green', 'red', 'purple', 'pink', 'blue']

# 10,000件のデータを生成
data = []
base_date = datetime(2025, 9, 1)  # オーロラシーズン開始

for i in range(1, 10001):
    loc = random.choice(locations)
    obs_date = base_date + timedelta(days=random.randint(0, 180))
    obs_hour = random.randint(20, 28) % 24  # 20時〜翌4時

    data.append({
        'id': i,
        'observation_date': obs_date.strftime('%Y-%m-%d'),
        'observation_hour': obs_hour,
        'location': loc['name'],
        'country': loc['country'],
        'latitude': loc['lat'],
        'longitude': loc['lon'],
        'temperature': random.randint(-35, -5),
        'kp_index': random.randint(1, 9),
        'duration_minutes': random.randint(10, 180),
        'color': random.choice(colors),
        'intensity': random.randint(1, 5),
        'cloud_cover': random.randint(0, 100),
        'is_photographed': random.choice([True, False])
    })

df = pd.DataFrame(data)

# CSV と Parquet で保存(Parquet は圧縮なし)
df.to_csv('data/aurora_observations.csv', index=False)
df.to_parquet('data/aurora_observations.parquet', compression=None)

print(f"データ生成完了: {len(df)}件")
print(f"\nファイルサイズ:")
import os
csv_size = os.path.getsize('data/aurora_observations.csv')
parquet_size = os.path.getsize('data/aurora_observations.parquet')
print(f"  CSV:     {csv_size:,} bytes ({csv_size/1024:.1f} KB)")
print(f"  Parquet: {parquet_size:,} bytes ({parquet_size/1024:.1f} KB)")
print(f"\n先頭10件:")
print(df.head(10))

S3 にアップロード

AWS CLI を使ってデータを S3 にアップロードします。

バケット作成

# バケット作成(バケット名は適宜変更してください)
aws s3 mb s3://your-bucket-name --region ap-northeast-1

データアップロード

CSV と Parquet を別フォルダにアップロードします。

# CSV をアップロード
aws s3 cp data/aurora_observations.csv s3://your-bucket-name/csv/

# Parquet をアップロード
aws s3 cp data/aurora_observations.parquet s3://your-bucket-name/parquet/

確認

aws s3 ls s3://your-bucket-name/ --recursive

出力例

2026-01-09 14:54:16     704164 csv/aurora_observations.csv
2026-01-09 14:54:46     180910 parquet/aurora_observations.parquet

コンソールでもアップロードされたことを確認できます。

s3_1

s3_2

s3_3


Athena テーブル作成

Athena コンソールのクエリエディタで DDL(CREATE TABLE 文)を実行してテーブルを作成します。

補足 初回利用時は、クエリ結果の出力先 S3 バケットの設定が必要です。Athena コンソールの「設定」から設定してください。なお、今回はワークグループなどの設定は特別実施せずにデフォルトのまま実行しています。

データベース作成

まず、テーブルを格納するデータベースを作成します。
エディタの枠内にSQL文を入力し「実行する」を押下します。

CREATE DATABASE IF NOT EXISTS northern_lights_db;

実行結果は以下のようになり、「クエリは成功しました」と出ました。

athena_1

余談ですが、急にデータベース名をnorthern_lightsにしたのはなんだかAmazon Auroraと被ってややこしくなりそう?だったからです。

CSV テーブル作成

CREATE EXTERNAL TABLE northern_lights_db.aurora_csv (
    id INT,
    observation_date STRING,
    observation_hour INT,
    location STRING,
    country STRING,
    latitude DOUBLE,
    longitude DOUBLE,
    temperature INT,
    kp_index INT,
    duration_minutes INT,
    color STRING,
    intensity INT,
    cloud_cover INT,
    is_photographed BOOLEAN
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION 's3://your-bucket-name/csv/'
TBLPROPERTIES ('skip.header.line.count'='1');

補足

  • ROW FORMAT DELIMITED で CSV 形式を指定
  • skip.header.line.count でヘッダー行をスキップ
  • 全カラムの型を手動で定義する必要がある

athena_2

Parquet テーブル作成

CREATE EXTERNAL TABLE northern_lights_db.aurora_parquet (
    id INT,
    observation_date STRING,
    observation_hour INT,
    location STRING,
    country STRING,
    latitude DOUBLE,
    longitude DOUBLE,
    temperature INT,
    kp_index INT,
    duration_minutes INT,
    color STRING,
    intensity INT,
    cloud_cover INT,
    is_photographed BOOLEAN
)
STORED AS PARQUET
LOCATION 's3://your-bucket-name/parquet/';

補足

  • STORED AS PARQUET だけでフォーマット指定完了
  • Parquet はスキーマを内蔵しているが、Athena では DDL でも定義が必要

athena_3

テーブル確認

SHOW TABLES IN northern_lights_db;

athena_4

これでテーブルの準備ができました!実際にクエリを実行し、比較してみます。


クエリ比較

同じクエリを CSV テーブルと Parquet テーブルに実行し、スキャン量を比較します。

クエリ1: 1列のみ使用(観測地集計)

観測地(location)ごとの観測回数をカウントし、多い順に並べるクエリです。
列としては1列のみを使用します。

CSV テーブル

-- CSV テーブル
SELECT location, COUNT(*) as count
FROM northern_lights_db.aurora_csv
GROUP BY location
ORDER BY count DESC;

athena_5

Parquet テーブル

-- Parquet テーブル
SELECT location, COUNT(*) as count
FROM northern_lights_db.aurora_parquet
GROUP BY location
ORDER BY count DESC;

athena_6

同じクエリ・同じ結果ですが、スキャン量は CSV が 687.66 KB に対し Parquet はわずか 3.83 KB。約 99% 削減されています。Parquet は location 列だけを読めば良いので、他の 13 列分のスキャンが不要になります。
クエリの実行時間も若干Parquetの方が速いですね。

クエリ2: 複数列取得 + フィルタ

日付・場所・気温・強度の 4 列を取得し、気温 -25℃ 未満でフィルタするクエリです。列が増えるとどうなるでしょう。

CSV テーブル

-- CSV テーブル
SELECT observation_date, location, temperature, intensity
FROM northern_lights_db.aurora_csv
WHERE temperature < -25;

athena_9

Parquet テーブル

-- Parquet テーブル
SELECT observation_date, location, temperature, intensity
FROM northern_lights_db.aurora_parquet
WHERE temperature < -25;

athena_10

4 列を使用するため、クエリ1(1列)よりスキャン量は増えますが、それでも CSV の 687.66 KB に対し Parquet は 26.69 KB と約 96% 削減されています。
どこも寒そうですね・・・、この中だとロヴァニエミに行ったことがありますが、当時は残念ながらオーロラ見られずでした。。。再チャレンジしたいですね。旅行会社にいた頃の噂によると、イエローナイフはわりと観測しやすいらしいです。(今はどうでしょう?)


クリーンアップ

検証が終わったら、作成したリソースを削除しておきましょう。

Athena テーブル・データベースの削除

DROP TABLE IF EXISTS northern_lights_db.aurora_csv;
DROP TABLE IF EXISTS northern_lights_db.aurora_parquet;
DROP DATABASE IF EXISTS northern_lights_db;

検証用 S3 バケットの削除

# バケット内のオブジェクトを削除
aws s3 rm s3://your-bucket-name/ --recursive

# バケット削除
aws s3 rb s3://your-bucket-name

クエリ結果の削除 (任意)

Athena のクエリ結果が保存されている S3 バケットの中身も、不要であれば削除してください。

まとめ

クエリ CSV スキャン量 Parquet スキャン量 削減率
クエリ1: 1列のみ使用(観測地集計) 687.66 KB 3.83 KB 99%
クエリ2: 複数列取得+フィルタ 687.66 KB 26.69 KB 96%

テストデータを用いて改めて、CSV は列数に関係なく全データを読むのに対し、Parquet は使用する列数に応じてスキャン量が変わることが分かりました。

補足 今回はデータ量が少なく Row Group が1つだけでしたが、大規模データで複数の Row Group がある場合は「述語プッシュダウン」の効果も期待できます。WHERE 句の条件に合わない Row Group を、メタデータ(min/max 統計)を見てスキップできるため、さらにスキャン量を削減できます。

Parquet を使うメリット

  • 列指向なので必要な列だけスキャン → クエリコスト/時間削減
  • 圧縮効率が高い → ストレージコスト削減
  • 型情報を持つ → スキーマ管理が楽

今後は さらに巨大なデータでの検証や、Apache Iceberg やそれをマネージドで扱える S3 Tables なども触ってみたいと思います。
最後までお読みいただきありがとうございました!


参考リンク

この記事をシェアする

FacebookHatena blogX

関連記事