rabbit-slide-kou-postgresql-conference-2015 2015.11.27.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,661 @@
1
+ = PGroongaの実装
2
+
3
+ : subtitle
4
+ (('note:ぴーじーるんがのじっそう'))
5
+ : author
6
+ 須藤功平
7
+ : institution
8
+ 株式会社クリアコード
9
+ : content-source
10
+ PostgreSQLカンファレンス2015
11
+ : date
12
+ 2015-11-27
13
+ : allotted-time
14
+ 40m
15
+ : theme
16
+ .
17
+
18
+ = (建前の)目的
19
+
20
+ PostgreSQLのamindexを説明
21
+
22
+ = 方法
23
+
24
+ PGroongaの実装を紹介\n
25
+ (('note:PGroongaはamindexとして実装されているため'))\n
26
+ (('note:ただし、ヒントだけなので詳細はPGroongaのソースを参照'))\n
27
+ (('note:https://github.com/pgroonga/pgroonga'))
28
+
29
+ = 本当の目的
30
+
31
+ PGroongaの自慢
32
+
33
+ = PGroongaとは
34
+
35
+ amindexの一種
36
+
37
+ = amindex
38
+
39
+ 索引の実装を\n
40
+ PostgreSQLに\n
41
+ 追加する仕組み
42
+
43
+ = amindexの使い方
44
+
45
+ # coderay sql
46
+ CREATE INDEX name ON table
47
+ USING pgroonga (column);
48
+
49
+ (('tag:center'))
50
+ 組み込みの索引と同じ\n
51
+ ((({USING}))を指定するだけ)
52
+
53
+ = PGroongaの提供機能
54
+
55
+ 索引を使った\n
56
+ ((*超高速な*))\n
57
+ 全文検索機能
58
+
59
+ = PostgreSQLと全文検索
60
+
61
+ 課題あり
62
+
63
+ = PostgreSQLと全文検索1
64
+
65
+ # coderay sql
66
+ CREATE INDEX name ON table
67
+ USING gin (to_tsvector('english', column));
68
+ SELECT * FROM table
69
+ WHERE to_tsvector('english', column) @@ '...';
70
+
71
+ (('tag:center'))
72
+ 組み込みの全文検索機能\n
73
+ 日本語非対応\n
74
+ (('note:http://www.postgresql.org/docs/current/static/textsearch.html'))
75
+
76
+ = PostgreSQLと全文検索2
77
+
78
+ # coderay sql
79
+ CREATE INDEX name ON table
80
+ USING gin (column gin_trgm_ops);
81
+ SELECT * FROM table
82
+ WHERE column % '...';
83
+
84
+ (('tag:center'))
85
+ contrib/pg_trgm\n
86
+ 日本語非対応\n
87
+ (('note:http://www.postgresql.org/docs/current/static/pgtrgm.html'))
88
+
89
+ = PostgreSQLと全文検索
90
+
91
+ 日本語非対応
92
+
93
+ = PGroongaとPostgreSQL
94
+
95
+ 日本語対応!
96
+
97
+ = PGroongaを使う
98
+
99
+ # coderay sql
100
+ CREATE INDEX name ON table
101
+ USING pgroonga (column);
102
+ SELECT * FROM table
103
+ WHERE column @@ '全文検索';
104
+
105
+ (('tag:center'))
106
+ 日本語対応!
107
+
108
+ = しかも速い!
109
+
110
+ = ヒット数と検索時間
111
+
112
+ # RT
113
+ delimiter = [|]
114
+
115
+ ヒット数 | 検索時間
116
+
117
+ 368 | 0.030s((' '))
118
+ 17,172 | 0.121s((' '))
119
+ 22,885 | 0.179s((' '))
120
+ 625,792 | 0.646s(*)
121
+
122
+ (('note:(*) work_memを10MBに増やしている'))
123
+
124
+ (('tag:center'))
125
+ データ:Wikipedia日本語版\n
126
+ (('note:約184万レコード・平均サイズ約3.8KB'))\n
127
+ (('note:詳細:http://www.clear-code.com/blog/2015/5/25.html'))
128
+
129
+ = 検索時間:比較
130
+
131
+ # RT
132
+ delimiter = [|]
133
+
134
+ ヒット数 | PGroonga | pg_bigm
135
+
136
+ 368 | 0.030s(!) | 0.107s((' '))
137
+ 17,172 | 0.121s(!) | 1.224s((' '))
138
+ 22,885 | 0.179s(!) | 2.472s((' '))
139
+ 625,792(*) | 0.646s((' ')) | 0.556s(!)
140
+
141
+ (('note:(*) 他は検索語が3文字以上でこれだけ2文字'))
142
+
143
+ (('tag:center'))
144
+ PGroongaは安定して速い!
145
+
146
+ = なぜ速いのか
147
+
148
+ バックエンドが\n
149
+ 外部の本格的な\n
150
+ 全文検索\n
151
+ ライブラリー
152
+
153
+ = amindexでのポイント
154
+
155
+ * (({_PG_init()}))
156
+ * ライブラリーを初期化
157
+ * (({on_proc_exit()}))
158
+ * 後始末
159
+
160
+ = 全文検索ライブラリー
161
+
162
+ Groonga
163
+
164
+ = Groonga
165
+
166
+ ((*本格的な*))\n
167
+ 全文検索\n
168
+ エンジン\n
169
+ (('note:サーバーとしてもライブラリーとしても使える'))
170
+
171
+ = 本格的な例1
172
+
173
+ (('tag:center'))
174
+ 長い文書でも検索性能が落ちない
175
+
176
+ (('tag:margin-top * 2'))
177
+ この特徴が有用なサービス例:
178
+
179
+ * Wiki
180
+ * イメージ:Wikipedia
181
+ * ドキュメント検索
182
+ * イメージ:ファイルサーバー検索
183
+
184
+ = 長い文書でも速い理由
185
+
186
+ ((*完全*))\n
187
+ 転置索引
188
+
189
+ = 転置索引
190
+
191
+ * 完全:位置情報あり
192
+ * Groonga
193
+ * 無印:位置情報なし
194
+ * GIN
195
+ * Groonga(('note:(必要なければ入れないことができる)'))
196
+
197
+ = 転置索引の違い
198
+
199
+ # image
200
+ # src = images/inverted-index.svg
201
+ # relative_height = 100
202
+
203
+ = 索引での検索の違い
204
+
205
+ # image
206
+ # src = images/search-with-inverted-index.svg
207
+ # relative_height = 100
208
+
209
+ = 検索速度の違い
210
+
211
+ * 完全転置索引:安定して速い
212
+ * 索引だけで検索完了
213
+ * 転置索引:速さが安定しない
214
+ * 索引での検索+全件スキャン
215
+ * 候補文書が多い・長い→遅くなる
216
+
217
+ = amindexでのポイント
218
+
219
+ * 全件スキャンを無効にする
220
+ * (({scan->xs_recheck = false}))
221
+ * (({tbm_add_tuples(..., false)}))
222
+
223
+ = 本格的な例2
224
+
225
+ (('tag:center'))
226
+ 常時更新・常時検索に強い
227
+
228
+ (('tag:margin-top * 2'))
229
+ この特徴が有用なサービス例:
230
+
231
+ * SNS
232
+ * イメージ:Twitter
233
+ * ナレッジ共有サービス
234
+ * イメージ:Qiita・teratail
235
+
236
+ = 常時更新・検索に強い?
237
+
238
+ 更新中も\n
239
+ 参照性能が\n
240
+ 落ちない
241
+
242
+ = 落ちない理由
243
+
244
+ 更新時に\n
245
+ 参照ロックなし
246
+
247
+ = 参照ロック
248
+
249
+ 獲得したら\n
250
+ 他の処理は\n
251
+ 参照不可になる\n
252
+ ロック
253
+
254
+ = GINと更新と参照
255
+
256
+ # image
257
+ # src = images/read-while-write-gin.svg
258
+ # relative_height = 95
259
+
260
+ = Groongaと更新と参照
261
+
262
+ # image
263
+ # src = images/read-while-write-groonga.svg
264
+ # relative_height = 95
265
+
266
+ = PGroongaと更新と参照
267
+
268
+ # image
269
+ # src = images/read-while-write-pgroonga.svg
270
+ # relative_height = 95
271
+
272
+ = 参照ロックフリーの実現
273
+
274
+ # image
275
+ # src = images/reference-lock-free-idea.svg
276
+ # relative_height = 100
277
+
278
+ = amindexでのポイント
279
+
280
+ * (({Lock*()}))(('note:(*)'))を呼ばない\n
281
+ (('note:(*) PostgreSQL提供のロックAPI'))
282
+ * 呼ぶとGroongaの参照ロックフリーの有意性を殺してしまう
283
+
284
+ = 本格的な例3
285
+
286
+ (('tag:center'))
287
+ 継続的な更新に強い
288
+
289
+ (('tag:margin-top * 2'))
290
+ この特徴が有用なサービス例:
291
+
292
+ * SNS
293
+ * イメージ:Twitter・Facebook
294
+ * チャット
295
+ * イメージ:Slack
296
+
297
+ = 継続的な更新に強い?
298
+
299
+ * 間欠的な性能劣化がない
300
+ * 更新も検索も
301
+ * GINは両方ある
302
+ * (({FASTUPDATE}))を無効にしていない場合
303
+
304
+ = 間欠的性能劣化がない理由
305
+
306
+ * 常に最新ポスティングリストを\n
307
+ 維持しているから
308
+ * 更新負荷が高くならない対策入り\n
309
+ (('note:https://github.com/groonga/groonga/wiki/Memo'))
310
+ * GINは維持をサボって高速化
311
+ * サボったつけを払うときに性能劣化
312
+ * 例:検索時・更新が溜まりすぎた時
313
+
314
+ = 本格的な例4
315
+
316
+ (('tag:center'))
317
+ 索引の作り直しが速い
318
+
319
+ (('tag:margin-top * 2'))
320
+ この特徴が有用なケース:
321
+
322
+ * ダンプのリストア
323
+ * サービス復旧
324
+
325
+ = 索引作成
326
+
327
+ # RT
328
+ delimiter = [|]
329
+
330
+ 元データの\nロード時間 | 索引\n作成時間
331
+
332
+ 16分31秒 | 25分37秒
333
+
334
+ (('tag:center'))
335
+ データ:Wikipedia日本語版\n
336
+ (('note:約184万レコード・平均サイズ約3.8KB'))\n
337
+ (('note:詳細:http://www.clear-code.com/blog/2015/5/25.html'))
338
+
339
+ = 索引作成:比較
340
+
341
+ # RT
342
+ delimiter = [|]
343
+
344
+ PGroonga | pg_bigm
345
+
346
+ 25分37秒 | 5時間56分15秒
347
+
348
+ (('tag:center'))
349
+ pg_bigmより約14倍速い!
350
+
351
+ = 速い理由
352
+
353
+ ((*静的*))\n
354
+ 索引構築を\n
355
+ サポート
356
+
357
+ = 索引構築方法
358
+
359
+ * ((*動的*))索引構築
360
+ * 構築中も完了した分は検索可能
361
+ * 即時反映可・一括登録不向き
362
+ * ((*静的*))索引構築
363
+ * 構築完了まで使えないが速い\n
364
+ (('note:(処理時間は入力に比例。指数関数的ではない。)'))
365
+ * 即時反映不可・一括登録向き
366
+
367
+ = SQLの違い
368
+
369
+ # coderay sql
370
+ -- 動的索引構築
371
+ CREATE INDEX ...;
372
+ INSERT ...;
373
+ -- 静的索引構築
374
+ INSERT ...;
375
+ CREATE INDEX ...;
376
+
377
+ = amindexでのポイント
378
+
379
+ * (({aminsert()}))
380
+ * 動的索引構築を実装
381
+ * (({ambuild()}))
382
+ * 静的索引構築を実装
383
+
384
+ = 速い理由のまとめ
385
+
386
+ * 索引だけで検索可能
387
+ * 更新中も検索可能
388
+ * 間欠的な性能劣化なし
389
+ * 静的索引構築をサポート
390
+
391
+ = 速さ以外の利点1
392
+
393
+ 便利な独自機能
394
+
395
+ = 独自機能1
396
+
397
+ 見慣れた\n
398
+ クエリー言語
399
+
400
+ = 例
401
+
402
+ # coderay sql
403
+ body @@ 'PostgreSQL OR MySQL -Oracle'
404
+
405
+ * Web検索エンジン互換
406
+ * →ユーザーの入力をそのまま使える
407
+ * デフォルトAND
408
+ * OR・-(除外)あり
409
+
410
+ = 独自機能2
411
+
412
+ 配列+全文検索
413
+
414
+ = 例
415
+
416
+ # coderay sql
417
+ CREATE TABLE logs (hosts text[]);
418
+ INSERT INTO logs
419
+ VALUES (Array['web', 'db']);
420
+ CREATE INDEX index ON logs
421
+ USING pgroonga (hosts);
422
+ SELECT * FROM logs
423
+ WHERE hosts @@ '各ホスト名を全文検索';
424
+
425
+ = 独自機能3
426
+
427
+ JSON+全文検索
428
+
429
+ = 例
430
+
431
+ # coderay sql
432
+ INSERT INTO logs (record)
433
+ VALUES ('{"host": "ダウンホスト"}'),
434
+ ('{"message": "シャットダウン"}');
435
+ SELECT * FROM logs
436
+ WHERE record @@ 'string @@ "ダウン"'
437
+ -- record
438
+ -- ----------------------------
439
+ -- {"host": "ダウンホスト"}
440
+ -- {"message": "シャットダウン"}
441
+
442
+ (('tag:center'))
443
+ JSON内の全テキストから全文検索
444
+
445
+ = 独自機能4
446
+
447
+ ノーマライザー
448
+
449
+ = ノーマライザー
450
+
451
+ * 文字を正規化するモジュール
452
+ * 表記の違いを吸収できる
453
+ * (('wait'))アルファベット:全部小文字
454
+ * (('wait'))ひらがな・カタカナ:全部全角
455
+ * (('wait'))㍉→ミリ
456
+ * (('wait'))UnicodeのNFKCベース
457
+
458
+ = 独自機能5
459
+
460
+ トークナイザー
461
+
462
+ = トークナイザー
463
+
464
+ * キーワード切り出しモジュール
465
+ * クエリーに指定できる\n
466
+ キーワードを調整
467
+ * 例:すもも|も|もも|も
468
+
469
+ = デフォルト:可変長Ngram
470
+
471
+ * 英語:字種区切り
472
+ * 例:Hello|World|!!!
473
+ * Bigramだとノイズが多い
474
+ * 日本語:Bigram
475
+ * 例:ポスグレ→ポス|スグ|グレ|レ
476
+ * 漏れがない
477
+
478
+ = 形態素解析器ベース
479
+
480
+ * MeCab:OSS
481
+ * 新語対応には辞書メンテが必要
482
+ * 参考:mecab-ipadic-neologd
483
+ * JMAT:商用製品
484
+ * ジャストシステム社製
485
+ * ATOKでも使っている辞書を提供
486
+ * 新語にも強い\n
487
+ (('note:参考:「JMAT Groonga Tokenizer Talks」で検索'))
488
+
489
+ = amindexでのポイント
490
+
491
+ * (({amoptions()}))
492
+ * (({CREATE INDEX}))でのオプションを定義
493
+
494
+ # coderay sql
495
+ CREATE INDEX index ON memos
496
+ USING pgroonga (content)
497
+ WITH (normalizer = 'NormalizerMySQLUnicodeCI',
498
+ tokenizer = 'TokenMecab');
499
+
500
+ = 速さ以外の利点2
501
+
502
+ 見慣れた機能\n
503
+ (('note:B-tree・GINの代わりに使える'))
504
+
505
+ = タグ検索
506
+
507
+ # coderay sql
508
+ CREATE TABLE memos (
509
+ tags varchar(1023)[]
510
+ );
511
+ CREATE INDEX index ON memos
512
+ USING pgroonga (tags);
513
+ SELECT * FROM memos
514
+ WHERE tags %% 'タグ';
515
+
516
+ = 範囲検索
517
+
518
+ # coderay sql
519
+ CREATE TABLE users (age int);
520
+ CREATE INDEX index ON users
521
+ USING pgroonga (age);
522
+ SELECT * FROM users
523
+ WHERE age < 20;
524
+
525
+ = Index Only Scan
526
+
527
+ * 索引がデータも返す
528
+ * テーブルにアクセスしないので高速
529
+ * PGroonga・B-tree:サポート
530
+ * GIN:未サポート
531
+
532
+ = amindexでのポイント
533
+
534
+ * (({amcanreturn()}))
535
+ * (({true}))を返す
536
+ * (({amgettuple()}))
537
+ * (({scan->xs_want_itup}))なら\n
538
+ (({scan->xs_itup}))にデータを設定
539
+
540
+ = LIKE
541
+
542
+ # coderay sql
543
+ CREATE INDEX index ON memos
544
+ USING pgroonga (content);
545
+ SELECT * FROM memos
546
+ WHERE content LIKE '%...%';
547
+
548
+ (('tag:center'))
549
+ 索引を使って高速検索\n
550
+ アプリケーションの変更不要
551
+
552
+ = マルチカラムインデックス
553
+
554
+ # coderay sql
555
+ CREATE INDEX index ON memos
556
+ USING pgroonga (title, content);
557
+ SELECT * FROM memos
558
+ WHERE title @@ '...' AND
559
+ content @@ '...';
560
+
561
+ (('tag:center'))
562
+ (('note:titleでもcontentでもマッチ!'))\n
563
+ (('note:と書けないのでそんなにうれしくない'))
564
+
565
+ = 設計ミス
566
+
567
+ = (({text @@ text}))
568
+
569
+ (('tag:center'))
570
+ 組み込みの定義と競合\n
571
+ (('note:ts_vector @@ ts_queryにキャストされる'))
572
+
573
+ (('tag:margin-top * 2'))
574
+ 回避方法:
575
+
576
+ # coderay sql
577
+ ALTER DATABASE name
578
+ SET search_path = '$user',public,pgroonga,pg_catalog;
579
+
580
+ = (({jsonb @@ text}))
581
+
582
+ (('tag:center'))
583
+ 全文検索にすればよかった
584
+
585
+ # coderay sql
586
+ jsonb @@ 'string @ "キーワード"'
587
+ -- ↓
588
+ jsonb @@ 'キーワード'
589
+
590
+ (('tag:center'))
591
+ (('note:今の細かい検索条件を指定できる機能は別演算子にする'))
592
+
593
+ = 今後
594
+
595
+ = もっとGroongaを活かす
596
+
597
+ # RT
598
+ delimiter = [|]
599
+
600
+ PGroonga | pg_bigm | Groonga
601
+
602
+ 0.646s | 0.556s | 0.085s(!)
603
+
604
+ (('note:ヒット数635,792、検索語は2文字'))
605
+
606
+ (('tag:center'))
607
+ 生Groongaは1桁速い!\n
608
+ (('note:詳細:https://github.com/groonga/wikipedia-search/issues/3'))
609
+
610
+ = 同義語展開サポート
611
+
612
+ # coderay sql
613
+ body @@ pgroonga.expand_query('ネジ')
614
+ -- ↓
615
+ body @@ 'ネジ OR ねじ OR ボルト'
616
+
617
+ (('tag:center'))
618
+ (('note:Groongaでは使える'))
619
+
620
+ = ステミングサポート
621
+
622
+ found/finds\n
623
+ ↓\n
624
+ find\n
625
+ (('note:Groongaでは使える'))
626
+
627
+ = text @@ pgroonga.query
628
+
629
+ # coderay sql
630
+ body @@ 'ポスグレ'::pgroonga.query
631
+
632
+ (('tag:center'))
633
+ (('note:組み込みの(({text @@ text}))との競合回避'))
634
+
635
+ = 重みサポート
636
+
637
+ # coderay sql
638
+ -- タイトルのほうが本文より10倍重要
639
+ body @@ ('title * 10 || body', 'ポスグレ')
640
+
641
+ (('tag:center'))
642
+ (('note:Groongaでは使える'))
643
+
644
+ = まとめ
645
+
646
+ * PGroongaは速い
647
+ * PGroongaは便利
648
+ * PGroongaには設計ミスがある
649
+ * PGroongaはもっと便利になる
650
+
651
+ (('wait'))
652
+ (('tag:center'))
653
+ PGroongaを使おう!
654
+
655
+ = おしらせ
656
+
657
+ * Groonga Meatup 2015\n
658
+ (('note:https://groonga.doorkeeper.jp/events/31482'))
659
+ * PGroongaの話題もアリ
660
+ * 多少空きアリ(('note:(定員を多少増やせる)'))
661
+ * 11月29日(日)13:30開始