たまたま発生したのでメモ。
実施環境: Splunk Free 8.2.2
発生した事象
以下のようなデータを考えます。
STR |
---|
AAA AAA |
| makeresults count=1
| eval STR = "AAA,AAA"
| makemv delim="," STR
これについて stats コマンドの count 関数を使用して件数を集計すると、1という結果が出ます。
| makeresults count=1
| eval STR = "AAA,AAA"
| makemv delim="," STR
| stats count
では、以下のように BY で STR 毎の件数を集計するとどうなるでしょうか。
普通に考えれば、先ほどと同じく1となるように思われます。
| makeresults count=1
| eval STR = "AAA,AAA"
| makemv delim="," STR
| stats count BY STR
ところが、実際に動かしてみると以下のようになります。
先ほどと異なり、結果は__「2」__になります。
なぜこのようなことが起こるのでしょうか。
原因
原因は、「マルチバリュー」にあります。
stats コマンドで BY を指定した際、同コマンドは「個別の値ごとに1つの行を返す」のですが、マルチバリューは含まれる値全てセットで「個別の値」ではなく、含まれる値__それぞれが__「個別の値」として扱われます。
すなわち、上記の例を用いると、
STR = AAA, AAA
ではなく、
STR = AAA, STR = AAA
といった感じになるのです。
検索での動作を見るとわかりやすいです。
マルチバリューでない「 AAA , AAA 」は「 AAA 」とは異なるので検索でヒットしません。
| makeresults count=1
| eval STR = "AAA,AAA"
| where STR = "AAA"
しかしこれをマルチバリューにすると、「 AAA 」でヒットするようになります。
これは、「 STR 」の値が「 AAA , AAA 」ではなく「 AAA 」と「 AAA 」だからです。
| makeresults count=1
| eval STR = "AAA,AAA"
| makemv delim="," STR
| where STR = "AAA"
マルチバリューの値を異なるものにすると、もっとわかりやすくなります。
以下の例をご覧下さい。
| makeresults count=1
| eval STR = "AAA,BBB"
| makemv delim="," STR
| where STR = "AAA" AND STR = "BBB"
「 STR が AAA かつ STR が BBB 」は普通に考えれば矛盾した条件であり、1件もヒットするはずはありません。
ですが、マルチバリューにおいては
STR = AAA, STR = BBB
すなわち、「 STR が AAA という値と BBB という値を同時にとる」という状況が発生するため、検索にヒットしてしまいます。
対処方法
マルチバリューを使用しないのが一番の対処法ですが、 collect コマンドで重複するフィールドを定義してしまった場合など、実際は思わぬ動作でマルチバリューが発生してしまうことがあります。
そのような状況へのシンプルな対処法として、 stats コマンドや timechart コマンドの dedup_splitvals オプションを true にするというのがあります。
このオプションはデフォルトでは false なのですが、これを true にするとマルチバリュー内での重複を排除してくれます。
| makeresults count=1
| eval STR = "AAA,AAA"
| makemv delim="," STR
| stats dedup_splitvals=true count BY STR
または、 eval コマンドの mvdedup 関数を使用するという方法もあります。
こちらもマルチバリュー内部での重複を排除する効果があります。
| makeresults count=1
| eval STR = "AAA,AAA"
| makemv delim="," STR
| eval STR_TMP = mvdedup(STR)
| stats count BY STR_TMP
ただし、上記はあくまで重複の排除なので、異なる値がマルチバリューとして同時に入っている場合は対応できません。
| makeresults count=1
| eval STR = "AAA,BBB"
| makemv delim="," STR
| stats dedup_splitvals=true count BY STR
このような場合は、マルチバリューを nomv コマンドで結合して、全てセットで「個別の値」として扱うことで対応可能です。
| makeresults count=1
| eval STR = "AAA,BBB"
| makemv delim="," STR
| nomv STR
| stats count BY STR