初心者向け、MySQL の日本語検索を高速で行う方法（ Mroonga 編 ）

# 目的：
MySQL の日本語検索を早くしたい。
全文検索ってのが良いらしいけどよく分からない。
Web での検索用が主。
特に CentOS + MySQL で。
とりあえず動かし方教えやがれ。
で、運用で問題になる点はどこ？

という方向けのエントリーです。
つまり4年前の自分にこれを読めとグリグリ押し付けたい内容です。
（特に前提部）
この記事の作成時の Mroonga は 6系ですが、記述内容（特に壊れるとは？）はそれよりも前の版の事が多いです。


# 急ぐ人用まとめ：
LIKE 検索が早くなる訳じゃない。全文検索用の検索句の記述が必要。
Mroonga のインストールと使用は簡単。
ハードウェアや OS 側の準備で運用の可否が決まる。（←ここ**重要**）
ちょっ速！
壊れても泣かない。

# 本編: 

すごく魅力的な Mroonga さんですが、インストールは簡単でも、運用するには前提の確認が必要です。

## 前提：
サーバーのメモリーは Mroonga の扱うデーター分**必要**になります。
インストールとカーネルパラメータの変更に root 権限が必要です。
レプリケーションに対応してますが、自動スケールは一旦諦めよう。
（基本はスケールアップで、メモリーが足りない時は分割）
トランザクションは「非対応」です。（ラッパーモード？知らない子ですね）
壊れた時にどう修復するか、また、その時間をどう確保するか。
割と重要な修正がたまによく出るので、Update する時間をどう確保するか。

この中で1つでも「？」や「！」があるなら Mroonga 以外のアプローチも考えた方が良いです。
なんとかする方法もあるかもしれませんが、「初心者」向きではないと思います。
（トランザクションはラッパーモードより、Master = InnoDB, Slave = Mroonga 構成という話も）

特にレンタルサーバーの WordPress で Mroonga 使いたい！は、難しい気がします。

## 説明する事リスト：
1. カーネルパラメーターを調整する。
2. Mroonga をインストールする。
3. テーブルを Mroonga にする。
4. 全文検索用の Index を作成する。
5. 全文検索用の SQL を組み立てる。
6. 壊れるとは？

### 1. カーネルパラメーターを調整する。
Mroonga さんはメモリを多く使うのですが、中でもたまにがっつり掴みに行きます。
大抵は本当には使わないのですが、 mmap がちょっと桁の違う数字を言い出すので
overcommit_memory の設定が必要です。
やらないと、 oom_killer さんが発動して、Mroonga のテーブルをぶっ壊してくれます。

設定方法は、公式のここ読む。
http://groonga.org/ja/docs/reference/tuning.html#vm-overcommit-memory
※ これ、Groonga で Mroonga じゃないじゃん。という人は Mroonga の中の人は Groonga だと覚えておこう。

vm.overcommit_memory = 1 は必須。
vm.max_map_count = 131072 はデーターとメモリーに応じて増やしてください。
7.22.3.1. の limits の nofile 設定はもっとあけても良い気がします。
※ CentOS 6 でも掘れば見てくれる、7なら標準でここ。

```

if  [ ! -d /etc/sysctl.d ]; then
  mkdir -p /etc/sysctl.d
fi
cat << '__EOF__' >/etc/sysctl.d/60-groonga.conf
vm.overcommit_memory = 1
vm.max_map_count = 131072
__EOF__

sysctl --system

vi /etc/security/limits.conf

* soft nofile 65535
* hard nofile 65535

```
なお、Mroonga/Groonga での「推奨」とは、「いいからやれ」。
「強く推奨」は「いいから今すぐやれ」と置き換えると理解しやすいです。

あと、 vm.swappiness = 0 に設定すると oom_killer さんが発動するので、 1 以上の値にしましょう。
https://access.redhat.com/ja/node/1433243


### 2. Mroonga をインストールする。

公式のドキュメント通りに。
http://mroonga.org/ja/docs/install.html
世界一綺麗かつ簡潔に「日本語」で書いてある。圧倒的感謝！

MySQLの後からインストールする際は、 MySQL の root アカウントのパスワードを一旦消す。

```
mysql -uroot -p -D mysql -e "SET password for root@localhost=password(''); FLUSH PRIVILEGES; FLUSH TABLES;"
```
か、後から 

```
mysql -uroot -p < /usr/share/mroonga/uninstall.sql
mysql -uroot -p < /usr/share/mroonga/install.sql
```

を手動で実行してもOK。yum で警告でるのは大抵これ。よくコケる。
※ root の パスワードを消したら戻すのを忘れない事。


