やってみようNoSQL　MongoDBを最速で理解する

# はじめに
本記事は、NoSQLのMongoDBについて、最速で理解することを目標に、MongoDBの導入、mongoシェルの基本的な操作、バックアップ及びログローテーション等についてまとめました。

## NoSQL
Webサービスで利用されるデータベースとして、NoSQLも大分浸透してきました。

二大巨頭であるMySQLやPostgreSQLの後ろをしっかり走っています。
かつて、2011年頃まではWebサービスのアクセス数上位20サイトのうち、世界では18サイト、国内では19サイトで利用しているRDBMSとして、MySQLが挙げられていてました。[（参考記事）](http://www.obci.jp/wp-content/uploads/2012/01/20120119_oracle.pdf)

また、PostgreSQLについても、現在も世界中で多く使用されていると思います。
RDBMSがここまで長く使われてきた理由は、汎用的ゆえに、信頼性を要求される[OLTP](https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%87%A6%E7%90%86)や複雑な検索に特化した[DWH](https://ja.wikipedia.org/wiki/%E3%83%87%E3%83%BC%E3%82%BF%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%8F%E3%82%A6%E3%82%B9)、更新負荷の高いバッチ処理等いかなる局面において求められるパフォーマンスを発揮していたからではないでしょうか。

高速・軽量のMySQL、高機能・高可用性のPostgreSQLは、アーキテクチャをはじめ、哲学の違いもありますが、それぞれ素晴らしいソフトウェアです。

しかし、高速検索が求められるWebサービスで必要なのは、シンプルさになります。
そこで、シンプルな検索を高速化することを得意としたNoSQLが台頭してきたのは、必然でした。

以下、Wikipediaより。
>NoSQL（一般に "Not only SQL" と解釈される）とは、関係データベース管理システム (RDBMS) 以外のデータベース管理システムを指すおおまかな分類語である。

### NoSQLの種類
![nosql.png](https://qiita-image-store.s3.amazonaws.com/0/221759/46017bdd-eda6-640b-5c5f-6d1b5478f9ac.png)

#### キーバリュー型データベース
キーバリュー型データベースは、キーバリューストア（KVS：Key-Value Store）とも呼ばれ、データを"キー"："バリュー"の配列（連想配列）で保持します。利点としては、このシンプルにより、パフォーマンスがいいことが挙げられます。

#### ドキュメント指向データベース
ドキュメント指向データベースは、1件分のデータを「ドキュメント」としてデータを格納します。また、個々のドキュメントのデータ構造は自由で、データを追加する都度変えることができます。スキーマレスになるので、リレーショナルデータベースとは違って、事前にテーブルの構造を決めておく必要がありません。また、最大の利点は、スケーラビリティを重視していることです。大量のデータを扱うシステムにおいて、リレーショナルデータベースと比べて、比較的簡単にスケールできるような仕組みを持っていることが一般的です。MongoDBは、ドキュメント指向データベースになります。

#### 列指向データベース
列方向の操作に適したデータベースのことを、「列指向データベース」と呼びます。列方向の操作を頻繁に行う場合は、性能面で有利になります。

## MongoDB概要
Webを支える技術として、AjaxやWebAPIは広く普及し、ほとんどのウェブサイトで使用されようになり、また、JavaScriptやJSON及びYAML等、多数の関連技術が脇を固めています。

昨今、WebAPIでデータ取得する場合は、JSONがデファクトスタンダードだと思います。
しかし、従来のRDBMSでは、階層的なJSONのスキーマ定義をすることは困難であり、WebAPIは仕様変更されることも多く、その度にスキーマを変更するのは大変です。

そういった背景より、MongoDBが誕生しました。

![スクリーンショット 2019-02-08 00.23.49.png](https://qiita-image-store.s3.amazonaws.com/0/221759/23ec4060-51a1-e6b1-a51c-da58ee286d8a.png)

### アーキテクチャ
![mongodb.png](https://qiita-image-store.s3.amazonaws.com/0/221759/db09e353-6b01-4db4-ed95-7cff74239b3c.png)

- RDBMSのようにレコードをテーブルに格納するのではなく、「ドキュメント」と呼ばれる構造的データをJSONライクな形式で表現し、そのドキュメントの集合を「コレクション」として管理する
- コレクションはスキーマレスなドキュメントで格納され、任意のフィールドを好きなときに追加できる
- ドキュメントには複雑な階層構造を持たせることもでき、それらの構造に含まれるフィールドを指定したクエリやインデックス生成も簡単な指定によって行える
- KVSでは苦手なValueを検索の条件としたり、ソート・集計を実現できる
- [永続性](https://ja.wikipedia.org/wiki/%E6%B0%B8%E7%B6%9A%E6%80%A7)を提供

**MongoDBは、以下のようなシステムに適しています。**

- Webサイトの操作データログの蓄積
- アドホックなフィールドを検索対象とするコンテンツ
- ソーシャルゲーム 

**でも、MongoDBは以下のことができません。**

- NoSQLなので、SQLは使用できない。クエリはJavaScriptで行う
- RDBMSのように高度な結合操作（joinに相当する2つの関係から1つの関係を返す演算処理）
- トランザクション処理（※）

（※）MongoDB 4.0 で[マルチドキュメントトランザクション](https://docs.mongodb.com/manual/release-notes/4.0/#multi-document-transactions)がサポートされました。これにより、RDBMS likeのような一貫性のある処理を行うことができますが、以下の条件が必要です。

- レプリカセット構成あるいはシャーディング構成
- Feature Compatibility Version（FCV）4.0以降       
- WiredTigerストレージエンジン（※）

（※）MongoDB2.6まではストレージエンジンがMMAPv1、MongoDB3.2からWiredTigerがデフォルト

**従って、複数のトランザクションを必要としたクエリが必要になる場合、MongoDBは適していません。**

端的にいうと、MongoDBは万全ではありません。
適材適所になるのでシステムに合わせてRDBMSと使い分けが必要です。

基本は、MongoDBは非正規化したドキュメント構造を扱っているので、どうしても分割されてしまうドキュメントに対しての排他処理を行いたい場合に、マルチドキュメントトランザクションを使用すればいいのではないかと考えます。

### 機能
- レプリカ   
レプリケーションを構成することで障害耐性も高め、フェールオーバーが簡単にできる。RDBMSで言うLifeKeeperのような商用ソフトウェアは不要。
- シャーディングで水平スケーリングが可能
　スケールアウトが容易に可能になり、RDBMSよりも高速なレスポンスが得られることがある
（逆説的に言うと非正規形前提で設計しているので、結合演算が不要な場合はそのパフォーマンスを発揮する）

- GridFSと呼ばれるプロトコルを使用することで、大きなファイルをデータベースに格納・取得することができる
- インメモリ機能により、高速なReadを実現するためにpageと言う単位でデータをメモリに保持している

（※）厳密に言うと、ファイルシステムに格納されているコレクションを[mmap](https://ja.wikipedia.org/wiki/Mmap)している

（※）メモリ内に無いデータはディスクからロードすることになる、（Page Fault）
　そのため、Page FaultはメモリからReadする場合と比較して時間がかかってしまう

### その他
#### シェア
[DB-Engines](https://db-engines.com/en/)によると、2019年2月の時点でOracle Database、MySQL、Microsoft SQL Server、PostgreSQL等の歴史あるRDBMSプロダクトに続き5位をキープしています。

#### 導入コスト
- インストールのしやすさ
- 様々なプラットフォームに対応（Windows、Linux、MacOS等）
- 多くのプログラミング言語用ドライバの充実
- Javascriptライクに書けるので、フロントエンジニアでも親しみやすい
- レプリカの仕組みがRDBMSに比べて簡単
- シャーディングもRDBMDに比べて簡単（RDBMSの場合、アクセス負荷を考慮した設計は大変）

####　設計思想（MongoDBの開発元である10gen社CTO兼共同創業者Eliot Horowitzによる引用）
>MongoDBは研究室の中で設計されたわけではありません。私たちは，大きなスケールの，高可用性を備えた，堅牢なシステムを構築した経験をもとにMongoDBを作りました。ゼロからはじめたわけではありません。何が壊れているのかを見つけ出し，それに取り組むという方法を取ってきました。

## MongoDB導入
MongoDBは、CommunityとEnterpriseの 2つのエディションとしてリリースされています。コミュニティはMongoDBのオープンソースリリースです。エンタープライズは、追加の管理、認証、および監視機能を提供します。

本記事の環境は以下になります。

環境：CentOS Linux release 7.6.1810 (Core) 

### MongoDBのインストール（CentOS）

- /etc/yum.repos.d/mongodb-org-4.0.repoの作成   
`vi /etc/yum.repos.d/mongodb-org-4.0.repo`

```text
[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc
```

- yumレポジトリの有効確認    
`yum repolist mongodb-org-4.0 -v`

```console
プラグイン「fastestmirror」を読み込んでいます
Config time: 0.006
Yum version: 3.4.3
Loading mirror speeds from cached hostfile
 * base: ftp-srv2.kddilabs.jp
 * extras: ftp-srv2.kddilabs.jp
 * updates: ftp-srv2.kddilabs.jp
Setting up Package Sacks
pkgsack time: 0.004
リポジトリー ID            : mongodb-org-4.0/7
リポジトリーの名前         : MongoDB Repository
リポジトリーの状態         : 有効
リポジトリーのリビジョン   : 1545246847
リポジトリー更新日         : Thu Dec 20 04:14:08 2018
リポジトリー内パッケージ数 : 30
リポジトリー容量           : 458 M
リポジトリー基準 URL       : https://repo.mongodb.org/yum/redhat/7/mongodb-org/4.0/x86_64/
リポジトリーの期限         : 21,600 秒 (最終: Wed Jan 30 01:33:13 2019)
  Filter     : read-only:present
Repo ファイル名: /etc/yum.repos.d/mongodb-org-4.0.repo

repolist: 30
```

- mongodb-orgパッケージのインストール
`yum install -y mongodb-org`

- mongodb-orgパッケージのインストール 確認
`yum list installed | grep mongodb-org`

```console
mongodb-org.x86_64               4.0.5-1.el7                    @mongodb-org-4.0
mongodb-org-mongos.x86_64        4.0.5-1.el7                    @mongodb-org-4.0
mongodb-org-server.x86_64        4.0.5-1.el7                    @mongodb-org-4.0
mongodb-org-shell.x86_64         4.0.5-1.el7                    @mongodb-org-4.0
mongodb-org-tools.x86_64         4.0.5-1.el7                    @mongodb-org-4.0
```

- mongodbサービス起動
`systemctl start mongod`

- mongodbサービス起動確認
`systemctl status mongod`

```console
● mongod.service - MongoDB Database Server
   Loaded: loaded (/usr/lib/systemd/system/mongod.service; enabled; vendor preset: disabled)
   Active: active (running) since 水 2019-01-30 02:06:26 JST; 1s ago
     Docs: https://docs.mongodb.org/manual
  Process: 4222 ExecStart=/usr/bin/mongod $OPTIONS (code=exited, status=0/SUCCESS)
  Process: 4220 ExecStartPre=/usr/bin/chmod 0755 /var/run/mongodb (code=exited, status=0/SUCCESS)
  Process: 4218 ExecStartPre=/usr/bin/chown mongod:mongod /var/run/mongodb (code=exited, status=0/SUCCESS)
  Process: 4217 ExecStartPre=/usr/bin/mkdir -p /var/run/mongodb (code=exited, status=0/SUCCESS)
 Main PID: 4225 (mongod)
   CGroup: /system.slice/mongod.service
           └─4225 /usr/bin/mongod -f /etc/mongod.conf

 1月 30 02:06:25 localhost.localdomain systemd[1]: Starting MongoDB Database Server...
 1月 30 02:06:25 localhost.localdomain mongod[4222]: about to fork child process, wait....
 1月 30 02:06:25 localhost.localdomain mongod[4222]: forked process: 4225
 1月 30 02:06:26 localhost.localdomain systemd[1]: Started MongoDB Database Server.
Hint: Some lines were ellipsized, use -l to show in full.
```

### 設定
mongodのデータベースシステムの動作を制御する場合は、コマンドラインあるいは設定ファイルで制御します。本記事では、設定ファイルについて簡単に解説します。

mongodの設定ファイルは、YAMLフォーマットを使用して記述します。詳細は、[Configuration File Options](https://docs.mongodb.com/manual/reference/configuration-options/)を参照。また、コンフィグオプションは、設定ファイルの方が優先されます。

- サンプル

```yaml
systemLog:
   destination: file
   path: "/var/log/mongodb/mongod.log"
   logAppend: true
storage:
   journal:
      enabled: true
processManagement:
   fork: true
net:
   bindIp: 127.0.0.1
   port: 27017
setParameter:
   enableLocalhostAuthBypass: false
...
```

### mongoシェルでのデータベース操作
mongoシェルは、MongoDBのインタラクティブなJavaScriptのインタフェースです。mongoシェルを使用して、データを照会および更新したり、管理操作を実行したりできます。

#### データベース作成〜コレクションの作成
- mongoシェル起動
`mongo`

- データベースの作成
`use testdb`

```console
switched to db testdb
```

- データベースの確認     
`show dbs`

```console
admin   0.000GB
config  0.000GB
local   0.000GB
```

use <データベース名> 実行後、一覧表示を行っても作成したデータベースは表示されません。最低でも1件以上のデータ登録が必要です。コレクション作成後、以下のように表示されます。

```console
admin   0.000GB
config  0.000GB
local   0.000GB
testdb  0.000GB
```

例として、testcollというコレクションを作成し、ドキュメントの挿入を行います。ドキュメントは、JSON形式で記述します。

- ドキュメントの挿入（コレクションの作成）   
`db.testcoll.insert( { "key1" : "value1", "key2" : "value2" } )`
- ハッシュと配列の混ざったドキュメントの挿入   
`db.testcoll.insert( { "array" : [ "Windows", "Mac" , "Linux" ] } )`

#### ドキュメントの参照
- ドキュメントの参照（コレクション全てのデータを取得）    
`db.testcoll.find()`

```console
{ "_id" : ObjectId("5c6198f6fb5e83ffaa3ba044"), "key1" : "value1", "key2" : "value2" }
{ "_id" : ObjectId("5c61991ffb5e83ffaa3ba045"), "array" : [ "Windows", "Mac", "Linux" ] }
```

"_id" : ObjectId("5c...")は、MongoDBが自動で付けます。RDBMSでいう主キーに該当します。

- ドキュメントの参照（最初の1つのドキュメントを取得）   
`db.testcoll.findOne()`

```console
{
	"_id" : ObjectId("5c6198f6fb5e83ffaa3ba044"),
	"key1" : "value1",
	"key2" : "value2"
}
```

- ドキュメントの参照（ハッシュキーを引数に渡して値を取得）  
`db.testcoll.findOne()["key1"]`

```console
value1
```

- コレクションの確認    
`show collections`

```console
testcoll
```

#### Javascriptでデータベース操作
- ドキュメントの挿入（forループでデータを挿入）
`for (let i = 1; i <= 10; i++) db.testcoll2.insert( { x : 1 , y : i } )`

- 検索結果に対するカーソル
`var c = db.testcoll2.find()`
`while ( c.hasNext() ) printjson( c.next() )`

```console
{ "_id" : ObjectId("5c6199aefb5e83ffaa3ba046"), "x" : 1, "y" : 1 }
{ "_id" : ObjectId("5c6199aefb5e83ffaa3ba047"), "x" : 1, "y" : 2 }
{ "_id" : ObjectId("5c6199aefb5e83ffaa3ba048"), "x" : 1, "y" : 3 }
{ "_id" : ObjectId("5c6199aefb5e83ffaa3ba049"), "x" : 1, "y" : 4 }
{ "_id" : ObjectId("5c6199aefb5e83ffaa3ba04a"), "x" : 1, "y" : 5 }
{ "_id" : ObjectId("5c6199aefb5e83ffaa3ba04b"), "x" : 1, "y" : 6 }
{ "_id" : ObjectId("5c6199aefb5e83ffaa3ba04c"), "x" : 1, "y" : 7 }
{ "_id" : ObjectId("5c6199aefb5e83ffaa3ba04d"), "x" : 1, "y" : 8 }
{ "_id" : ObjectId("5c6199aefb5e83ffaa3ba04e"), "x" : 1, "y" : 9 }
{ "_id" : ObjectId("5c6199aefb5e83ffaa3ba04f"), "x" : 1, "y" : 10 }
```

- ドキュメントを変数に格納    
`doc=db.testcoll2.findOne()`
- キー"key1"の値を表示    
`printjson(doc["x"])`

```console
1
```

MongoDBのCRUD操作の詳細は、[MongoDB CRUD Operations](https://docs.mongodb.com/manual/crud/)を参照。

#### データベースの終了
mongoシェルで接続時のサービス終了は、以下のコマンドを実行します。
   
`use admin`   

```console
switched to db admin
```

`db.shutdownServer();`

```console
server should be down...
2019-02-12T00:53:59.058+0900 I NETWORK  [js] trying reconnect to 127.0.0.1:27017 failed
2019-02-12T00:53:59.058+0900 I NETWORK  [js] reconnect 127.0.0.1:27017 failed failed
```

`quit();`

#### mongodの起動失敗
mongodの起動に失敗する場合は、強制終了などをしてロックファイルが残っている場合があります。

```console
● mongod.service - MongoDB Database Server
   Loaded: loaded (/usr/lib/systemd/system/mongod.service; enabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since 月 2019-02-11 23:47:18 JST; 47min ago
     Docs: https://docs.mongodb.org/manual
  Process: 3190 ExecStart=/usr/bin/mongod $OPTIONS (code=exited, status=14)
  Process: 3185 ExecStartPre=/usr/bin/chmod 0755 /var/run/mongodb (code=exited, status=0/SUCCESS)
  Process: 3181 ExecStartPre=/usr/bin/chown mongod:mongod /var/run/mongodb (code=exited, status=0/SUCCESS)
  Process: 3176 ExecStartPre=/usr/bin/mkdir -p /var/run/mongodb (code=exited, status=0/SUCCESS)

 2月 11 23:47:06 localhost.localdomain systemd[1]: Starting MongoDB Database Server...
 2月 11 23:47:10 localhost.localdomain mongod[3190]: about to fork child process, wait....
 2月 11 23:47:10 localhost.localdomain mongod[3190]: forked process: 3461
 2月 11 23:47:18 localhost.localdomain systemd[1]: mongod.service: control process exi...4
 2月 11 23:47:18 localhost.localdomain systemd[1]: Failed to start MongoDB Database Se....
 2月 11 23:47:18 localhost.localdomain systemd[1]: Unit mongod.service entered failed ....
 2月 11 23:47:18 localhost.localdomain systemd[1]: mongod.service failed.
Hint: Some lines were ellipsized, use -l to show in full.
```

その場合は、以下のコマンドを実行し、ロックファイルを削除すると、正常に起動できます。

`rm /var/lib/mongo/mongod.lock`
`rm /tmp/mongodb-27017.sock`

あるいは、/var/log/mongodb/mongod.logを参照し、以下のような「715: /var/lib/mongo/WiredTiger.turtle: handle-open: open: Permission denied」のログが出力されるている場合は、/var/lib/mongoディレクトリの権限を見直してください。

```text
2019-02-12T01:10:04.498+0900 E STORAGE  [initandlisten] WiredTiger error (13) [1549901404:498011][3521:0x7f0ba3f22b80], wiredtiger_open: __posix_open_file, 715: /var/lib/mongo/WiredTiger.turtle: handle-open: open: Permission denied Raw: [1549901404:498011][3521:0x7f0ba3f22b80], wiredtiger_open: __posix_open_file, 715: /var/lib/mongo/WiredTiger.turtle: handle-open: open: Permission denied
```

### MongoDBのバックアップ
MongoDBのバックアップ対象は、データそのもののバックアップと、コンフィグオプションのバックアップになります。
コンフィグオプションは、mongod起動シェルやコンフィグファイルなどのファイルをコピーすればよいでしょう。

なお、データのバックアップには一般的にフルバックアップと差分バックアップを組み合わせて使用しますが、MongoDBに差分バックアップの機能はありません。

#### MongoDBのバックアップ方法
MongoDBでフルバックアップを行うには，以下の2通りの方法があります。

- データファイルをコピーする方法  
  データベースを停止するか、ロックしておく必要がある
- mongodumpを利用する方法    
  mongodumpデータベースの内容のバイナリエクスポートを作成するためのユーティリティ。mongodumpの場合、オンラインバックアップが可能

**留意事項**
インデックスはダンプされずにリストア時に再構築されるため，大きなインデックスがある場合にはリストアに時間がかかります。また、小規模なMongoDB展開をバックアップおよび復元するためのシンプルで効率的なツールですが、大規模システムのバックアップを取得するのには理想的ではありません。

#### mongodumpによるバックアップ
- バックアップ   
`mongodump`

```console
2019-02-12T01:28:20.371+0900	writing admin.system.version to 
2019-02-12T01:28:20.372+0900	done dumping admin.system.version (1 document)
2019-02-12T01:28:20.372+0900	writing testdb.testcoll2 to 
2019-02-12T01:28:20.372+0900	writing testdb.testcoll to 
2019-02-12T01:28:20.379+0900	done dumping testdb.testcoll (2 documents)
2019-02-12T01:28:20.380+0900	done dumping testdb.testcoll2 (10 documents)
```

mongodumpコマンド実行後、カレントディレクトリにdumpディレクトリが作成されます。そのdumpディレクトリの中に、データベースのコレクション等データが退避されます。

```console
[root@localhost tmp]# ll dump/
合計 0
drwxr-xr-x. 2 root root  69  2月 12 01:28 admin
drwxr-xr-x. 2 root root 110  2月 12 01:28 testdb
```

#### mongodumpによるリストア
試しに、リストア前に作成したデータを削除して、リストア後、データが復元されているかを確認してみましょう。
まずは、例としてtestcollとtestcoll2のコレクションをそれぞれ削除します。   

- コレクションの削除   
`db.testcoll.drop();`    
`db.testcoll2.drop();`

コレクションは削除できましたが、データベースは削除できていないので、データベースも削除します。

- データベースの削除    
`db.dropDatabase();`

```console
{ "dropped" : "testdb", "ok" : 1 }
```

データベースも削除できたので、リストアします。

- リストア    
`mongorestore ./dump`

```console
2019-02-12T03:32:42.592+0900	preparing collections to restore from
2019-02-12T03:32:42.594+0900	reading metadata for testdb.testcoll2 from dump/testdb/testcoll2.metadata.json
2019-02-12T03:32:42.608+0900	restoring testdb.testcoll2 from dump/testdb/testcoll2.bson
2019-02-12T03:32:42.611+0900	reading metadata for testdb.testcoll from dump/testdb/testcoll.metadata.json
2019-02-12T03:32:42.611+0900	no indexes to restore
2019-02-12T03:32:42.611+0900	finished restoring testdb.testcoll2 (10 documents)
2019-02-12T03:32:42.624+0900	restoring testdb.testcoll from dump/testdb/testcoll.bson
2019-02-12T03:32:42.627+0900	no indexes to restore
2019-02-12T03:32:42.627+0900	finished restoring testdb.testcoll (2 documents)
2019-02-12T03:32:42.627+0900	done
```

リストア後、mongoシェルに接続して復元されていることを確認します。

`show dbs`   

```console
admin   0.000GB
config  0.000GB
local   0.000GB
testdb  0.000GB
```

`show collections`

```console
testcoll
testcoll2
```

`db.testcoll.find()`

```console
{ "_id" : ObjectId("5c6198f6fb5e83ffaa3ba044"), "key1" : "value1", "key2" : "value2" }
{ "_id" : ObjectId("5c61991ffb5e83ffaa3ba045"), "array" : [ "Windows", "Mac", "Linux" ] }
```

データベース、コレクション、ドキュメントについて復元されていることが確認できました。

### 監視
mongodの監視に関するコマンドを以下に記載します。

- コレクションの処理時間順に表示   
`mongotop`

```console
2019-02-12T01:34:41.789+0900	connected to: 127.0.0.1

                    ns    total    read    write    2019-02-12T01:34:42+09:00
    admin.system.roles      0ms     0ms      0ms                             
  admin.system.version      0ms     0ms      0ms                             
config.system.sessions      0ms     0ms      0ms                             
     local.startup_log      0ms     0ms      0ms                             
  local.system.replset      0ms     0ms      0ms                             
       testdb.testcoll      0ms     0ms      0ms                             
      testdb.testcoll2      0ms     0ms      0ms 
```

- MongoDB全体の状態を表示  
`mongostat`

```console
      time
    *0    *0     *0     *0       0     2|0  0.0% 0.0%       0 1.06G 49.0M 0|0 1|0   158b   62.4k    1 Feb 12 01:35:25.188
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0 1.06G 49.0M 0|0 1|0   157b   62.2k    1 Feb 12 01:35:26.188
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0 1.06G 49.0M 0|0 1|0   157b   62.1k    1 Feb 12 01:35:27.190
    *0    *0     *0     *0       0     2|0  0.0% 0.0%       0 1.06G 49.0M 0|0 1|0   158b   62.4k    1 Feb 12 01:35:28.187
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0 1.06G 49.0M 0|0 1|0   157b   62.2k    1 Feb 12 01:35:29.187
    *0    *0     *0     *0       0     1|0  0.0% 0.0%       0 1.06G 49.0M 0|0 1|0   157b   62.2k    1 Feb 12 01:35:30.188
    *0    *0     *0     *0       0     2|0  0.0% 0.0%       0 1.06G 49.0M 0|0 1|0   158b   62.2k    1 Feb 12 01:35:31.187
```

- MongoDB全体の統計情報（serverStatus()メソッド）   
`db.serverStatus()`

```console
{
	"host" : "localhost.localdomain",
	"version" : "4.0.5",
	"process" : "mongod",
	"pid" : NumberLong(3718),
	"uptime" : 5961,
	"uptimeMillis" : NumberLong(5961613),
	"uptimeEstimate" : NumberLong(5961),
	"localTime" : ISODate("2019-02-15T18:04:20.956Z"),
	"asserts" : {
		"regular" : 0,
		"warning" : 0,
		"msg" : 0,
		"user" : 2,
		"rollovers" : 0
	},
	"connections" : {
		"current" : 1,
		"available" : 51199,
		"totalCreated" : 4
...
```


### ログ
バージョン3.0.0の新機能より、--logRotate reopenを付与すると、
典型的なLinux / Unixのログローテーションの動作に従ってログファイルが開かれます。

ここでは、MongoDBのログローション方法について記載します。
まずは、/etc/mongod.confファイルに「logRotate: reopen」の設定値を記述します。

```text
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
  logRotate: reopen
```

次に、/etc/logrotate.d/配下にmongod用のログローテーション用の設定ファイルを作成します。

`vi /etc/logrotate.d/mongod `

```text
/var/log/mongodb/mongod.log {
  daily
  rotate 30
  missingok
  compress
  delaycompress
  notifempty
  create 640 mongod mongod
  sharedscripts
  postrotate
    /bin/kill -SIGUSR1 `cat /var/run/mongodb/mongod.pid 2>/dev/null` >/dev/null 2>&1
  endscript
}
```

ログローテーションのテストは以下のコマンドを実行します。
`logrotate -dv /etc/logrotate.d/mongod`

```console
reading config file /etc/logrotate.d/mongod
Allocating hash table for state file, size 15360 B

Handling 1 logs

rotating pattern: /var/log/mongodb/mongod.log  after 1 days (30 rotations)
empty log files are not rotated, old logs are removed
considering log /var/log/mongodb/mongod.log
  log does not need rotating (log has been already rotated)not running postrotate script, since no logs were rotated
```

エラーが出力されないことを確認したら、logrotateにより自動でログローテーションが行われます。

```console
-rw-r-----. 1 mongod mongod   1385  2月 16 01:38 /var/log/mongodb/mongod.log
-rw-------. 1 mongod mongod 115166  2月 16 01:38 /var/log/mongodb/mongod.log.1
```

また、mongoシェル接続時に、以下のコマンドでも手動でログローテーションできます。
`db.adminCommand( { logRotate:1 } )`

```console
{ "ok" : 1 }
```

### ユーザ作成
認証機能を有効にして、データベースに対してユーザ毎のアクセス制御を行います。
まずは、mongoシェル接続時に、以下のコマンドを実行し、管理者用ユーザを作成します。

`use admin`

```
db.createUser(
    {
      user: "admin",
      pwd: "password",
      roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
    }
  )
Successfully added user: {
	"user" : "admin",
	"roles" : [
		{
			"role" : "userAdminAnyDatabase",
			"db" : "admin"
		}
	]
}
```

ユーザー情報は、adminデータベースに保存されています。また、admin データベースの system.users コレクションにユーザー情報が保持されています。

ユーザー管理が行えるロールは、userAdminとuserAdminAnyDatabase の2種類があり、userAdminは指定されたデータベースに対してのみユーザー管理が行える組み込みロールになります。userAdminAnyDatabaseは、adminデータベースに対してのみ指定できる特別な組み込みロールで、このロールが付与されたユーザーはすべてのデータベースに対するユーザー管理が行えます。

次に一般用ユーザを作成します。　　　
`use testdb`

```
db.createUser(
    {
      user: "testuser",
      pwd: "password",
      roles: [ { role: "readWrite", db: "testdb" } ]
    }
  )
Successfully added user: {
	"user" : "testuser",
	"roles" : [
		{
			"role" : "readWrite",
			"db" : "testdb"
		}
	]
}
```

ユーザが作成されたことを確認します。　　　
`use admin`
`db.system.users.find()`

```console
{ "_id" : "admin.admin", "user" : "admin", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "mBaj6lY+FmjRpDoXAq/9nA==", "storedKey" : "/zqpTgZEV2iUlFsXwybT7t2NeYI=", "serverKey" : "+m4wf2orLVRyntSJTLjbIsa23+Q=" }, "SCRAM-SHA-256" : { "iterationCount" : 15000, "salt" : "leY10lhgYGZHVm3FdcDqIKVig4ZSegW8lYBDzA==", "storedKey" : "gwSafO6sc4OqJTWJ2aJcYXQ4XnxdZFEYR4Bv4aGjyas=", "serverKey" : "n8D7GZboYIpkJf++35GXENDK8ubSWiksu23+OFNXcGE=" } }, "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] }
{ "_id" : "admin.testuser", "user" : "testuser", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "EVUclTLqMCIn1jtYqNBjWQ==", "storedKey" : "HQ1HiqS4FTMuVSH4wYuFPYMHBfI=", "serverKey" : "kQZPAh6kNqRlqoglY3k7D1864n8=" }, "SCRAM-SHA-256" : { "iterationCount" : 15000, "salt" : "KjnGdFe2WrAb5xhXdoAL25okfrc00uU1xI7Oow==", "storedKey" : "ZRUhnJPJrQBdgq94qcrRFOglQ3SgO/o5By5mKMKHMIk=", "serverKey" : "2N9s8GJLT7Q9dVT4Q33r/QehPK9H+ThgwK4EwQTvCWE=" } }, "roles" : [ { "role" : "readWrite", "db" : "testdb" } ] }
```

ユーザ認証機能を有効にします。
`vi /etc/mongod.conf`

```text
security:
  authorization: enabled
```

サービス再起動を行い、設定反映後、mongoシェルに接続して以下のコマンドを実行し、認証機能が有効になっていることを確認します。

`use admin`
`db.system.user.find()`

```
Error: error: {
	"ok" : 0,
	"errmsg" : "command find requires authentication",
	"code" : 13,
	"codeName" : "Unauthorized"
}
```

認証機能が有効になっているため、エラーが出力されるので認証を行います。
`db.auth("admin","password")`

```console
1
```

再度、コマンドを実行すると、ユーザ情報が確認できました。
`db.system.users.find()`

```console
{ "_id" : "admin.admin", "user" : "admin", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "mBaj6lY+FmjRpDoXAq/9nA==", "storedKey" : "/zqpTgZEV2iUlFsXwybT7t2NeYI=", "serverKey" : "+m4wf2orLVRyntSJTLjbIsa23+Q=" }, "SCRAM-SHA-256" : { "iterationCount" : 15000, "salt" : "leY10lhgYGZHVm3FdcDqIKVig4ZSegW8lYBDzA==", "storedKey" : "gwSafO6sc4OqJTWJ2aJcYXQ4XnxdZFEYR4Bv4aGjyas=", "serverKey" : "n8D7GZboYIpkJf++35GXENDK8ubSWiksu23+OFNXcGE=" } }, "roles" : [ { "role" : "userAdminAnyDatabase", "db" : "admin" } ] }
{ "_id" : "admin.testuser", "user" : "testuser", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "EVUclTLqMCIn1jtYqNBjWQ==", "storedKey" : "HQ1HiqS4FTMuVSH4wYuFPYMHBfI=", "serverKey" : "kQZPAh6kNqRlqoglY3k7D1864n8=" }, "SCRAM-SHA-256" : { "iterationCount" : 15000, "salt" : "KjnGdFe2WrAb5xhXdoAL25okfrc00uU1xI7Oow==", "storedKey" : "ZRUhnJPJrQBdgq94qcrRFOglQ3SgO/o5By5mKMKHMIk=", "serverKey" : "2N9s8GJLT7Q9dVT4Q33r/QehPK9H+ThgwK4EwQTvCWE=" } }, "roles" : [ { "role" : "readWrite", "db" : "testdb" } ] }
```

### 勘所
MongoDBの設計時の勘所を以下に記載します。

- 性能
    - シャーディングをしっかり組むこと
    - ドキュメント肥大化によりドキュメントの再配置が起こらないように、ドキュメントに必要なフィールドをあらかじめ作っておく
    - ジャーナルの書き込み回数を減らす
    - オンメモリを考慮し、全データをアクセスするような処理は行わない
- 運用
    - ハードウェア障害に備えて、バックアップファイルは別のサーバ上に退避させる
    - ディスク使用率やメモリ使用率のしきい値監視
    - クラウドの場合、DC被災時を考慮してレプリカセットを構築しておけば、DR切替が容易。
- セキュリティ
    - デフォルトポートからの変更
    - クエリログの出力
    - データベースに対する外部からのアクセス制限及び認証
    - ディスク暗号化あるいは、データベースレイヤーでの暗号化（性能とトレードオフ）

## 参考
- [Welcome to the MongoDB Docs](https://docs.mongodb.com/) 
     - [The MongoDB 4.0 Manual](https://docs.mongodb.com/manual/) 
     - [Install MongoDB Community Edition on Red Hat Enterprise or CentOS Linux](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/)
- [MongoDBの薄い本](https://www.cuspy.org/diary/2012-04-17/the-little-mongodb-book-ja.pdf)
- [MongoDBを始めた頃に知っていたら、と思う14のこと](https://www.infoq.com/jp/articles/Starting-With-MongoDB)
- [Automating MongoDB Log Rotation](https://www.percona.com/blog/2018/09/27/automating-mongodb-log-rotation/)

# おわりに
NoSQL及びMongoDBの魅力は、SQLにないデータ構造の扱いやすさや、PoostgreSQL及びMySQLにはないモダンな設計に尽きると思います。ユースケースによって、性能やACID等、RDBMSと比較すると一長一短が出てくるので、引き続き見極めていきます。