【MarkLogic Server】XQueryチューニングメモ～ハードウェア・フォレスト要件

はじめに
========
今回は、ちょっとプログラミングの枠を超えハードウェアの要件についてちょっと見ていきましょう。

背景
========
XQueryの修正によるチューニングが性能のための戦術であるなら、データベースを動かすハードウェアを揃えるのは戦略です。「劣勢を戦術で挽回する」というのは聞こえがいいものの、現実には難しい課題です。
厄介なことに予算の確定→ハードウェアの選定→開発(プログラミング)→性能試験という流れを考えると、性能試験で問題が発生した場合にハードウェアの変更や追加購入に戻るのは相当な困難が伴います。
今回は、MarkLogic社の[サイト](https://docs.marklogic.com/guide/relnotes/other "サイト")から一定の性能を担保するハードウェアの選定について考えることにしました。なお、以下は現時点での最新バージョンであるver9を念頭に話を進めます。

ハードウェア要件
========
要約するとこんな感じです。

|ハードウェア| 必要な規模 | 補足 |
|---|---|---|
| CPU | DBサイズ100GB当たり1core | 1ライセンス/8core |
| メモリ | DBサイズの1/16倍 | インストール時、一部パラメータがメモリサイズに比例 |
| ディスク | DBサイズの1.5倍 | データの整理作業であるマージのために空きが必要 |

ディスクはHDDよりSSDが望ましいとのことです。
また上記の記述はMarkLogicに関する部分なので、例えばOSの領域なども考慮しましょう。当然ですがバックアップ機能や、レプリケーション機能を使う場合はさらに必要ですね。

ハードウェア見積もりの注意点
========
お分かりだと思いますが、上記の必要要件は全て「DB投入後のサイズ」をパラメータとしていることです。しかし、以前の[記事](https://qiita.com/JucoML/items/f17e3e7bbdd2fb26f736 "記事")に書いたように、元のXMLやCSVなどからDB投入後のサイズを確定させるのは困難です。
なるべく早く実際に使うデータのミニチュア版を作って、まずはロードしてみてどれくらいの倍率になるかを計測することをお奨めします。

フォレスト要件
========
フォレスト(forest)は、データが格納されるディレクトリです。実はフォレストの大きさを適正に保つことは性能を維持する上で重要です。この[サイト](https://docs.marklogic.com/guide/cluster/scalability "サイト")には経験則(rule-of-thumb)と断りつつ具体的な指標があります。(バージョン間で結構違いますのでご注意ください。以下はver9です。)

| 項目 | 指標 |
|---|---|
| 1フォレストのサイズ | 256GB以下 |
| 1フォレストのフラグメント数 | 1.28億以下 |
| 1フォレストのCPU数 | 2core以上 |

サイズとフラグメント数について指標を超えるとErrorLog.txtに警告文が継続的に出るようになると伺っています。
クエリ性能と直接関係ありませんが、バックアップはフォレスト単位で行われるため1個のフォレストに集中させるよりも複数に分割させた方が早く終わるようです。フラグメントはMarkLogic内のファイルやディレクトリなどを単位に設定されるものです。

設計におけるフォレストの個数
========
フォレストについては、設計の段階でCPU数の許す限り(CPU数の1/2倍の個数)作っておくことをお奨めします。例えば8CPUならば4個を予め作っておきましょう。
もしこの状態で閾値を超えるようならば同一ノード内でのフォレストの追加よりも、クラスタノードの追加(スケールアウト)が推奨されます。いずれにしてもフォレストの追加はデータに対するリバランスによる性能低下を一定時間は甘受しなければなりません。

フォレストの監視
========
運用を続けるとフォレストがどんどん膨らんでいくかもしれません。サイズはディレクトリに対してduコマンドで取ることもできるのですが、フラグメントはXQueryで取ることができます。折角なのでサンプルを書いてみました。

```
xquery version "1.0-ml";
declare namespace fst = "http://marklogic.com/xdmp/status/forest";
declare variable $databaseName as xs:string external;
(: == フォレストごとに集計 == :)
for $forest-id in xdmp:database-forests(xdmp:database($databaseName))
  let $standSize-seq := xdmp:forest-status($forest-id)/fst:stands/fst:stand/fst:disk-size/text()
  let $standFragmentCount-seq := xdmp:forest-counts($forest-id)/fst:stands-counts/fst:stand-counts/fst:active-fragment-count/text()
  (: フォレスト名:サイズMB:フラグメント数 :)
  return fn:concat(
    xdmp:forest-name($forest-id), ":", 
    fn:sum(xs:integer($standSize-seq)), "MB:", 
    fn:sum(xs:integer($standFragmentCount-seq), "個")
  )
```

現実には、上記のようなクエリを日次などで投げて得られた結果から、256GB/1.28億個よりも少し小さい値を閾値として警告を上げるようにし、その時点でフォレストの追加やディスクの追加(スケールアウト)などを検討するべきでしょう。

おわりに
========
今回は、ハードウェアとフォレストに焦点を当てました。XMLDBの躍進は各種ハードウェアの価格下落に支えられた面がありますが、扱うデータ量増加や処理の複雑化などによって現実にはハードウェアが予算に占める割合は大きいままだと思います。一方で十分な性能を出すためには潤沢な性能を持つハードウェアを選ぶ「戦略」が重要です。開発者目線ではありますが、本記事が性能試験でのづまづきを減らすことに寄与することを願います。


```math
\def\textsmall#1{%
  {\rm\scriptsize #1}
}
```

------------------------------------------------------------------------

**免責事項**

​​​​​$\textsmall{当ユーザ会は本文書及びその内容に関して、いかなる保証もするものではありません。}$
​​​​​$\textsmall{万一、本文書の内容に誤りがあった場合でも当ユーザ会は一切責任を負いかねます。}$
​​​​​$\textsmall{また、本文書に記載されている事項は予告なしに変更または削除されることがありますので、予めご了承ください。}$