開発中のシステムの動作検証をするとき、少なからずシードデータやダミーのデータが必要となることが多いと思います。
私が携わった業務の中でも「パフォーマンス検証用に十数カラム数千万レコードのデータを用意しなければ」ということがあり、その際Embulkが非常に役立ったため生地に残したいと思います。
Embulkの便利なところ
まずEmbulkとは、よく「Fluentdのバッチ版」と説明されますがその通りで、大量データの転送、加工(ETL)処理を行うことができます。
今回Embulkを扱うことの利点として以下が挙げられます。
- 様々なプラグインを利用できる
- インプット先、アウトプット先が多様
- 設定ファイルを書くだけで実行できる
多数あるプラグインを用いることで、ファイルやDB、クラウドサービスまで様々なデータソースを利用することが可能です。
また、「どこからデータを取ってきて、どこに送るのか」、「データをどう変換・加工するのか」をyamlファイルだけで定義できるのですぐに使い始めることができます。
ダミーデータ生成
今回はランダムデータを生成してくれるembulk-input-randomjというインプットプラグインを利用します。
また、出力はホストPC内のCSVファイルとします。
プラグインのインストール
※ Embulk自体の実行環境は整っている前提とします。
embulk-input-randomjプラグインをインストールします。
$ embulk gem install embulk-input-randomj
設定ファイルを作成
作成するダミーデータのフォーマットを設定するため、下記に示すconfig.yml
ファイルを作成します(ファイル名はすきに変更して大丈夫です)。
今回は気軽に試せるように生成するレコード数は少なく設定しています。
in:
type: randomj
rows: 100
threads: 4
primary_key: id
schema:
- {name: id, type: long}
- {name: name, type: string}
- {name: hash, type: string, length: 16}
- {name: hobby, type: string, null_rate: 1000}
- {name: price, type: long}
- {name: day, type: long, min_value: 1, max_value: 31}
- {name: average, type: double}
- {name: rate, type: double, min_value: -100, max_value: 100}
- {name: flag, type: boolean}
- {name: time, type: timestamp, format: '%Y-%m-%d %H:%M:%S'}
- {name: date, type: timestamp, format: '%Y-%m-%d', start_date: 20180101, end_date: 20201231}
out:
type: file
path_prefix: output_
file_ext: csv
formatter:
type: csv
header_line: true
charset: UTF-8
newline: CRLF
column_options:
time: {format: '%Y-%m-%d %H:%M:%S'}
date: {format: '%Y-%m-%d'}
randomjプラグインの設定について
randojプラグインの設定パラメータは次の通りです。
パラメータ名 | 説明 |
---|---|
rows | 生成するレコード数(スレッド単位) |
threads | スレッド数 |
primary_key | 主キーカラムの指定。指定したカラムは連番の値となる。 |
schema | カラムの設定 |
生成される総レコード数はrows * threads
の値となります。上記設定ファイルだと100 * 4 = 400
レコード分だけ生成されます。
実行
次のコマンドでEmbulkを実行します。
$ embulk run config.yml
(なお、embulk preview config.yml
でdry-runもできます。)
処理が終了すると、実行ディレクトリ下にoutput_*.csv
というファイルが複数生成されているのが確認できます。
ファイル数は並列実行数すなわちスレッド数とCPUコア数によって異なります。
$ ls -l
total 168
-rw-r--r-- 1 uro3 staff 934 2 16 15:01 config.yml
-rw-r--r-- 1 uro3 staff 16569 2 16 15:02 output_000.00.csv
-rw-r--r-- 1 uro3 staff 0 2 16 15:02 output_001.00.csv
-rw-r--r-- 1 uro3 staff 16639 2 16 15:02 output_002.00.csv
-rw-r--r-- 1 uro3 staff 0 2 16 15:02 output_003.00.csv
-rw-r--r-- 1 uro3 staff 16679 2 16 15:02 output_004.00.csv
-rw-r--r-- 1 uro3 staff 0 2 16 15:02 output_005.00.csv
-rw-r--r-- 1 uro3 staff 16643 2 16 15:02 output_006.00.csv
-rw-r--r-- 1 uro3 staff 0 2 16 15:02 output_007.00.csv
生成するレコード数が少なかったり、threadsパラメータの値を低くすると空のファイルが生成されることもあります(上記実行例でも空ファイルが生成されてしまってますね)。
ファイルの中身を確認してみます。
$ head -n 5 output_000.00.csv
1,dQYp5qfwcs4LEG8tpXXcL9nk8T5z8p0b,6jrmeMklNClY6z9D,uQDLLqDIzEGr04lZ1sk1HfSBTtPXrC4d,2627,1,9319.759903806967,-60.2255006491297,true,2020-05-03 06:09:20,2019-12-18
2,2zzN94AhX7dKEPj7iYNqpa1bFzis44RW,ymz2GQL7gGXLDHAq,6mlKrDC3I0vfDVGppW7W4YRj19WlPyHX,5883,13,4519.424678970713,16.01039975680497,true,2020-05-26 17:54:12,2018-08-21
3,6Lh4yZlDd6bNJMi6JWjcFafcufP5CsYw,H7egv5rdCgPgOxGo,GfOFodiZgcm115ja4EnhXMrdGGEwiB4r,1323,28,750.4551048594033,29.261926615295117,true,2020-03-30 20:29:09,2018-07-01
4,QcN0omRoNepFEmXycolrfmF5EetNTWR0,yjty1bTTb9ns0o9L,WF7lS4Ules7qjh7ECsx4zVpB28JwWmUg,8218,27,1103.5836005487843,30.128739554862438,false,2020-06-05 18:59:53,2020-01-13
5,rUQc8nXMLa2S6Q2ecXOUODx0B2SPOtLF,2VuDAxO4cu1i5F9u,DFQMU8DpiCnf6WTbRvPzu4qSY2C5SU5N,385,25,7301.194699442833,6.115086705050664,true,2020-03-05 03:01:28,2019-01-02
設定通りのデータができているようです。
生成された複数のファイルを一つのファイルにまとめたいときはcatコマンドを使いましょう。
$ cat output_*.csv >> result.csv
もう少しリッチなダミーデータ
基本的に文字列は完全にランダムな値なのでなんだか味気ないですよね。
そこで、他のfilterプラグインを活用して少しだけデータをリッチにしてみましょう。
今回は下記のプラグインを利用します。
embulk-filter-gsubは正規表現にマッチした文字列を任意の文字列に変換するプラグインです。
embulk-filter-typecastはカラムの型をキャストするプラグインです。embulk-filter-gsubで変換するにはstring型にする必要があるため、このプラグインを利用して型を変換します。
インストールコマンドは次の通りです。
$ embulk gem install embulk-filter-gsub embulk-filter-typecast
新しくconfig_2.yml
という名前で設定ファイルを作成します。
in:
type: randomj
rows: 100
threads: 4
primary_key: id
schema:
- {name: id, type: long}
- {name: filename, type: string, length: 8}
- {name: device, type: boolean}
- {name: weekday, type: long, min_value: 0, max_value: 6}
filters:
- type: typecast
columns:
- {name: "device", type: string}
- {name: "weekday", type: string}
- type: gsub
target_columns:
filename:
- {type: regexp_replace, pattern: (^.*$), to: $1.txt}
device:
- {type: regexp_replace, pattern: 'true', to: PC}
- {type: regexp_replace, pattern: 'false', to: SP}
weekday:
- {type: regexp_replace, pattern: '0', to: 日}
- {type: regexp_replace, pattern: '1', to: 月}
- {type: regexp_replace, pattern: '2', to: 火}
- {type: regexp_replace, pattern: '3', to: 水}
- {type: regexp_replace, pattern: '4', to: 木}
- {type: regexp_replace, pattern: '5', to: 金}
- {type: regexp_replace, pattern: '6', to: 土}
out:
type: file
path_prefix: output_
file_ext: csv
formatter:
type: csv
header_line: false
charset: UTF-8
newline: CRLF
filename
カラムにはサフィックスとして拡張子をつけるようにしました。
また、device
カラムにはPCかSPという文字列が、weekday
カラムには曜日を表す文字列がそれぞれ入ります。
それでは実行してみます。
$ embulk run config_2.yml
生成されたファイルの中身を確認してみます。
$ head -n 5 output_000.00.csv
1,udbQMu8j.txt,SP,木
2,PwdY5iJb.txt,SP,金
3,gtB79Y5W.txt,SP,金
4,DFSVa32v.txt,SP,月
5,jqSdcCGu.txt,PC,日
これである程度規則のあるデータも作成することができました。
まとめ
Embulkのembulk-input-randomjプラグインを使うことでダミーデータを簡単に生成することができます。
今回はcsvファイルとして作成しましたが、他にもDBやクラウドストレージにデータ挿入することもできたいへん便利です。
なお、生成するレコード数や出力先によってはある程度のマシンパワーが必要となるのでご注意ください。