MAGAZINE

ルーターマガジン

インフラ/運用

Docker環境でのPostgreSQL CSVインポート速度検証

2025.07.04
Pocket

1. 概要・目的

Docker上のPostgreSQLに対してCSVインポートを複数パターンで実施し、「どの方法が一番速いのか?」を検証してみました。特に、共有ボリュームの内外でパス指定した場合のパフォーマンス差や、STDIN経由とファイルパス指定の違いがどれほど効くのかを明らかにします。

2. テストデータについて

今回の検証では、rooter.jpのMySQLとPostgreSQLの速度比較記事を参考に、国税庁「法人番号公表サイト」基本3情報の全件データ(約536万件)を使用。 CSVファイル名は 00_zenkoku_all_20250530.csv です。

3. 実験環境

  • OS: Ubuntu 22.04
  • CPU: 4 vCPU x 2.592GHz
  • メモリ: 16GB
  • PostgreSQL: 17(Docker公式イメージ)

4. 構築手順

4.1 共有ディレクトリ作成&コンテナ起動

mkdir pgdata
sudo docker pull postgres:17
sudo docker run --name my-postgres17 \
  -e POSTGRES_HOST_AUTH_METHOD=trust \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_DB=postgres \
  -v $(pwd)/pgdata:/var/lib/postgresql/data \
  -p 5432:5432 \
  -d postgres:17
  • 共有ボリューム
    • ホスト: $(pwd)/pgdata
    • コンテナ: /var/lib/postgresql/data

4.2 テーブル作成

CREATE TABLE public.enterprises (
    sequencenumber int4 NOT NULL,
    corporatenumber bpchar(13) NOT NULL,
    process bpchar(2) NOT NULL,
    correct bpchar(1) NOT NULL,
    updatedate date NULL,
    changedate date NULL,
    "name" varchar(150) NULL,
    nameimageid bpchar(8) NULL,
    kind bpchar(3) NOT NULL,
    prefecturename varchar(10) NULL,
    cityname varchar(20) NULL,
    streetnumber varchar(300) NULL,
    addressimageid bpchar(8) NULL,
    prefecturecode bpchar(2) NULL,
    citycode bpchar(3) NULL,
    postcode bpchar(7) NULL,
    addressoutside varchar(300) NULL,
    addressoutsideimageid bpchar(8) NULL,
    closedate date NULL,
    closecause bpchar(2) NULL,
    successorcorporatenumber bpchar(13) NULL,
    changecause varchar(500) NULL,
    assignmentdate date NULL,
    latest bpchar(1) NOT NULL,
    enname varchar(300) NULL,
    enprefecturename varchar(9) NULL,
    encityname varchar(600) NULL,
    enaddressoutside varchar(600) NULL,
    furigana varchar(500) NULL,
    hihyoji bpchar(1) NULL,
    CONSTRAINT enterprises_pkey PRIMARY KEY (sequencenumber)
);

5. CSVファイルの配置パターン

区分 ホスト側パス コンテナ側パス
共有ボリューム外 ./00_zenkoku_all_20250530.csv なし
共有ボリューム内 ./pgdata/00_zenkoku_all_20250530.csv /var/lib/postgresql/data/00_zenkoku_all_20250530.csv

6. インポート手法とコマンド集

6.1 共有ボリューム外(ホスト)→ docker execでコンテナ内psql(STDIN)

time sudo cat ./00_zenkoku_all_20250530.csv | \
sudo docker exec -i my-postgres17 bash -c \
"psql -h 127.0.0.1 -U postgres -d postgres -c 'copy public.enterprises from STDIN with csv;'"

6.2 共有ボリューム外(ホスト)→ ホスト側psql(STDIN)

time sudo cat ./00_zenkoku_all_20250530.csv | \
psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from STDIN with csv;"

6.3 共有ボリューム内(ホスト)→ docker execでコンテナ内psql(STDIN)

time sudo cat ./pgdata/00_zenkoku_all_20250530.csv | \
sudo docker exec -i my-postgres17 bash -c \
"psql -h 127.0.0.1 -U postgres -d postgres -c 'copy public.enterprises from STDIN with csv;'"

6.4 共有ボリューム内(ホスト)→ ホスト側psql(STDIN)

time sudo cat ./pgdata/00_zenkoku_all_20250530.csv | \
psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from STDIN with csv;"

6.5 共有ボリューム内(コンテナ)→ ホスト側psql(ファイルパス指定)

time psql -h 127.0.0.1 -U postgres -d postgres -c \
"copy public.enterprises from '/var/lib/postgresql/data/00_zenkoku_all_20250530.csv' with csv;"

6.6 共有ボリューム内(ホスト)→ ホスト側psql(ファイルパス指定)

time psql -h 127.0.0.1 -U postgres -d postgres -c \
"copy public.enterprises from './00_zenkoku_all_20250530.csv' with csv;"

6.7 共有ボリューム内(コンテナ)→ docker execでコンテナ内psql(ファイルパス指定)

time sudo docker exec -i my-postgres17 bash -c \
"psql -h 127.0.0.1 -U postgres -d postgres -c \"copy public.enterprises from '/var/lib/postgresql/data/00_zenkoku_all_20250530.csv' with csv;\""

7. ベンチマーク結果まとめ

