rabbit-slide-kou-mysql-and-postgresql-and-japanese-full-text-search-3 2016.9.29.1 → 2016.9.29.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 14fab6ad8003dcdfd675192e5fc15bfb61d8462b
4
- data.tar.gz: e48e5ae1d5449193521dde73413b41e9ed89a645
3
+ metadata.gz: 8a7e9133fb0379251b27f937ccc07b01bba05d69
4
+ data.tar.gz: cfa0b94b9792e68a52c042971842e96069a2c7df
5
5
  SHA512:
6
- metadata.gz: 7f7891adc7087d18153dc69b698fddcadda9244054743426ace0546f3668d48531c77a26f00ca8f6768d5218be6e16ab439b266732ac3ae6540d02775e04615d
7
- data.tar.gz: a6328c347ec533f0a794b11bd114b8032a6466f6d5ab559dbe6b2a0280a95f930833ed4a85c1b4f9b2df148e3ed42c9adba346af0e771c57612ffa1d331f9aa6
6
+ metadata.gz: e725f5fe183960883fe75810123dca8f27085cfe2fb175c313989bf7c6066de560809d2cf2d3ba7ba72a9c340de592bd5ad4e732e749d904740e551b193aec4e
7
+ data.tar.gz: 3ccbc9da173beb65a24c78bd105ca2c3a868eac50e331e11acdfd5115a28b7dcb13843fcbe24c4329399a94deb6c35af0e12319bf27364dd0ec38c759a4474f8
@@ -9,7 +9,7 @@ tags:
9
9
  - postgresql
10
10
  - full-text-search
11
11
  presentation_date: 2016-09-29
12
- version: 2016.9.29.1
12
+ version: 2016.9.29.2
13
13
  licenses:
14
14
  - CC BY 3.0
15
15
  - CC BY-SA 4.0
@@ -43,7 +43,7 @@
43
43
 
44
44
  * Redmine
45
45
  * チケット管理システム
46
- * Ruby on Redmineを使用
46
+ * Ruby on Railsを使用
47
47
  * Zulip
48
48
  * チャットツール
49
49
  * Djangoを使用
@@ -174,7 +174,7 @@ MySQL + Mroongaのケース
174
174
  class Issue
175
175
  # この後にロールバックされることがあるのでカンペキではない
176
176
  # 再度同じチケットを更新するかデータを入れ直せば直る
177
- after_safe do |record|
177
+ after_save do |record|
178
178
  fts_record =
179
179
  FtsIssue.find_or_initialize_by(issue_id: record.id)
180
180
  fts_record.subject = record.subject