### 3. テーブルを Mroonga にする。
新規にテーブルを作る際：
```
CREATE TABLE テーブル名 ( ～ ) ENGINE=mroonga;
```

既存のテーブルを Mroonga に変える際：
```
ALTER TABLE テーブル名 ENGINE Mroonga;
```
これでデフォルトの ストレージモード。 parser が TokenBigram で出来ます。

### 4. 全文検索用の Index を作成する。

```
CREATE FULLTEXT INDEX インデックス名 ON テーブル名 (フィールド名) COMMENT 'normalizer "NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark"';
```

これで全文検索用の Index を作成します。normalizer と長い名前が何かは調べてみてください、面白いですよ。


### 5. 全文検索用の SQL を組み立てる。

今まで、
```
SELECT * FROM テーブル名 WHERE フィールド名 LIKE '%検索文字1%' AND フィールド名 LIKE '%検索文字2%';
```
としていたものを、
```
SELECT * FROM テーブル名 WHERE MATCH(フィールド名) AGAINST('*D+ 検索文字1 検索文字2' in boolean mode);
```
で検索して、期待する結果になっている事を確認する。
期待通りであれば、これで実装を考える。

期待通りでなければ、AGAINST の D プラグマの種類を調べる。
http://mroonga.org/ja/docs/reference/full_text_search/boolean_mode.html
normalizer （統一化）の種類を調べる。（この文字とこの文字は同じ扱いにしてくれる。「あ」「ア」等）
parser の種類を調べる。（文中よりどこで切って単語とするか）


### 6. 壊れるとは？
ざっくり分類すると、このパターンを経験しました。

A. INSERT できずに固まっている。
B. Index だけが壊れて、期待するレコードを返さなくなる。IGNORE INDEX だと返す。
C. Unique 制約が壊れて、重複してはいけないものが重複して入る。
D. テーブルに触った瞬間に固まって何も出来ない。

#### A.は、lock_clear で強制解除。
http://groonga.org/ja/docs/reference/commands/lock_clear.html

```
SELECT mroonga_command('clearlock');
```

> 注釈 これは危険なコマンドです。他のプロセスまたは他のスレッドが対象オブジェクトに書き込み処理を実行している間はこのコマンドを使ってはいけません。もし使ったなら、データベースは壊れるかもしれませんし、実行中のプロセスはクラッシュするかもしれません。

なんて脅し文句が書いてありますが、大抵事実です。実行時によくぶっ壊れます。
たまに壊れていないように見える事もあるかも知れませんが、検知できていないだけの可能性も大いにあります。
そもそも、クラッシュ時に更新してたテーブルはまず壊れます。

#### B,C は Index 回りがおかしくなったパターン。

Unique Index 以外は、
```
ALTER TABLE テーブル名 DISABLE KEYS;
ALTER TABLE テーブル名 ENABLE KEYS;
```
で直るとの話もありますが、古い Mroonga でこれを行っていた際に、ひどい目を見たのでやっていません。

```
ALTER TABLE テーブル名 ENGINE Mroonga;
```
で修復をかけると、Unique 違反でなければ直せます。
（修復中 SELECT 以外が発生すると悲惨です）

ほとんどの場合、
```
CREATE TABLE テーブル名2 ～
```
で元と同じ構造で作り、

```
INSERT INTO テーブル名2 SELECT * FROM テーブル名 ORDER BY id;
```
で流し込み直します。
Unique が重複していると分かった際は、テーブル名2 の CREATE 文で Unique を外して index にし、探し出して調整します。

```
ALTER TABLE テーブル名 RENAME テーブル名_org;
ALTER TABLE テーブル名2 RENAME テーブル名;
```
で切り替えます。  （この間、サービスはほぼ動かしたままです。）
最近の Mroonga ではだいぶ壊れなくなったと思います。

#### D. 諦めた。
Index 壊す再現テスト中で発生。
```
grndb recover 
```
でも一緒。
テーブル単位ではなく、データーベースごと作り直しが必要だった。
最悪全滅も想定して、元データーは InnoDB や別DBからのリカバリーが出来るようにしておく事。


# まとめ:
Mroonga を使うには、

とにかくメモリを積む！
最新の Mroonga/Groonga を使用する！
壊れるパターンを掴んで検知！
修復する準備！

が決め手です。

あと、やっぱり速いです。
しかも前提さえ満たせば簡単ですし。

Bug とか怪しい挙動の再現手順をコミュニティに投げれば、これまた対応が速いです。
それが……**日本語で出来る！！！**

おまけ:
同時に更新しないテーブルと、参照側は壊れないんだよなぁ～。
あ、という事は、Master = InnoDB, Slave = Mroonga 構成は良いのかも知れない。