お金をかけずにサーバーの勉強をしよう

DB用の大きなダミーデータを作る

2024年4月14日

メニューへ戻る

億千万!億千万!

システムの新規立ち上げの際、要件定義が終わって朧気ながらデータベースの論理設計が見えてきますが、その頃にはもうデータベースサーバーのサイジングをしないといけない事がよくあります。

今の時点で分かるか!(クソッ)

というのがサーバーエンジニアの本音ですが、仕方ありません。

そんな時に、論理値でディスクのサイズなんかを算出するよりも、検証環境で実際にデータをブチ込んだ方が速くて正確なんですよね。

ダミーデータであろうが実際にブツがあるなら、バックアップにかかるディスク量とか実行時間とかもかなり正確に見積もれますから、現場じゃよくダミーデータを作ったものです。

アプリケーションのテストで使う少量(だけど内容が大事)のダミーデータなら Excelでサックリと作ってしまうのですが、ここで言ってるのはそういうのではなくて、データがフルに載った時の動きを見るため、億件単位の物量があるものになります。

ですので、Excelじゃ無理。


有名所の各RDB製品には、何かしらテキストファイルからデータを取り込む「ローダー」と呼ばれるユーティリティツールが用意されています。

Oracleでは「SQL Loader」で、SQL Serverなら「bcp.exe」ですね。

こういうツールが取り込めるフォーマットに沿ったテキストファイルを、スクリプトやプログラムによって自動生成するちゅ〜ことになります。

今回は実際に DBへの取り込みはしませんが、Linux上でこういった類の大量のダミーデータを作るパターンをやってみたいなと。

プログラマなら楽勝ですけど、サーバーエンジニアはそうもいかぬ…。
という訳で、レッツ・トライであります。


作るダミーデータの例として、以下の構成の DBテーブルにブチ込む CSV形式のファイルを作ることにします。
文字コードは UTF-8です。

ユーザーID
(P-key)
ユーザー名備考
9桁の数字100文字くらいまで200文字くらいまで

1行分だけ具体的に書くと、こんな感じ。

"000000001","名前-000000001","適当な備考"

こういうのを 1億2,000万件作ります。

環境は VMware Workstation Playerの仮想マシンにインストールした Lubuntu 22.04.4を使います。

CPUは Intel Core i5 4570・ストレージには SSDを使っています。


1.Pythonで作る

Ubuntu 22.04にはデフォルトで Python 3が入っていますので、ループを使ってお手軽に作成することができます。

とりあえず作ってみたのがこれ。
[makedata.py]

limit = 120000000