@@ -0,0 +1,205 @@
1
+ # [Groonga] MySQLとPostgreSQLと日本語全文検索3:MroongaとPGroongaの導入方法例 #mypgft
2
+
3
+ 2016年9月29日(肉の日!)に「[MySQLとPostgreSQLと日本語全文検索3](https://groonga.doorkeeper.jp/events/50541)」というイベントを開催しました。その名の通りMySQLとPostgreSQLでの日本語全文検索についての話題を扱うイベントです。今回も[DMM.comラボ](http://labo.dmm.com/)さんに会場を提供してもらいました。
4
+
5
+ [2月9日に開催した1回目のイベント](20160209)では[Mroonga](http://mroonga.org/ja/)・[PGroonga](https://pgroonga.github.io/ja/)については次の2つのことについて紹介しました。
6
+
7
+ * Mroonga・PGroongaが速いということ
8
+ * Mroonga・PGroongaの使い方
9
+
10
+ [6月9日に開催した2回目のイベント](20160609)ではMroonga・PGroongaについては次の2つのことについて紹介しました。
11
+
12
+ * Mroonga・PGroongaのオススメの使い方
13
+ * レプリケーションまわり
14
+
15
+ 今回はMroonga・PGroongaについては次のことについて紹介しました。
16
+
17
+ * [Redmine](http://www.redmine.org/)・[Zulip](https://zulip.org/)にMroonga・PGroongaを導入する方法
18
+
19
+ <div class="rabbit-slide">
20
+ <iframe src="https://slide.rabbit-shocker.org/authors/kou/mysql-and-postgresql-and-japanese-full-text-search-3/viewer.html"
21
+ width="640" height="524"
22
+ frameborder="0"
23
+ marginwidth="0"
24
+ marginheight="0"
25
+ scrolling="no"
26
+ style="border: 1px solid #ccc; border-width: 1px 1px 0; margin-bottom: 5px"
27
+ allowfullscreen> </iframe>
28
+ <div style="margin-bottom: 5px">
29
+ <a href="https://slide.rabbit-shocker.org/authors/kou/mysql-and-postgresql-and-japanese-full-text-search-3/" title="Mroonga・PGroonga導入方法">Mroonga・PGroonga導入方法</a>
30
+ </div>
31
+ </div>
32
+
33
+ 関連リンク:
34
+
35
+ * [スライド(Rabbit Slide Show)](https://slide.rabbit-shocker.org/authors/kou/mysql-and-postgresql-and-japanese-full-text-search-3/)
36
+ * [スライド(SlideShare)](http://www.slideshare.net/kou/mysql-and-postgresql-and-japanese-full-text-search-3)
37
+ * [リポジトリー](https://github.com/kou/rabbit-slide-kou-mysql-and-postgresql-and-japanese-full-text-search-3)
38
+
39
+ ## Redmineへの導入方法
40
+
41
+ RedmineへのMroonga・PGroongaの導入方法を説明します。RedmineはRuby on Railsを利用しているのでRuby on Railsを使っているアプリケーションに導入する例ということになります。
42
+
43
+ [redmine\_full\_text\_searchプラグイン](20160411)を使うとRedmineでMroongaまたはPGroongaを使って全文検索できるようになります。
44
+
45
+ このプラグインを使うとRedmineの全文検索が高速になります。たとえば、クリアコードで使っているRedmineには3000件くらいのチケットがありますが、その環境では次のように高速になりました。
46
+
47
+ <table>
48
+ <thead>
49
+ <tr>
50
+ <th>プラグイン</th>
51
+ <th>時間</th>
52
+ </tr>
53
+ </thead>
54
+ <tbody>
55
+ <tr>
56
+ <td>なし</td>
57
+ <td>467ms</td>
58
+ </tr>
59
+ <tr>
60
+ <td>あり</td>
61
+ <td>93ms</td>
62
+ </tr>
63
+ </tbody>
64
+ </table>
65
+
66
+ 200万件のチケットがある環境でも約380msで検索できているという報告もあります。
67
+
68
+ <blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">200万チケット<a href="https://twitter.com/MySQL">@MySQL</a>でやってみたよ。検索時間は約380ms。 <a href="https://twitter.com/hashtag/Redmine?src=hash">#Redmine</a> の未来が広がって嬉しいな。ありがたいな。/Redmineで高速に全文検索する方法 - ククログ(2016-04-11) <a href="https://t.co/s7FA4gSThu">https://t.co/s7FA4gSThu</a> <a href="https://twitter.com/_clear_code">@_clear_code</a></p>&mdash; Kuniharu AKAHANE (@akahane92) <a href="https://twitter.com/akahane92/status/733832496945594368">2016年5月21日</a></blockquote>
69
+ <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
70
+
71
+ ### Mroongaを導入する方法
72
+
73
+ Mroongaはトランザクションに対応していないのでトランザクションが必須のRedmineに組み込む場合はひと工夫必要になります。単純に、`ALTER TABLE table ENGINE=Mroonga ADD FULLTEXT INDEX (column)`とするわけにはいきません。
74
+
75
+ ではどうするかというと別途全文検索用のテーブルを作成して元のテーブルとは`JOIN`できるようにします。(他にもレプリケーションしてレプリケーション先をMroongaにするという[2回目のイベントで紹介した方法](20160609)もありますが、プラグインでやるには大掛かりなのでこの方法を使っています。)
76
+
77
+ マイグレーションファイルでいうと次のようにします。ここでは`issues`テーブル用の全文検索用のテーブルを作成しています。
78
+
79
+ ```ruby
80
+ def up
81
+ create_table(:fts_issues, # 全文検索用テーブル作成
82
+ id: false, # idは有効・無効どっちでも可
83
+ options: "ENGINE=Mroonga") do |t|
84
+ t.belongs_to :issue, index: true, null: false
85
+ t.string :subject, default: "", null: false
86
+ t.text :description, limit: 65535, null: false
87
+ t.index [:subject, :description], type: "fulltext"
88
+ end
89
+ end
90
+ ```
91
+
92
+ 全文検索用のテーブルには元のデータをコピーする必要があります。マイグレーション時には既存のデータを一気にコピーします。そのため、本当のマイグレーションの内容は次のようになります。データコピー後にインデックスを追加するようにしているのはそっちの方が速いからです。
93
+
94
+ ```ruby
95
+ def up
96
+ create_table(:fts_issues, # 全文検索用テーブル作成
97
+ id: false, # idは有効・無効どっちでも可
98
+ options: "ENGINE=Mroonga") do |t|
99
+ t.belongs_to :issue, index: true, null: false
100
+ t.string :subject, default: "", null: false
101
+ t.text :description, limit: 65535, null: false
102
+ end
103
+ execute("INSERT INTO " + # データをコピー
104
+ "fts_issues(issue_id, subject, description) " +
105
+ "SELECT id, subject, description FROM issues;")
106
+ add_index(:fts_issues, [:subject, :description],
107
+ type: "fulltext") # 静的インデックス構築(速い)
108
+ end
109
+ ```
110
+
111
+ このテーブルのモデルは次のようになります。
112
+
113
+ ```ruby
114
+ class FtsIssue < ActiveRecord::Base
115
+ # 実際はissue_idカラムは主キーではない。
116
+ # 主キーなしのテーブルなので
117
+ # Active Recordをごまかしているだけ。
118
+ self.primary_key = :issue_id
119
+ belongs_to :issue
120
+ end
121
+ ```
122
+
123
+ Mroonga導入後に更新されたデータはアプリケーション(Redmine)側でデータをコピーします。Active Recordの`after_save`フックを利用します。Mroongaがトランザクションをサポートしていないため、ロールバックのタイミングによってはデータに不整合が発生することがありますが、再度保存すれば復旧できることとそれほどロールバックは発生しないため、実運用時には問題になることはないでしょう。
124
+
125
+ ```ruby
126
+ class Issue
127
+ # この後にロールバックされることがあるのでカンペキではない
128
+ # 再度同じチケットを更新するかデータを入れ直せば直る
129
+ after_save do |record|
130
+ fts_record =
131
+ FtsIssue.find_or_initialize_by(issue_id: record.id)
132
+ fts_record.subject = record.subject
133
+ fts_record.description = record.description
134
+ fts_record.save!
135
+ end
136
+ end
137
+ ```
138
+
139
+ 全文検索時は全文検索用のテーブルを`JOIN`して`MATCH AGAINST`を使います。
140
+
141
+ ```ruby
142
+ issue.
143
+ joins(:fts_issue).
144
+ where(["MATCH(fts_issues.subject, " +
145
+ "fts_issues.description) " +
146
+ "AGAINST (? IN BOOLEAN MODE)",
147
+ # ↓デフォルトANDで全文検索
148
+ "*D+ #{keywords.join(', ')}"])
149
+ ```
150
+
151
+ この説明はわかりやすさのために[実際の実装](https://github.com/okkez/redmine_full_text_search)を単純化しています。詳細が知りたい方は実装を確認してください。
152
+
153
+ ### PGroongaを導入する方法
154
+
155
+ PGroongaはトランザクションに対応しているので別途全文検索用のテーブルを作成する必要はありません。既存のテーブルに全文検索用のインデックスを作成します。
156
+
157
+ マイグレーションファイルでいうと次のようにします。ここでは`issues`テーブルに全文検索用のインデックスを作成しています。`enable_extension("pgroonga")`はPGroongaを使えるようにするためのSQLです。
158
+
159
+ ```ruby
160
+ def up
161
+ enable_extension("pgroonga")
162
+ add_index(:issues,
163
+ [:id, :subject, :description],
164
+ using: "pgroonga")
165
+ end
166
+ ```
167
+
168
+ あとは検索時に全文検索条件をつけるだけです。
169
+
170
+ ```ruby
171
+ issue.
172
+ # 検索対象のカラムごとに
173
+ # クエリーを指定
174
+ where(["subject @@ ? OR " +
175
+ "description @@ ?",
176
+ keywords.join(", "),
177
+ keywords.join(", ")])
178
+ ```
179
+
180
+ この説明もわかりやすさのために[実際の実装](https://github.com/okkez/redmine_full_text_search)を単純化しています。詳細が知りたい方は実装を確認してください。
181
+
182
+ ## Zulipへの導入方法
183
+
184
+ ZulipへのPGroongaの導入方法を説明します。ZulipはPostgreSQLを使っているので、導入するのはPGroongaだけです。ZulipはDjangoを使っているのでDjangoを使っているアプリケーションに導入する例ということになります。
185
+
186
+ Zulipはチャットツールです。チャットツールなので小さなテキストの書き込みが頻繁に発生する傾向があります。各書き込みは十分速く完了する必要があります。書き込みが遅いとユーザーの不満が溜まりやすいからです。
187
+
188
+ Zulipは書き込みをできるだけ速くするためにインデックスの更新を遅延させています。インデックスの更新はデータの追加よりも重い処理なので、その処理を後回しにしているということです。(PGroongaは検索だけでなく更新も速いので遅延させずにリアルタイムで更新しても十分速いかもしれません。アプリケーションの要件次第でどのような実装にするか検討する必要があります。)
189
+
190
+ Zulipは、インデックスの更新を遅延させるため、カラムの値を直接全文検索対象にせずに、別途全文検索用のカラムを用意しています。その全文検索用のカラムの更新を後回しにすることでインデックスの更新を遅延させています。
191
+
192
+ マイグレーションファイルでいうと次のようにします。最初の`ALTER ROLE`はPGroongaが提供する`@@`という全文検索用のオペレーターの優先順位を調整するためのものです。本質ではないのでここでは気にしなくて構いません。
193
+
194
+ ```python
195
+ migrations.RunSQL("""
196
+ ALTER ROLE zulip SET search_path
197
+ TO zulip,public,pgroonga,pg_catalog;
198
+ ALTER TABLE zerver_message
199
+ ADD COLUMN search_pgroonga text;
200
+ UPDATE zerver_message SET search_pgroonga =
201
+ subject || ' ' || rendered_content;
202
+ CREATE INDEX pgrn_index ON zerver_message
203
+ USING pgroonga(search_pg$roonga);
204
+ """, "...")
205
+ ```
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rabbit-slide-kou-mysql-and-postgresql-and-japanese-full-text-search-3
3
3
  version: !ruby/object:Gem::Version
4
- version: 2016.9.29.1
4
+ version: 2016.9.29.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - 須藤功平
@@ -40,6 +40,7 @@ files:
40
40
  - images/zulip-full-text-search-form.png
41
41
  - images/zulip-highlight.png
42
42
  - mroonga-and-pgroonga.rab
43
+ - mypgft3.md
43
44
  - pdf/mysql-and-postgresql-and-japanese-full-text-search-3-mroonga-and-pgroonga.pdf
44
45
  homepage: http://slide.rabbit-shocker.org/authors/kou/mysql-and-postgresql-and-japanese-full-text-search-3/
45
46
  licenses: