この記事では、システム開発における性能試験や、導入済みシステムで発生した性能劣化の対策を行う方向けに、1000万件規模の大量データをすぐに作成する方法をご紹介します。
私も常駐先の業務で、「システムを導入した客先で性能劣化が発生している。すぐに現象の再現を行いたいので、検証環境に本番と同規模の1000万件のデータを用意してくれないか?」と依頼されたことが調査のきっかけでした。
目次
直積を用いたINSERT文
さすがにINSERT文を1000万回実行するわけにもいきません。
どうしたものかとネットで調べていたところ、「直積」という方法を使って大量のデータをINSERTする方法を見つけました。
直積とは「クロス結合(cross join)」とも呼ばれますが、要は2つのテーブル同士の全通りの組合せを取得する方法です。
詳しいことは実際にSQLを実行して試してみたほうが理解が早いかと思います。
大量データ作成の元となるテーブルを用意
まず最初に、直積を行うためのテーブルを用意します。
ここでは、以下のようにcross_join_tableを作成してみます。
idとvalueの2カラムしか無い、シンプルなテーブルです。
※以下、PostgreSQLでの実行サンプルとなります。
--cross_join_table作成
CREATE TABLE cross_join_table(
id SERIAL,
value integer NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
次に、このcross_join_tableに10件のデータを挿入します。
単純に、valueカラムに1~10の値をセットするのみです。
--cross_join_tableにデータを10件作成
INSERT INTO cross_join_table(value)
VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
cross_join_tableには、以下のようなデータが作成されています。
※本記事ではA5SQL Mk-2を使用してデータを閲覧
CROSS JOIN句を使ったSELECT文
ここで、作成したテーブルを使用して、以下のSQLを実行してみます。
同じテーブルを別名で宣言し、CROSS JOIN句で連結したものの件数をカウントします。
SELECT count(*)
FROM cross_join_table AS t1
CROSS JOIN cross_join_table AS t2
;
上記の結果は、100件が返されます。
もともとcross_join_tableにはデータが10件しかありませんが、10件ずつ2個のテーブルをCROSS JOIN句で結ぶことで、10 × 10 の総当たりを求めることになります。なので、結果は100件が返されます。
つまり、CROSS JOIN句でどんどんテーブルを連結することで、データが10倍ずつ増えていきます。ここまでくると、もうお分かりですね。
上記の要領で7つのテーブルを連結することで、1000万件が返されることになるのです。
ちなみに、CROSS JOIN句は省略が可能です。
なので、以下のように記述してもOKです。
SELECT
count(*)
FROM
cross_join_table AS t1 --10
,cross_join_table AS t2 --100
,cross_join_table AS t3 --1000
,cross_join_table AS t4 --10000
,cross_join_table AS t5 --100000
,cross_join_table AS t6 --1000000
,cross_join_table AS t7 --10000000
;
1000万件を追加するINSERT文の例
さて、直積を利用して1000万件のデータを抽出できることはわかりました。
あとは、実際に1000万件のデータを追加するにはどうしましょう?
察しの良い方は既にお分かりかと思いますが、INSERT文にそのまま組み込んでしまえば良いのです。
ここでは、例えば以下のテーブルが存在したとして、このテーブルへ1000万件のデータを追加する方法をご紹介します。
ここではまず、users_tableという名前のテーブルを生成しておきます。
CREATE TABLE users_table(
id SERIAL,
user_name character varying(50) NOT NULL,
created_at timestamp with time zone DEFAULT NOW(),
updated_at timestamp with time zone DEFAULT NOW(),
PRIMARY KEY (id)
);
idにはデータ追加のたびに連番がセットされて、created_atとupdated_atにはデータ追加時の現在日時を自動でセットされます。
(本来ならcreated_atは追加時のみ、updated_atは更新時に上書きなんですが)
で、このテーブルへのINSERT文を以下のように記述し実行してみてください。
ここでは、user_nameカラムに”user_XX”(XXの部分にはROW_NUMBER()を付与)という一意の名前をセットするSQL文としています。
で、こいつを実行することで、1000万件のデータがセットされます。
INSERT INTO users_table(user_name)
SELECT
'user_' || ROW_NUMBER() OVER ()
FROM
cross_join_table AS t1, --10
cross_join_table AS t2, --100
cross_join_table AS t3, --1000
cross_join_table AS t4, --10000
cross_join_table AS t5, --100000
cross_join_table AS t6, --1000000
cross_join_table AS t7 --10000000
;
1000万件のデータも数分で作成できた
ただ、さすがにいきなり1000万件のデータを作るのも危険かと思います。
どれだけ時間がかかるか分からなかったので、とりあえずCROSS JOINさせるテーブルの数を限定して、10万件までのINSERTを試してみましたが、いずれも数秒で完了しました。
これなら、1000万件のINSERTを実行したとしても、せいぜい数分~十数分で終わるだろう、と見込みを持ったところで、いよいよ1000万件データのINSERTを実行!
結果、私のローカルPCでは、1000万件のデータ追加するのに約10分ほどかかりました。SQLのみで作業が完結して、なおかつ10分程度で1000万件のデータが作れてしまうので便利です。
もちろん他にも方法はあるかと思いますが、私は実際に業務でやってみて、この方法が一番お手軽だと感じました。
急ぎで大量のデータをテーブルに追加する必要がある方は、ぜひ本記事の内容を参考にしてみてください。
ただし、いきなり大量データのINSERTを実行したりせず、まずは少量のデータの追加でどのくらい時間がかかるかを計測してから、じょじょに投入するデータ量を増やしてみることをオススメします。
コメント
危うくinsert文を何回も投げるところでした。この記事のおかげで助かりました。
ありがとうございます。