ElasticsearchでXFSとBtrfsを容量削減目的で比較してみた

Elasticsearchでなるべく長期間データ保存をしておきたいけど、データ容量が結構大きい。。。
ということで、圧縮できないかXFSとBTrfsを試してみました。

目的

  • データ圧縮してストレージ容量の削減をしたい

観点

  • どの程度容量が圧縮されるのか
  • 検索速度はどの程度違うのか
  • 負荷はどのレイヤがどの程度変わるのか

注意

今回紹介するBtrfsの圧縮機能の状態はまだmostly OKなので利用時はデータの破損等自己責任でお願いします。
備考には(needs verification and source) auto-repair and compression may crashとの記載あり。
Status - btrfs Wiki

XFSとBtrfsについて

各ファイルシステムの紹介は細かく記載してくださっているサイトがあるので、
ご紹介程度にとどめておきます。

試したファイルシステムの設定種類

サーバ名 OS Instance Type ファイルシステム ファイルシステムオプション(fstab)
ec2-xfs-default CentOS Linux release 7.2.1511 t2.medium xfs defaults
ec2-btrfs-default CentOS Linux release 7.2.1511 t2.medium btrfs defaults
ec2-btrfs-force-lzo CentOS Linux release 7.2.1511 t2.medium btrfs noatime,autodefrag,compress-force=lzo,space_cache
ec2-btrfs-force-zlib CentOS Linux release 7.2.1511 t2.medium btrfs noatime,autodefrag,compress-force=zlib,space_cache

試した環境イメージ

f:id:st1t:20170408162353p:plain

投入したデータについて

ec2-dsrc01,02からmetricbeatを使って毎秒以下の通りデータを投入。

$ sudo cat /etc/metricbeat/metricbeat.yml
metricbeat.modules:

#------------------------------- System Module -------------------------------
- module: system
  metricsets:
    # CPU stats
    - cpu

    # System Load stats
    - load

    # Per CPU core stats
    - core

    # IO stats
    - diskio

    # Per filesystem stats
    - filesystem

    # File system summary stats
    - fsstat

    # Memory stats
    - memory

    # Network stats
    - network

    # Per process stats
    - process

    # Sockets (linux only)
    - socket
  enabled: true
  period: 1s
  processes: ['.*']

ディスク容量の推移

f:id:st1t:20170408164453p:plain
ec2-xfs-defaultとec2-btrfs-defaultではあまり大きな容量の差は見られませんでした。
しかし、ec2-btrfs-force-lzoはec2-btrfs-defaultと比較して3割強、
ec2-btrfs-force-zlibに至っては6割のデータ容量が削減されていることが確認できました。

CPU使用率(USER)の推移

f:id:st1t:20170412200544p:plain
予想に反してbtrfs-zlibの使用率が低い結果になりました。
※全台たまに使用率が跳ねているのは恐らくSegment Mergeが起きていたのではないかと思います。
今回は検証の範囲外のため行っていませんが、もし本当にSegment Mergeであるか確認するなら、
_cluster/settingsのindices.store.throttle.max_bytes_per_sec等を修正すると影響があるかもしれません。

www.elastic.co
blog.mikemccandless.com

簡単なクエリを投げ続けてみた

以下のような直近1日のCPU使用率を取得するクエリを投げてみました。
なお、get-cpu.shを叩く前に全台Elasticsearchを再起動してメモリキャッシュをクリアさせています。

get-cpu.sh

#!/bin/bash

LOG=/tmp/get-average-cpu-24hour-`date +%Y%m%d-%H%M%S`.log

# Warmup
date >> $LOG
echo "start warmup." >> $LOG
for i in {1..500}
do
  (time curl -s -XGET {{ ansible_default_ipv4.address }}:9200/metricbeat-*/_search -d @get-average-cpu-24hour.json | jq .) > /dev/null 2>&1
done
date >> $LOG
echo "stop warmup." >> $LOG

date >> $LOG
echo "sleep 1m." >> $LOG
sleep 60
date >> $LOG

