S3に置いたCSVをAthena (+Glue) で検索したい

2022.04.08

広島の吉川です。

DynamoDBに蓄積したデータを分析用途で柔軟に検索したいという要件に対し、Athenaが良い武器になる気がしたので素振りしてみました。

「DynamoDBからS3にCSV出力し、そのCSVをAthenaで集計する」といったシナリオを念頭に、今回は「S3上のCSVファイルのスキーマを読み取ってGlueに作成し、Athenaでクエリをかける」という部分を検証してみました。

Athenaが初めてだったので色々ドキュメントやブログ見たり社内で聞いたりしつつ触ってみました。

環境

  • node 16.14.0
  • typescript 4.6.3
  • esbuild 0.14.32
  • esbuild-register 3.3.2
  • uuid 8.3.2
  • csv 6.0.5

テスト用のCSVファイルを作成

50万件レコードのCSVファイルを作成します。今回は以下のNode.js (TypeScript) コードで test.csv を生成しました。

import { stringify } from 'csv/sync'
import fs from 'fs'
import { v4 } from 'uuid'

const records = []
for (let i = 0; i < 500000; i++) {
  records.push({
    id: v4(),
    name: `ヨシカワ ヨシカワオ${i}`,
    gender: '男性',
    age: 29,
  })
}

fs.writeFileSync('test.csv', stringify(records, { header: true }))

S3バケットを2つ作成

  • Athenaクエリ保存用バケット
  • CSV用バケット

の2つを作成します。

今回は前者を「yoshikawa-athena-queries」、後者を「yoshikawa-test-2」という雑な命名でバケットを作成しました。S3バケット名は一意でないといけないので、各自任意に命名する必要があります。両方ともブロックパブリックアクセス設定を「パブリックアクセスをすべて ブロック」としておきます。

CSV用バケットにファイルアップロード

test.csv をCSV用バケットにアップロードします。

Glueを設定

Glueのコンソールを開き、データベース→データベースの追加を選択します。

今回は「yoshikawa_test」でデータベースを作成します。

続いて上のデータベースにテーブルを作成します。

テーブル→テーブルの追加→クローラを使用してテーブルを追加を選択します。

  • Crawler source type: Data stores
  • Repeat crawls of S3 data stores: Crawl all folders

のまま、「次へ」を押下します。

データストア「S3」を選択し、インクルードパスにCSV用バケットを指定します。

「別のデータストアの追加」は「いいえ」で進みます。

「IAMロールを作成する」を選択し、任意の名前をつけて進みます。

スケジュール「オンデマンドで実行」を選択し進みます。

クローラの出力は先程作成したデータベースを指定します。

内容を確認し「完了」します。

続いて「クローラ」から作成したクローラを選択し詳細画面に遷移します。「クローラの実行」を押下します。

クローラのステータスが「Starting」となりますので、数分程度待ちます。

すると「Ready」になるので、「テーブル」→作成したテーブルを選択し、詳細画面に遷移します。

このようにCSVファイルを読み取ってよしなにスキーマが生成されていることがわかります。

Athenaを設定

Amazon Athenaコンソール→「設定」→「管理」と選択します。「S3を参照」を押下します。

S3バケット一覧が出てくるので、今回用意したAthenaクエリ保存用バケットを指定します。

保存し、Athenaの設定を完了します。

Athenaでクエリ実行

Athenaコンソールでエディタを開きます。「データベース」で先程作成したデータベースを選択するとテーブルが表示されますので、その横のハンバーガーボタンを選択します。するとメニューが出てくるので「テーブルをプレビュー」を押下します。

すると自動でクエリが生成・表示され、実行もしてくれます。

CSVの内容をテーブル形式で取得できていますね。

続いて絞り込みをしてみます。クエリに WHERE "name" = 'ヨシカワ ヨシカワオ0' を追加して対象1件を絞り込み取得しようとしてみます。

意図通りのレコードを取得できました。

ちなみに、当初は WHERE name = "ヨシカワ ヨシカワオ0" みたいな書き方をしてハマったので、

  • カラム名は " (ダブルクオーテーション) で囲む。
  • 値が文字列の場合は ' (シングルクオーテーション) で囲む。

について留意しておくと良さそうです。

まとめ

Athenaを使おうとするとGlueも出てくるのは認識していなかったので、実際触って勉強になった部分でした。

今の所、

  • S3: ここにデータの実体がある
  • Glue: ここにデータベースとテーブルのスキーマがある
  • Athena: ここからGlueのスキーマを使ってS3上のデータにクエリをかける

という理解をしています。間違っていたらツッコミお願いします。

「柔軟な検索をしたい」という目的の実現にあたってはそもそもDynamoDBではなくRDSを使うという選択肢も有力だと思うのですが、やはり構成全体をサーバーレスに寄せてAWSにお任せする範囲をできるだけ増やすことのメリットも強いと思うので、要件によってはDynamoDB+S3+Athenaでもかなり戦えるんじゃないかと実感した次第です。

参考