MongoDB 3のWired Tigerを使うとうれしいこと

MongoDB 3の大きな変更点として、データを永続化するストレージ部分の実装が、
今まではOS任せのMMAPベースしか選べなかったのが、WiredTigerも選べるようになったことだ。

WiredTigerになって何がうれしいのかまとめてみた。


### ドキュメントレベルロックになった

MongoDB 2系ではMMAPベースでロックの単位がデータベースで、MongoDB 3系でも従来のMMAPベースのものだとコレクションレベルロックまでしかできないが、WiredTigerを選べばドキュメントレベルロックができるようになった。

これにより書き込み時のロック待ちが減る

### インデックスとデータを別ディレクトリにすることができるようになった

` --wiredTigerDirectoryForIndexes`のオプションにより、インデックスをデータと別の場所に配置できる。メモリだけを高速なSSDに入れ、データは磁気ディスクといった細かいチューニングができるようになった。

### メモリ使用量の制限ができるようになった

従来のMMAPベースでは、メモリはOSから与えられるものであり、MongoDB側で制御するすべはなかった。
そのためメモリはOSから与えられるだけ使うため、ほぼ無制限にメモリを使う状態であった。
WiredTigerでは`--wiredTigerCacheSizeGB`オプションにより、メモリの使用量を制限できるようになった。

### ストレージの統計情報を出力できるようになった

`--wiredTigerStatisticsLogDelaySecs`オプションで、データディレクトリの下に、以下のようなストレージの稼働統計情報を定期的に出せるようになった。

```
Mar 19 00:57:21 77824 data/ block-manager: bytes read
Mar 19 00:57:21 155648 data/ block-manager: bytes written
Mar 19 00:57:21 0 data/ block-manager: mapped blocks read
Mar 19 00:57:21 5 data/ block-manager: blocks pre-loaded
Mar 19 00:57:21 18 data/ block-manager: blocks read
Mar 19 00:57:21 34 data/ block-manager: blocks written
Mar 19 00:57:21 14347 data/ cache: tracked dirty bytes in the cache
Mar 19 00:57:21 22224 data/ cache: bytes currently in the cache
```

### データの圧縮ができるようになった

`--wiredTigerCollectionBlockCompressor`オプションで、データを圧縮できる。またこの圧縮はコレクションごとに制御できる。

### インデックス圧縮ができるようになった

`--wiredTigerIndexPrefixCompression`オプションで、インデックスのプレフィックスを圧縮できるようになった。

### データファイルのフラグメンテーションが発生しなくなった

MMAPベースでは、ドキュメントを消したりドキュメントが大きくなって移動が発生した場合に、データファイルに穴ができ（フラグメンテーションが発生し）、実データ容量よりもデータファイルが大きくなる問題があった。さらに、これはメモリにもそのまま乗るため、メモリ使用量も圧迫してた。
WiredTigerでは挿入時にコンパクションを行うため、この問題は発生しないらしい。[こちらのブログ](https://groups.google.com/forum/#!topic/mongodb-user/nV-DWbgB2oM)にかいてあった。

### ジャーナリングの仕方が変わった

~~MMAPベースではジャーナリングを有効にすると、ジャーナルに書いてその後ストレージに書くという流れだったが、
WiredTigerでは、チェックポイントを設け、それまではWrite Ahead Logに書き溜めていき、チェックポイントでストレージに反映するという方式に変わった。~~

MMAPベースとWiredTigerではジャーナリングの機能が全く異なり、保証する内容も違う。

MMAPベースでは、ドキュメントの更新は今まであったデータを直接更新する方式であったため、**ジャーナリングが無効でドキュメントを書き途中でMongoDBがクラッシュすると、次回起動時にドキュメントが壊れており起動できなくなる**可能性があった(これを直すにはrepairコマンドを打つ必要があった)

WiredTigerでは、ドキュメントの更新は古いデータは残して新しいバージョンでデータを追記する方式になっており、ドキュメントの更新はジャーナルの有効・無効にかかわらずアトミックに行われる。すなはち、**ジャーナルが無効でドキュメントの書き途中でMongoDBがクラッシュしても、ドキュメントが壊れることはない**。

では、WiredTigerのジャーナルが何のために存在するかを説明する。MongoDBはcheckpointと呼ばれる単位(60秒or2G書き込み)でデータをメモリからディスクに永続化する。ジャーナルが無効のでMongoDBがクラッシュした場合、最後のcheckpoint以降の更新は永続化されていないため、ロストする。ジャーナルが有効であれば、直近の更新まで永続化される。

### さいごに

従来のMMAPとWiredTigerは同一レプリカ内に共存することができるので、プライマリはMMAPのままにして、とりあえずセカンダリだけWiredTigerで動かしてみて様子を見ることができます。

参考
* http://docs.mongodb.org/manual/reference/program/mongod/
* http://docs.mongodb.org/manual/core/storage/

## WiredTiger実装について(2016/4/15追記)

WiredTigerの実装について少し調べたの追記します。

### MVCCとCAS

MVCC(multiversion concurrency control)では、データを更新する際に、物理的に前のデータを上書きするわけではなく、より新しいバージョンのデータを追記します。これにより、データの書き込み最中でも、読み込みスレッドは古いバージョンからデータを読めるので、スループットの向上が見込まれます。

CAS(Compare-and-Swap)はデータを更新する際の排他制御です。CASでは、スレッドがデータを更新する際に「前の値はAだったけど、それをBに更新したい」といった指示をします。もし他のスレッドがデータを更新していなければ更新が成功し、他のスレッドが更新していればその更新は失敗します。これによりロックを取ることなくデータを更新できます。

以前のMMAPベースは、データベースのファイルを直接更新するといった非常に大雑把な方式であったため、ロックの粒度はデータベース単位でロックも発生していました。しかし、WiredTigerになり、MVCCやCASといった方式の採用したことにより、マルチコア環境であれば複数スレッドで並行して処理できるようになり、スループットが向上しています。

参照
* http://www.slideboom.com/presentations/133166
* http://www.slideshare.net/wiredtiger