# exe
date >> $LOG
echo "start benchmark." >> $LOG
for i in {1..1000}
do
  (time curl -s -XGET {{ ansible_default_ipv4.address }}:9200/metricbeat-*/_search -d @get-average-cpu-24hour.json | jq .) 2>&1 | grep ^real >> $LOG
done
date >> $LOG
echo "stop benchmark." >> $LOG

get-average-cpu-24hour.json

{ 
  "size": 0,
  "query": {
    "bool": {
      "must": [
        { 
          "query_string": {
            "query": "*",
            "analyze_wildcard": true
          }
        },
        { 
          "range": {
            "@timestamp": {
              "gte": 1491554333990,
              "lte": 1491640733990,
              "format": "epoch_millis"
            }
          }
        }
      ],
      "must_not": []
    }
  },
  "_source": {
    "excludes": []
  },
  "aggs": {
    "2": {
      "date_histogram": {
        "field": "@timestamp",
        "interval": "30m",
        "time_zone": "Asia/Tokyo",
        "min_doc_count": 1
      },
      "aggs": {
        "1": {
          "avg": {
            "field": "system.process.cpu.total.pct"
          }
        }
      }
    }
  }
}

実行時間

トータル実行時間

btrfsは一度読み込むとメモリ乗っかるせいか、
圧縮オプションによる実行時間はほとんど変わりませんでした。
XFSは2倍強くらいになってしまいました。。。

サーバ名 実行時間
ec2-btrfs-force-zlib 1m 55s
ec2-btrfs-default 1m 56s
ec2-btrfs-force-lzo 1m 56s
ec2-xfs-default 4m 51s
単一実行時間の散布図

f:id:st1t:20170410120406p:plain
縦軸:処理時間(秒)
横軸:回数

CPU負荷のかかり方(USER)

f:id:st1t:20170412204854p:plain
ユーザランドのCPU使用率だけ見てみると全台綺麗にCPUを使い切っていて、
負荷のかかりかた自体は大きな違いがあるようではなさそうでした。

DISK IOの負荷のかかり方

f:id:st1t:20170412203632p:plain

  • 11:54 - 11:56周辺の山:Elasticsearchを再起動実施タイミング
  • 11:59 - 12:01周辺の山:ベンチマーク中

DISKの圧縮が効いていればいるほどDISKのIO Waitが小さく、かつIOが出ているようです。

参考:timelionのクエリ(ec2-xfs-default)

※以下クエリは意図的になるべく見やすくするために意図的に改行を付加してますが、
実際はワンライナーで記載する必要があります。

(
  .subtract(
    .es(index=metricbeat-*,metric=max:system.diskio.read.count,q=beat.hostname:ec2-xfs-default),
    .es(index=metricbeat-*,metric=max:system.diskio.read.count,q=beat.hostname:ec2-xfs-default,offset=-1s)
  ).label("ec2-xfs-default:read i/o"),
  .subtract(
    .es(index=metricbeat-*,metric=max:system.diskio.write.count,q=beat.hostname:ec2-xfs-default),
    .es(index=metricbeat-*,metric=max:system.diskio.write.count,q=beat.hostname:ec2-xfs-default,offset=-1s)
  ).label("ec2-xfs-default:write i/o")
).bars(width=1).yaxis(1,max=1500,label="DISK IO Count"),
.multiply(
  .es(index=metricbeat-*,metric=max:system.cpu.iowait.pct,q=beat.hostname:ec2-xfs-default),
  100
).lines(width=1).label("ec2-xfs-default:cpu i/o wait").yaxis(2,max=100,label="CPU IO wait(%)")

念のため3回ほど回してみた

f:id:st1t:20170412204234p:plain
DISKやCPUの負荷傾向は同じ傾向となりました。

結論

  • btrfsをデフォルト設定で使ってもDISK容量削減だと意味がなさそう
    • 圧縮率を求めるならcompress-forceでlzoかzlibを使う必要がある
  • 今回みたいな小さいクエリの場合はXFSよりBtrfsのほうが早い模様
    • なぜそうなるのかが追求できていないので全体的な傾向としてBtrfsが早いのか、今回の条件に限りなのかは要確認