14
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Aurora MySQL から S3 に直接 CSV 出力する【SELECT INTO OUTFILE S3】

Posted at

アプリで大量レコードのCSV出力は重すぎる

僕は常々思ってるんですが、
CSV出力って面倒じゃないですか
本来アプリがやるには重すぎる仕事なんじゃないかと思うんです

大量レコードを出力する際、

  • DBから1万件ずつデータ引っ張ってきて
  • CSVファイルに書き込んで
  • 数GBのCSVをS3にアップロードして
  • メモリにもCPU負荷にも気を遣って

など、いろいろ配慮する必要がある

こうした重たいCSV出力を、
アプリを介さずDB側で完結させることができます

そう、SELECT INTO OUTFILE S3 ならね

SELECT INTO OUTFILE S3ってなに?

Aurora MySQLからクエリ結果を直接S3に書き出すための構文
通常の SELECT 文の後ろに INTO OUTFILE S3 という魔法の言葉をつけることで実行できます

例: usersテーブルを指定した s3 uri に書き出す
SELECT *
FROM users
INTO OUTFILE S3 's3://mybucket/exports/users_20260114'
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n';

どんな時に使うと嬉しいの?

代表的なユースケースは以下あたりです

システム連携・分析用途

  • 分析基盤へのデータ連携
    Aurora のデータを S3 に吐き出し、Athena や Redshift などで集計・分析する

  • 大規模データのスナップショット取得
    本番DBのある時点の状態を、アプリを介さず退避する

アプリ負荷を切り離したいケース

  • 重いSELECTの逃がし先
    全件スキャンや長時間クエリを、アプリ処理から切り離す

今回、使用したケース

後者のアプリ側の負荷軽減で、ユーザ向けのCSVダウンロードのために使いました
ただ、本来の使用想定は分析用のデータ連携などのシステム連携であって、
「日本で人間がExcelでデータを見たい」といったケースだと痒いところに手は届きません ※後述

使い方

下記のユーザーガイドにすべてまとまっています

要点をまとめると、

  • Aurora MySQL に S3 アクセス許可
  • Aurora MySQL のユーザに権限付与
  • 構文を使って実行

構文は以下です
ハマった点として、ちゃんとオプションを順番通りに指定しないと構文エラーになります

SELECT
    [ALL | DISTINCT | DISTINCTROW ]
        [HIGH_PRIORITY]
        [STRAIGHT_JOIN]
        [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
        [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
    select_expr [, select_expr ...]
    [FROM table_references
        [PARTITION partition_list]
    [WHERE where_condition]
    [GROUP BY {col_name | expr | position}
        [ASC | DESC], ... [WITH ROLLUP]]
    [HAVING where_condition]
    [ORDER BY {col_name | expr | position}
         [ASC | DESC], ...]
    [LIMIT {[offset,] row_count | row_count OFFSET offset}]
INTO OUTFILE S3 's3_uri'
[CHARACTER SET charset_name]
    [export_options]
    [MANIFEST {ON | OFF}]
    [OVERWRITE {ON | OFF}]
    [ENCRYPTION {ON | OFF | SSE_S3 | SSE_KMS ['cmk_id']}]

export_options:
    [FORMAT {CSV|TEXT} [HEADER]]
    [{FIELDS | COLUMNS}
        [TERMINATED BY 'string']
        [[OPTIONALLY] ENCLOSED BY 'char']
        [ESCAPED BY 'char']
    ]
    [LINES
        [STARTING BY 'string']
        [TERMINATED BY 'string']
]

注意点

BOMはつけられない

BOMは「Byte Order Mark」の略で「このファイルの文字コードはコレです」を表すテキストの先頭に付与される数バイトのデータのこと

UTF-8 の CSV ファイルをダブルクリックして Excel で開くと文字化けする可能性あり
→デフォルトで Shift_JIS で開こうとするため
これって日本だけの話っぽいですね、不思議な国ですね

圧縮できない

出力した数GBのCSVをそのままユーザがダウンロードする、といったケースだとダウンロードに時間かかりすぎます
そういう使い方をするのであれば、別途 圧縮処理する必要ありです

ファイルが分割される、ファイル名にpart_NNNNNがつく

約6GBごとにファイルが分割される仕様
ファイル名の末尾に.part_00000が付き、分割されるごとに採番されていく
ファイルが1つしか出力されない前提で実装すると事故ります
オプションでマニフェストファイルを出力するようにし、出力結果を確認して後処理を書くのが吉

DB負荷

アプリの負荷をDBが担うことになるため、DB負荷にも配慮する

さいごに

「CSV 出力はアプリでごちゃごちゃやる」だけの世界線で生きてきましたが、今回新しい世界を知りました
アプリ、DBのどちらに責務を置くか、みたいな視点もあるんですね

14
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?