テキストログレコードを圧縮するのに適した圧縮ツールを検証した
テキスト形式のログレコードが巨大な場合に、
Fluentdで転送する際のネットワーク帯域とか、S3に配置した際のサイズがでかいとかそういうのが気になりだしました。
どうにかレコードを小さく出来ないか考えたのですが
gzipなどの圧縮ツールをログ1行単位でやっていけばマシになるんじゃね?と思ったんですが、
速さと圧縮率を兼ねそなえたものがなんなのかわからなかったので、検証してみました。
ちなみにログ1行単位で圧縮したいのは、圧縮したログをFluetndを使って転送を行いたいからです。
(こういう方法あるよ!っていうのがあれば教えてください。。)
前提
選定ツール
名前 | 特徴 |
---|---|
gzip | 圧縮ツールのデファクトスタンダード |
xz | Linuxに標準で入っている圧縮ツール。圧縮率の点でgzipより優れていそう |
lz4 | 圧縮率に期待ができそうなので選定 |
zstd | lz4と同じ作者が制作した圧縮ツール |
brotli | Googleが作成した圧縮ツール |
単純な圧縮率の比較
速さとか気にせず、普通に圧縮した時の圧縮率を見てみたいと思います。
圧縮対象のファイルは以下のようなサイズです。
ファイル名 | Size(KB) |
---|---|
data.json | 86.49 |
結果
Name | Size(KB) | 圧縮率 |
---|---|---|
gzip | 21.20 | 24.51% |
xz | 18.31 | 21.17% |
lz4 | 26.22 | 30.32% |
zstd | 18.58 | 21.48% |
brotli | 22.37 | 24.56% |
単純な圧縮率を見てみると、「xz」が一番よかったです。
lz4は速さを優先しているようです。
この圧縮率を測ったコマンドは以下になります。
$ cat data.json | gzip -1 - | base64 -w 0 | wc -c $ cat data.json | xz -0 - | base64 -w 0 | wc -c $ cat data.json | lz4 -1 - | base64 -w 0 | wc -c $ cat data.json | zstd -1 - | base64 -w 0 | wc -c $ cat data.json | brotli -0 - | base64 -w 0 | wc -c
速度と圧縮率の比較
続いて速度を見ていきましょう。
上記の「単純な圧縮率の比較」で実行したコマンドを「1万回」実行した結果です。
(1) … 処理にかかった秒数
(2) … 1秒あたりの処理行数
(3) … 1行あたりにかかった時間
(4) … 出力サイズ(MB)
(5) … 圧縮率
Name | (1) | (2) | (3) | (4) | (5) |
---|---|---|---|---|---|
未圧縮 | 11 | 909 | 1.1ms | 844.63 | - |
gzip | 39 | 256 | 3.9ms | 207.07 | 24.51% |
xz | 69 | 145 | 6.9ms | 178.84 | 21.17% |
lz4 | 33 | 303 | 3.3ms | 256.12 | 30.32% |
zstd | 33 | 303 | 3.3ms | 181.47 | 21.48% |
brotli | 43 | 233 | 4.3ms | 218.55 | 25.87% |
速さを見てみると「lz4」と「zstd」が同値になりました。
圧縮率は「xz」が1番良かったですが、「zstd」もかなり競っていることがわかります。
速さと圧縮率を鑑みると、「zstd」の性能がかなり優れていることがわかりました。
検証に使ったスクリプトは以下になります。
#!/bin/bash # 第1引数に圧縮ツール名 # 第2引数に実行回数 function loop_compress() { echo $1 level=2 start_date=$(date '+%s') for i in $(seq 1 $2) do echo ${i} if [ "$1" == "none" ]; then cat data.json >> $1.log else cat data.json | $1 -${level} - | base64 -w 0 | xargs echo >> $1.log fi done end_date=$(date '+%s') echo -e "time:\t$(expr $end_date - $start_date)" echo -e "line:\t$(cat $1.log | wc -l)" echo -e "size:\t$(cat $1.log | wc -c)" rm -f $1.log } tool=$1 loop_cnt=$2 loop_compress ${tool} ${loop_cnt}
最後に
ログ1行ごとに圧縮する際は、「zstd」がよさそうだ というのがわかりました。
が、ログ1行単位で圧縮する場合、CPUのリソースをかなり食うことが確実なので
トレードオフなのかなと思います。
このzstdをログ1行ずつ実行する場合は、
rsyslogでコマンド実行すると導入しやすいと思います。
rsyslogでコマンド実行をしてログに手を加える
https://blog.haramishio.xyz/post/000008/
そもそもこういうアプローチの仕方はあってるんだろうか…
と不安になりますが、とりあえず検証してみました!
では!