try:
    file = open("data_py.csv", 'w')

    for i in range( 1, limit // 2 +1):
        file.write( f'"{i:09}","名前-{i:09}","瞳は億千万の胸騒ぎ"\n' )
        j = limit - i + 1
        file.write( f'"{j:09}","名前-{j:09}","命のときめきエキゾチック・ジャパン"\n' )

except Exception as e:
    print(e)

finally:
    file.close()

データがプライマリキーのインデックス通りにキレイに並んでしまうのは実態とかけ離れてしまうので、for文にちょっとだけ細工して、ユーザーIDが小さなものと大きなものを交互に並べるようにしました。
(焼け石に水な気もしますが、何もしないより良いでしょう。)

実行時に dateコマンドで前後を挟んで、実行時間がどれだけかかったか分かるようにしました。

subro@Lubuntu2204:/data$ date; python3 makedata.py; date
2024年  4月 14日 日曜日 05:23:13 JST
2024年  4月 14日 日曜日 05:25:44 JST

2分31秒かかりました。

できあがった CSVファイルは 8.76GBもありました。 こんなショボいカラム数のデータでも、1億件を超えると大きくなります。

subro@Lubuntu2204:/data$ ls -l
total 8554712
-rw-rw-r-- 1 subro subro 8760000000  4月 14 05:25 data_py.csv
drwx------ 2 root  root       16384  4月 12 20:37 lost+found
-rw-rw-r-- 1 subro subro        379  4月 14 05:22 makedata.py

[data_py.csv]ファイルの頭とケツをそれぞれ headコマンドと tailコマンドで見てみます。
ファイルが大き過ぎて catコマンドやテキストエディタでは見ていられません。

subro@Lubuntu2204:/data$ head data_py.csv
"000000001","名前-000000001","瞳は億千万の胸騒ぎ"
"120000000","名前-120000000","命のときめきエキゾチック・ジャパン"
"000000002","名前-000000002","瞳は億千万の胸騒ぎ"
"119999999","名前-119999999","命のときめきエキゾチック・ジャパン"
"000000003","名前-000000003","瞳は億千万の胸騒ぎ"
"119999998","名前-119999998","命のときめきエキゾチック・ジャパン"
"000000004","名前-000000004","瞳は億千万の胸騒ぎ"
"119999997","名前-119999997","命のときめきエキゾチック・ジャパン"
"000000005","名前-000000005","瞳は億千万の胸騒ぎ"
"119999996","名前-119999996","命のときめきエキゾチック・ジャパン"

subro@Lubuntu2204:/data$ tail data_py.csv
"059999996","名前-059999996","瞳は億千万の胸騒ぎ"
"060000005","名前-060000005","命のときめきエキゾチック・ジャパン"
"059999997","名前-059999997","瞳は億千万の胸騒ぎ"
"060000004","名前-060000004","命のときめきエキゾチック・ジャパン"
"059999998","名前-059999998","瞳は億千万の胸騒ぎ"
"060000003","名前-060000003","命のときめきエキゾチック・ジャパン"
"059999999","名前-059999999","瞳は億千万の胸騒ぎ"
"060000002","名前-060000002","命のときめきエキゾチック・ジャパン"
"060000000","名前-060000000","瞳は億千万の胸騒ぎ"
"060000001","名前-060000001","命のときめきエキゾチック・ジャパン"

想定通りの内容になっていて良かったです。

データサイズはさておき、1億2千万件のデータを作るのに 2分31秒かかったわけです。

日本だからこれで済みましたが、大陸のデータだと 10倍の 25分くらいかかってしまいます。

ちょっとデータを変えて…とやる度に 25分だと困りますね…。


2.C言語で作る

何かもっと速く作る方法が無いか?ということで、C言語でも作ってみます。

C言語のコンパイラである gccをインストールします。

subro@Lubuntu2204:/data$ sudo apt install -y gcc
〜〜〜 得に問題なく入るので省略 〜〜〜

Pythonスクリプトと似たようなコードを書きました。
[makedata.c]

#include <stdio.h>

int main(void)
{
    FILE *file;
    long total, i, j;

    total = 120000000;
    file = fopen("data_c.csv", "w");

    for( i = 1 ; i <= total / 2 ; i++ ) {
        fprintf(file, "\"%09ld\",\"名前-%09ld\",\"瞳は億千万の胸騒ぎ\"\n", i, i);
        j = total + 1 - i;
        fprintf(file, "\"%09ld\",\"名前-%09ld\",\"命のときめきエキゾチック・ジャパン\"\n", j, j);
    }

    fclose(file);
    return 0;
}

コンパイルします。

subro@Lubuntu2204:/data$ gcc -o makedata makedata.c

即時に完了し、実行ファイル [makedata] ができました。

subro@Lubuntu2204:/data$ ls -l
total 8554732
-rw-rw-r-- 1 subro subro 8760000000  4月 14 05:25 data_py.csv
drwx------ 2 root  root       16384  4月 12 20:37 lost+found
-rwxrwxr-x 1 subro subro      16048  4月 14 05:48 makedata
-rw-rw-r-- 1 subro subro        445  4月 14 05:42 makedata.c
-rw-rw-r-- 1 subro subro        379  4月 14 05:22 makedata.py

こちらも実行時に dateコマンドで前後を挟みます。

subro@Lubuntu2204:/data$ date; ./makedata; date
2024年  4月 14日 日曜日 05:51:49 JST
2024年  4月 14日 日曜日 05:52:39 JST

50秒でした。

[data_c.csv]ファイルが、Pythonで作ったデータと同じサイズでできています。

subro@Lubuntu2204:/data$ ls -l
total 17109424
-rw-rw-r-- 1 subro subro 8760000000  4月 14 05:52 data_c.csv
-rw-rw-r-- 1 subro subro 8760000000  4月 14 05:25 data_py.csv
drwx------ 2 root  root       16384  4月 12 20:37 lost+found
-rwxrwxr-x 1 subro subro      16048  4月 14 05:48 makedata
-rw-rw-r-- 1 subro subro        445  4月 14 05:42 makedata.c
-rw-rw-r-- 1 subro subro        379  4月 14 05:22 makedata.py

内容が同じなら中身は OKなので、diffコマンドで検証したのですが、ファイルが大きすぎて作業バッファが足りないのでしょう。
ファイルが大きいとこういう小回りが効かなくなってきます。

subro@Lubuntu2204:/data$ diff data_c.csv data_py.csv
Killed

head・tailコマンドを使います。

subro@Lubuntu2204:/data$ head data_c.csv
"000000001","名前-000000001","瞳は億千万の胸騒ぎ"
"120000000","名前-120000000","命のときめきエキゾチック・ジャパン"
"000000002","名前-000000002","瞳は億千万の胸騒ぎ"
"119999999","名前-119999999","命のときめきエキゾチック・ジャパン"
"000000003","名前-000000003","瞳は億千万の胸騒ぎ"
"119999998","名前-119999998","命のときめきエキゾチック・ジャパン"
"000000004","名前-000000004","瞳は億千万の胸騒ぎ"
"119999997","名前-119999997","命のときめきエキゾチック・ジャパン"
"000000005","名前-000000005","瞳は億千万の胸騒ぎ"
"119999996","名前-119999996","命のときめきエキゾチック・ジャパン"

subro@Lubuntu2204:/data$ tail data_c.csv
"059999996","名前-059999996","瞳は億千万の胸騒ぎ"
"060000005","名前-060000005","命のときめきエキゾチック・ジャパン"
"059999997","名前-059999997","瞳は億千万の胸騒ぎ"
"060000004","名前-060000004","命のときめきエキゾチック・ジャパン"
"059999998","名前-059999998","瞳は億千万の胸騒ぎ"
"060000003","名前-060000003","命のときめきエキゾチック・ジャパン"
"059999999","名前-059999999","瞳は億千万の胸騒ぎ"
"060000002","名前-060000002","命のときめきエキゾチック・ジャパン"
"060000000","名前-060000000","瞳は億千万の胸騒ぎ"
"060000001","名前-060000001","命のときめきエキゾチック・ジャパン"

内容は良さげです。

同じデータを作るにしても、随分と時間が短くなりました。
これなら大陸でも 9分くらいで作れそうです。


==========
ロジックの組み方によって Pythonでももっと速いのを作れるかも知れませんが、目的はテスト用のダミーデータ作成ですから、ロジックの突き詰めに時間をかける訳にはいきません。

サックリとプログラムを書いてサックリと作る、その結果「やっぱり Cは速ぇ〜わ」という結果になりました。

最近では Rustでも C言語なみに速いのを作れると思います。

こういったとにかく量が多いデータ、Excelじゃ作りきれないものを作るシチュエーションは DBをやっていれば出会うと思うんですよね。

その時に Pythonのようなインタプリタのスクリプトだけではなく、C言語でも書けるようになっておくと良いですねってお話でした。

作業用のマシンが遅い程に、プログラムの処理効率が大きく処理時間に反映するので、以前ラズベリーパイでデータを作った時は C言語じゃなきゃやってられなかった覚えがあります。


さすが歴史のある C言語、日本語の本にも事欠きません。