手法 CSVパス psql実行環境 COPY方法 実行時間(秒) 平均値(秒)
6.1 ./00_zenkoku_all_20250530.csv docker 内 STDIN 26.716, 31.524, 27.293 28.51
6.2 ./00_zenkoku_all_20250530.csv ホスト STDIN 29.798, 32.198, 32.257 31.42
6.3 ./pgdata/00_zenkoku_all_20250530.csv docker 内 STDIN 32.017, 29.803, 30.836 30.89
6.4 ./pgdata/00_zenkoku_all_20250530.csv ホスト STDIN 25.227, 29.092, 29.860 28.06
6.5 /var/lib/postgresql/data/00_zenkoku_all_20250530.csv ホスト ファイルパス 25.411, 25.773, 29.074 26.75
6.6 ./00_zenkoku_all_20250530.csv ホスト ファイルパス 26.874, 27.744, 24.838 26.49
6.7 /var/lib/postgresql/data/00_zenkoku_all_20250530.csv docker 内 ファイルパス 27.713, 25.312, 28.918 27.31

8. 考察

  • ちょっとの違いですが、ファイルパス指定(COPY ... FROM 'パス')の方が、STDIN経由よりも平均して速い結果となりました。
  • 共有ボリュームの内外でのパス指定による極端な性能差は見られませんでした。
  • docker exec経由とホストから直接psqlを実行する場合でも、体感できるほどの差はありませんでした。

9. 結論

  • 「ファイルパス指定でのCOPY」がおすすめ

最後に使ったテストスクリプトを載せますので、みなさんの環境でもテストしてみてください。

echo 'docker共有ボリューム外においたCSVをdocker外で読み込んで、docker exec コマンドを通して、docker内のpsqlに、標準入力で渡す'
time sudo cat ./00_zenkoku_all_20250530.csv |sudo docker exec -i my-postgres17 bash -c "psql -h 127.0.0.1 -U postgres -d postgres -c 'copy public.enterprises from STDIN with csv;'"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time sudo cat ./00_zenkoku_all_20250530.csv |sudo docker exec -i my-postgres17 bash -c "psql -h 127.0.0.1 -U postgres -d postgres -c 'copy public.enterprises from STDIN with csv;'"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time sudo cat ./00_zenkoku_all_20250530.csv |sudo docker exec -i my-postgres17 bash -c "psql -h 127.0.0.1 -U postgres -d postgres -c 'copy public.enterprises from STDIN with csv;'"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60

echo "docker共有ボリューム外においたCSVをdocker外で読み込んで、docker外のpsqlに標準入力で渡す"
time sudo cat './00_zenkoku_all_20250530.csv' | psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from STDIN with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time sudo cat './00_zenkoku_all_20250530.csv' | psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from STDIN with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time sudo cat './00_zenkoku_all_20250530.csv' | psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from STDIN with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60

echo 'docker共有ボリュームにおいたCSVをdocker外で読み込んで、docker exec コマンドを通して、docker内のpsqlに、標準入力で渡す'
time sudo cat ./pgdata/00_zenkoku_all_20250530.csv |sudo docker exec -i my-postgres17 bash -c "psql -h 127.0.0.1 -U postgres -d postgres -c 'copy public.enterprises from STDIN with csv;'"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time sudo cat ./pgdata/00_zenkoku_all_20250530.csv |sudo docker exec -i my-postgres17 bash -c "psql -h 127.0.0.1 -U postgres -d postgres -c 'copy public.enterprises from STDIN with csv;'"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time sudo cat ./pgdata/00_zenkoku_all_20250530.csv |sudo docker exec -i my-postgres17 bash -c "psql -h 127.0.0.1 -U postgres -d postgres -c 'copy public.enterprises from STDIN with csv;'"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60

echo "docker共有ボリュームにおいたCSVをdocker外で読み込んで、docker外のpsqlに標準入力で渡す"
time sudo cat './pgdata/00_zenkoku_all_20250530.csv' | psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from STDIN with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time sudo cat './pgdata/00_zenkoku_all_20250530.csv' | psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from STDIN with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time sudo cat './pgdata/00_zenkoku_all_20250530.csv' | psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from STDIN with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60

echo "docker共有ボリュームにおいたCSVをdockerの中のパス指定でdocker外のpsqlで読み込み"
time psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from '/var/lib/postgresql/data/00_zenkoku_all_20250530.csv' with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from '/var/lib/postgresql/data/00_zenkoku_all_20250530.csv' with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from '/var/lib/postgresql/data/00_zenkoku_all_20250530.csv' with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60

echo "docker共有ボリュームにおいたCSVをdockerの外のパス指定でdocker外のpsqlで読み込み"
time psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from './00_zenkoku_all_20250530.csv' with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from './00_zenkoku_all_20250530.csv' with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time psql -h 127.0.0.1 -U postgres -d postgres -c "copy public.enterprises from './00_zenkoku_all_20250530.csv' with csv;"
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60

echo "docker共有ボリュームにおいたCSVをdockerの中のパス指定でdocker内のpsqlで読み込み"
time sudo docker exec -i my-postgres17 bash -c "psql -h 127.0.0.1 -U postgres -d postgres -c \"copy public.enterprises from '/var/lib/postgresql/data/00_zenkoku_all_20250530.csv' with csv;\""
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time sudo docker exec -i my-postgres17 bash -c "psql -h 127.0.0.1 -U postgres -d postgres -c \"copy public.enterprises from '/var/lib/postgresql/data/00_zenkoku_all_20250530.csv' with csv;\""
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
time sudo docker exec -i my-postgres17 bash -c "psql -h 127.0.0.1 -U postgres -d postgres -c \"copy public.enterprises from '/var/lib/postgresql/data/00_zenkoku_all_20250530.csv' with csv;\""
psql -h 127.0.0.1 -U postgres -d postgres -c "truncate table public.enterprises;"
sleep 60
Pocket

CONTACT

お問い合わせ・ご依頼はこちらから