Slide 1

Slide 1 text

ニジエチューニング 2023-12 2023/12/04 インフラボランティア:

Slide 2

Slide 2 text

あんただれ ● 名前 ○ ٩( ᐛ )( ᐖ )۶とか₍₍⁽⁽(◌ી( ・◡・ )ʃ)とか ○ 匿名ボードだとインちゃんと呼ばれてる ● インフラ・バックエンドのボランティアスタッフです ● バックエンドを手伝ってくれる人が増えて嬉しいです ● 2014/03/18にJoin ● 絵上手くなりたいです ● ニジエ手伝って9年経過してるのマジ驚く ● nijieinfraのXアカウントにも転職しませんか?みたいなのが来て驚く ○ 企業?アカウントにも送ってくるんだ・・・というか本業でない ● そろっと婚活したいきもする(ておくれ おひさしぶりです

Slide 3

Slide 3 text

だいぶたってるのでニジエインフラの現状と 直近やっていたことの紹介

Slide 4

Slide 4 text

ニジエで使ってるMWなど ● OS・管理・監視 ○ Ubuntu, teleport, Ansible, Prometheus, grafana, statsD, fluentd ● HTTP/Sトラフィック周辺 ○ hitch, Varnish, Apache, Nginx, keepalived ● App関連 ○ PHP, Python ● DataStore ○ MySQL, Memcached, Meilisearch ● メール関連 ○ Postfix ● 外部サービス ○ Github, Let’sEncrypt, slack, newrelic APM, GSLB, S3, R2, Cloudflare ● などなど 新しいなかま(MW)が増えた

Slide 5

Slide 5 text

サーバ台数 4GB 2GB 1GB メモリ1GB~4GBのサーバが計24台(k8sとかは使ってない)

Slide 6

Slide 6 text

なぜクラウドやコンテナではないのか ● 現状で月数万程度のインフラコストで動いている ● モダンな構成にすること自体は簡単だが様々な検討が必要 ○ コストが極端に上がらないことが必要 ○ コストが上がったとしても見合うメリットが必要 ● コストで問題がないか? ○ リソース(CPU/RAM/STORAGE/EGRESSなど)のバランスを見て選定が必要 ■ 特にニジエはトラフィックヘビーであり、 egressが高いとそれだけで辛い ○ 昨今の円安傾向から外貨建てのサービスは可能な限り避けたい ○ といったことを考えると積極的に載せ替える動機とならない ● メリットがあるか? ○ アダルトということもあり、突然 BANされても復旧が容易である必要がある ■ クラウド特有のマネージドサービスを使った場合でも他で動かせる検討が必要 ○ 負荷の増減で素早く ASができる/しやすいメリットがあるがキャッシュで吸収している ○ IaCについてもAnsibleで実現できており運用負荷を加味しても VM運用がバランスが良い なのでしばらくは現状構成でいく(一部はコンテナ使うかも )

Slide 7

Slide 7 text

みたいなことはポリシーを元に検討しています

Slide 8

Slide 8 text

インフラのポリシー これらを組み合わせて80%を目指してく

Slide 9

Slide 9 text

ちなみに80%を目指すというのは ニジエを手伝い初めて最初に書いたスライドで 触れていて大事にしている ニジエチューニング3月(2014/04/01公開)より

Slide 10

Slide 10 text

他にもポリシーはある ● 例えばキャッシュポリシー ○ そのデータはリアルタイムである必要があるのか? ○ そうでなければ遅延させることで負荷軽減できないか検討 ○ フラグメントキャッシュでキャッシュが強く掛かっていることをそこまで意識させない ○ など ● ポリシーを決めておくと何かしらの変更をする際にぶれなくていいのでおすすめ ○ 定期的にだったり大きな変更が必要な時に見直す

Slide 11

Slide 11 text

直近やっていたこと

Slide 12

Slide 12 text

直近?やっていたこと ● ストレージの構成変更 ● DB改善 ● 全文検索導入 ● bot対策 ● 認証改修 ● サムネ生成改修 ● コスト削減 ● CDN連携 ● 監視改善 ● などなど 他にもいろいろあるんですが

Slide 13

Slide 13 text

直近?やっていたこと ● ストレージの構成変更 ● DB改善 ● 全文検索導入 ● bot対策 ● 認証改修 ● サムネ生成改修 ● コスト削減 ● CDN連携 ● 監視改善 ● などなど 今回はこの3つを紹介します

Slide 14

Slide 14 text

ストレージの構成変更を簡単に言うと ● 重い投稿の改善 ● コスト削減のための R2投入 ● データをHot/Coldに整理してストレージ構成変更 簡単に書くと3行だが道のりは険しかった

Slide 15

Slide 15 text

ストレージの歴史 ● そもそもニジエが運用している各サービスは画像投稿を主とするのでどう保存するかは重要 ● そのため数回大規模な改修があり、ざっくり区分けすると4つに分けられる ● Gen1 試行錯誤時期(ごく初期) ○ ローカルに保存したり、s3のみとしたり、別建ての画像サーバ(以下pic)と試行錯誤していた ○ バックアップはない ● Gen2 pic+backup(S3)構成の確立 ○ picに保存しつつバックアップとしてS3、参照はpicのみ ■ 正直バックアップは信頼性がない(後述) ○ 投稿時にSFTPでpicに書き込み、同時にサムネ生成、S3書き込みをおこなっていた ○ 途中でサムネの動的生成ができるようになりサムネ生成を停止(Gen2.5ぐらい) ■ このあたりから自分が手伝い始めた ● Gen3 書き込み速度の改善・コード整理・信頼性向上 ○ バックアップの信頼性が担保できたので障害時にS3にrerouteするように ○ SFTPからwebdav、S3への書き込みを非同期に変更し書き込み速度を改善 ● Gen4 運用コスト削減・コード整理(現行) ○ S3からR2に変更 ○ 増え続けるpicを何とかするためにストレージクラスをHot/Cold/Backupに整理 ■ pic(Hot/RW)+R2(Cold/RW)+M-DISC(Backup/W) ○ 必ず2か所にデータがあるようにすることで信頼性と低コストを両立(pic+R2 / R2+M-DISC) ○ ファイルに関する処理を集約

Slide 16

Slide 16 text

ストレージの歴史 ● そもそもニジエが運用している各サービスは画像投稿を主とするのでどう保存するかは重要 ● そのため数回大規模な改修があり、ざっくり区分けすると4つに分けられる ● Gen1 試行錯誤時期(ごく初期) ○ ローカルに保存したり、s3のみとしたり、別建ての画像サーバ(以下pic)と試行錯誤していた ○ バックアップはない ● Gen2 pic+backup(S3)構成の確立 ○ picに保存しつつバックアップとしてS3、参照はpicのみ ■ 正直バックアップは信頼性がない(後述) ○ 投稿時にSFTPでpicに書き込み、同時にサムネ生成、S3書き込みをおこなっていた ○ 途中でサムネの動的生成ができるようになりサムネ生成を停止(Gen2.5ぐらい) ■ このあたりから自分が手伝い始めた ● Gen3 書き込み速度の改善・コード整理・信頼性向上 ○ バックアップの信頼性が担保できたので障害時にS3にrerouteするように ○ SFTPからwebdav、S3への書き込みを非同期に変更し書き込み速度を改善 ● Gen4 運用コスト削減・コード整理(現行) ○ S3からR2に変更 ○ 増え続けるpicを何とかするためにストレージクラスをHot/Cold/Backupに整理 ■ pic(Hot/RW)+R2(Cold/RW)+M-DISC(Backup/W) ○ 必ず2か所にデータがあるようにすることで信頼性と低コストを両立(pic+R2 / R2+M-DISC) ○ ファイルに関する処理を集約

Slide 17

Slide 17 text

先の3行の行間には このコード整理が詰まってる(つらい)

Slide 18

Slide 18 text

最初に強調しておきたいこと ● 個人的な意見ですが、動いているサービスにおいては常に改修が必要です ● 様々な制約によって改修のレベルがパッチ当てから式年遷宮のようなものまであります ● サービスに様々な機能が追加されていくにつれて他の改修のスピードについていけなかったパッチだ らけのコードが陳腐化していき (あまり好きな言葉ではないのですが) 技術的負債と呼ばれる ○ 一般的な話ですが、過去の経緯を無視してダメというのは喧嘩になるのでやめよう ● これらを踏まえてGen2は試行錯誤していた Gen1を整理したものであったがそのあとの改修に取り残 されて技術的負債となりつつあった ● Gen3も改修した後にもコスト等の問題があり、やはり技術的負債となりつつあった ● 改修後に当時は見えなかった問題や、優先度で先送りした問題が気になってくるのは当然 過去の経緯を無視せず敬意を持とう

Slide 19

Slide 19 text

Gen2の問題 ● 大規模改修のきっかけは投稿が重いという問い合わせからだった ○ picへの書き込みがSFTP ■ プロトコル的に重いのもそうですが、 1ファイル毎に接続を開いて ...(そりゃ遅い) ○ S3へのバックアップが投稿と同時 (sync) ■ リモートなので遅い ○ 特に漫画投稿などの枚数が多いと顕著に遅い ■ PHPなので並列化が困難 (最新だと状況変わってますが ) ● 他にも問題点はいろいろ存在 ○ バックアップが信頼できない ■ 呼び出し元がpic/S3の書き込みを直ハンドルするため S3が漏れているパスがある ○ 動的サムネも途中からなのでサムネ生成が投稿時に同時に行われているパスがある ■ 余計重い 問題たくさん

Slide 20

Slide 20 text

Gen3への改修 ● picへの書き込みをSFTPからwebdavへ変更 ○ プロトコル的に軽い ● S3へのバックアップは直接書きこまずにローカルに一旦保存して cronでアップロード ○ そもそもバックアップなので即時である必要がない ○ ローカルに保存しても pic+ローカルの2か所で保存されており片方死んでも復帰可能 ● 呼び出し元にあった各処理を全サービスに共通コードで提供 ○ 形式チェックや投稿処理を共通化して呼び出すように変更 ○ これで投稿の品質を一定に担保 Gen3の最大の成果はローカルを使った非同期書き込み

Slide 21

Slide 21 text

ローカルを使った非同期書き込み ● 現在のところ3種類に分けて管理している ○ put リモート書きこみ ○ delete リモート削除 ○ async_unlink pic削除 ● pic削除も非同期にしているのは即時に削除すると不都合があるものがあるため ○ 例えばプロファイル画像を消しても、旧画像参照が即止まるわけではない ■ 様々なページをESIキャッシュしておりその期間は残しておく必要がある ○ 退会時などに一気に削除しようとすると時間がかかる ● 実行タイミングはmtimeで管理 ○ 書き込み途中にflushされても困るのでmtimeから1分経過したファイルを対象としている ○ 削除する場合はmtimeに予定時刻を入れておいて、経過したタイミングで削除 ● 仮に失敗した場合でもそこで breakするので次のタイミングで再実行 ● 再起動などのタイミングでは強制で flushがかかるように ● 空のディレクトリが大量にできるので cronで定期削除している 仕組みは単純だけどこの規模だと安定している(Gen4も同じ仕組み ) 削除予定時刻(未来) 現在時刻

Slide 22

Slide 22 text

ちゃんと監視して妙に滞留してたら確認してます(今のところないですが)

Slide 23

Slide 23 text

共通コード化と参照reroute ● ファイルを書きこみをする際にはざっくりこんな処理がある ○ 形式チェック(投稿サイズ /フォーマットなど) ○ 書き込み先選定 ○ パス作成 ○ 書き込み ○ バックアップ ● 今まではこの処理をそれぞれの投稿処理で行っていたのでそれは漏れても仕方ない ● 書き込みとバックアップを同時に実行するコードを作成し既存コードを置き換えた ○ 先の非同期と合わせてバックアップが信頼できるようになった ● チェックロジックも共通化してブレが無いように変更 ● バックアップが信頼できるようになったことから例えば障害やサーバの入れ替えにも 参照をs3にrerouteすることでサービスの継続性も高まった バックアップも信頼できて基盤として安定してきた

Slide 24

Slide 24 text

そしてGen4へ ● Gen3でも意識はしていたが改修コストが高く積み残した問題が多くあった (BPS的超法規的措置 ) ● また長く運用してきた中で新しく問題となったのが増え続ける picサーバ ○ CPUがついたストレージと考えれば格安でサムネ生成と兼用していた ○ とはいえ台数が増え CPUが余ってしまいコストメリットが薄くなってきた ■ 純粋にストレージとして考えると s3より当然高い ○ 増設は運用コストが高く、また減らせないので費用面でも重しとなる ○ 要はサムネ生成で必要な台数を保持して、増減させない形で使いたい ■ 直s3書き込みは遅いので採用はできなく、サムネ生成でインスタンスは必要 ○ コストバランスが崩壊した ● そこで出てきたのが r2 ○ 検証で参照を本格的に流しても問題なく、コストも予測可能であった (egress無料など) 本格的に参照をr2に流そう

Slide 25

Slide 25 text

cloudflare r2検証 ● Gen3運用中にcloudflareからr2が発表されベータのころから検証 ○ 保存コストが安いのもそうだが何より egressが無料、APIのリクエストも無料枠が大きい ○ ベータは遅く使えなかったが、 GAになりリージョンが追加され速くなった ● まずバックアップをs3からr2に切り替えて書き込みが詰まったりしないかを見ていたが安定 ● 一時的に参照リクエストも流してみたりもしたが許容範囲で使えると判断 ● (ちなみにですが)s3はsnapshot/log保存に利用しておりこちらは使い続けるつもり ○ 現時点はwrite onlyのAPI発行ができない(ACL) ○ 参照をしないのであれば s3 Glacierのほうが安い(コスト) 本当に助かるサービス

Slide 26

Slide 26 text

ストレージクラス ● Gen4で最初に決まった方針がストレージクラスを Hot/Cold/Backupにわけることでした ● Hotは比較的参照がされるデータ (直近投稿)でpicを利用 ● Coldはあまり参照がされないデータ (ある程度経過した投稿 )でr2を利用 ○ Hotがある場合はBackupを兼用 ● Backupは緊急時のみ使うデータで M-DISCを利用 ○ 最初はs3 glacierも検討したがBAN回避を考えてM-DISC(まだ迷ってるので glacierにするかも) ■ コスト的には5,6年でトントンぐらい ■ 回転メディアなので個別にファイルを書くと無限に時間がかかるので isoに固めて焼くというtarを思い出す運用 ● データをColdに変更するタイミングで同時に M-DISCを作る運用 ○ 運用コストは発生するがそこまで頻繁ではなく増設より楽なので今のところ許容範囲 ストレージクラスの方針は決まったが問題があった

Slide 27

Slide 27 text

おやおや

Slide 28

Slide 28 text

どうやってクラス判定をするのか? ● 当時の投稿画像のパスは「 https://pic.nijie.net/01/nijie_picture/foo.jpg」といった形 ○ 本当に初期(Gen1)の投稿だと投稿ファイル名がそのまま出ているなど非常に自由闊達 ■ 新規(1).jpgみたいなのがあった ○ リクエストをpic/r2のどちらかに振り分けすればいいか proxyで判定できない ● URLの格納にも問題があった ○ pic.nijie.net/01/nijie_picture/foo.jpgの場合 ■ DBに格納されているのは pic01を表すhostindex(これはOK)とfoo.jpg(!?)だけ ■ /nijie_picture/はテンプレート(!?)と書き込みコードにべた書きされている (!?!?) ● つまり ○ パスの抜本的な変更が必要 ■ 今まで書き込み側で生成していたパスのハンドルが必要になる ■ proxyでパスを元にクラス判定させたいので全画像のリバランスが必要 ○ テンプレートにパスの一部が入ってるのを辞めて DBに寄せる ■ 恐ろしい数のテンプレート /コードの修正 地獄が見える 覚悟は決めてやり切ったけどほんと辛かった

Slide 29

Slide 29 text

Gen4 現行世代 ● この時点で把握しているストレージの問題を覚悟をもって精算する ● クラスによって画像の有無が変わるので「配信サービス」として高度な統合が必要 ○ https://pic.nijie.net/03/nijie/23m12/…foo.jpg ■ 23m12の投稿年月でHot/Coldのルーティングを決める(パス変更が必須) ○ 各サービスとの責任分界点を明確に決める必要がある ● Gen3では共通コードにまとめたがパスの生成など大部分の処理は呼び出す元 ○ 保存したいと投げればいい感じに採番して URLだけ返ってくるサービスが必要 ○ 処理の委譲 ● ということから当初からサービスであることを意識して設計(内部名は StorageService#v1) ○ 呼び出し側の各プロダクトでドライバとなる共通クラスを用意して Gen4を呼び出す ○ 比較的マイクロサービスに近いものができたと思う ■ 単独デプロイ可能、interfaceも明確、自身で配信もやってるし・・・ ■ 不要なのでやってないが RPC経由にすることも可能 とにかく処理を巻き取る

Slide 30

Slide 30 text

ドライバコード ● 実際のイラスト・アイコンの投稿ドライバコード ● アクション側はこのドライバを必ず呼ぶ ● ドライバでもチェックは行っているが Serviceでも同様 のチェックは行っている ● ドライバを作ることで将来的に gen5ができたとしても 改修がしやすく storage service#v1 やっとすっきりした 各アクション

Slide 31

Slide 31 text

まぁでもコード改修が大変でした こんなPRが大量・・・

Slide 32

Slide 32 text

そして現在 ストレージ構成 配信構成

Slide 33

Slide 33 text

Gen4まとめ ● 一気にpicを3台まで減らすことができ、今後もほぼ増えないので安心感がある ○ ごく一部全部Hotであるプロファイルのようなデータはあるがしばらくは問題ない ■ 問題になったらこれも r2に移すのも良い(複数枚ではないので重さは許容範囲) ○ 減らしたサーバの一部を後述の全文検索に利用 ● r2のコストも想定内 ○ ストレージコストが安いが、何も気にせず GETをしまくるとAPIコストがかかる ○ GETは10MReq(1000万)無料、月間平均で3.85RPSを越えると無料枠を越える ■ キャッシュ構成を工夫することで無料範囲に収めている (下画像の通り月平均1.56RPS) ● 今後も何かしら改善点がでてくるだろうがサービスとして分界点も定義できたので安心 大成功 R2へのRPS

Slide 34

Slide 34 text

DBがやばい お前定期的にやばくなってるな!

Slide 35

Slide 35 text

DBやばい ● DBが原因でピークタイムや特定のアクションで刺さることが増えた ● 原因は3点 ○ 大量の更新 ○ サーチクエリ ○ 抜いた系クエリ(レコメンド) ● 今までも逐次改善は行っていた ○ 検索では複数のページ分を一括取得してキャッシュ ■ で、offset取得してページ構築 ○ swrで見え方として遅くならないように ○ とはいえこれは小手先の対応だった ● 小手先の対応でできる範囲が少なくなり複雑度も許容範囲を越えたため抜本的対策が必要に DB改善やるぞ

Slide 36

Slide 36 text

とりあえずbinlogを見てみた 広告IMP

Slide 37

Slide 37 text

大量の更新を何とかする ● 調べてみて驚いたが 7割弱が広告IMPのUPDATE ○ 正直これを見た時勝ったな・・・と思った(フラグ) ● 直DBを辞めアクセスログからの登録も検討したが sqlite3を使った ○ 同じ広告は複数クライアントから見られるので 1リクエスト毎にDBに登録するのではなくある程 度まとめて+10とかにしたかった ○ 他にもリアルタイム更新が不要なものがあるので応用が利くものが欲しかった ○ 要はgroup byできるキュー的な仕組みが欲しかった ● 仕組みは単純 1. ローカルのsqlite3にinsert 2. cronで1秒前までに挿入されたデータを group by + count(*) 3. ある程度まとまった状態で dbに登録 4. 登録成功したら削除 ● 狙い通り7割弱の更新リクエストが消えた これで多少は持つか・・・

Slide 38

Slide 38 text

が・・・・・駄目っ

Slide 39

Slide 39 text

多少の改善にはなったが ● ピークタイムに刺さりやすいのは変わらず抜本的な解決が必要 ● サーチクエリと抜いた系クエリ(レコメンド)をなんとかするしかない ● なぜサーチクエリが遅いのか ○ 要はLIKE検索を行っていたがユーザ規模の増加で DBが耐えられなくなった ■ %word%はインデックスが効かないので full scanとなる ● 前方一致だと効くんですがそれだと無意味 ○ LIKEを否定してるわけではなく小規模ならお手軽なのでいいと思います ● 全文検索を導入するしかない・・・が何を入れるのか ○ DB拡張のFTSを検証したがしっくりこない ○ そもそも非力な環境で使える FTSエンジンは少ない・・・ ● 抜いた系については更にデータの刈り込みを行って一旦様子見 ○ そもそもがグラフ構造なのでグラフ DBを検討中 餅は餅屋に

Slide 40

Slide 40 text

Meilisearch ● https://www.meilisearch.com/ ● Rust製の全文検索エンジンで日本語も対応している ● 単体でなおかつメモリが少ない環境でも動く ● クラウド版もそんな高くなく使える ○ ニジエは当然セルフホスト ● とはいえ使えるのか・・・ということで検証

Slide 41

Slide 41 text

すっごく速くて検索結果も満足(RAM:2GB)

Slide 42

Slide 42 text

ユーザ・イラスト検索に導入 ● 全文検索を行っているのはイラストとユーザ ● ユーザが増えてきたこともあり運が悪いとユーザ検索が動くだけで全体が刺さることがあった ● イラストはタグやソート順など条件が複雑なのでひとまずユーザ検索に入れてみることに ○ 特に問題なく導入出来て爆速になり刺さる件数が減った ● イラスト検索にも適用しようと思ったが機能が足りずしばらく足踏み・・・ ○ v1.3でattributesToSearchOnが導入され検索対象のフィールドを指定できるように ● 機能も揃ったのでイラストに適用・・・ ○ とはいえ今までの負荷対策でロジックが複雑になっていてかなり辛かった 導入で一気に負荷が減った

Slide 43

Slide 43 text

DBの読み込み行数 LIKEはfullscanなのでRowsが多かったが一気に減っている

Slide 44

Slide 44 text

DBの読み込み行数/select数 select当たりの平均Rowsが適用が進むにつれ改善

Slide 45

Slide 45 text

CPU CPU使用率もわかりやすく改善

Slide 46

Slide 46 text

slow slowqueryも改善

Slide 47

Slide 47 text

大成功

Slide 48

Slide 48 text

とはいえいくつかハマった点がある なお現在利用しているのは v1.4.1

Slide 49

Slide 49 text

検索ワードをquoteしないと厳しい ● キャラ名などで意図しない結果となる ○ 画像例はデストロイの typoと判定された?(推測) ■ 意味的にはあってそうな気がする (𝑭𝒂𝒕𝒂𝒍𝒊𝒕𝒚...) ○ 辞書にないので仕方ない ● 元がLIKE検索なので曖昧な検索はいらないと 割り切ってquoteして解消 ● v1.4でカスタム辞書が使えるようになったので 将来的にタグを登録してなどは検討している

Slide 50

Slide 50 text

漢字のみの検索でヒットしなくなる ● 導入後に検索がおかしいとの問い合わせを受ける ○ issueは認識してたが対応ビルドをいれ忘れた ○ テストはカタカナで行っており気付かなかった ○ 問い合わせありがとうございます! ● Meilisearchは複数言語に対応しており CJKも行ける ● 漢字のみだと中国語判定されることがあり 日本語ドキュメントが検索対象外となる ● ひとまずは日本語強制でビルドして入れ替え ○ cargo build --release --no-default-features --features "analytics mini-dashboard japanese" ● オプションは変わる可能性があるのでこちらを参照 ○ Japanese specialized Meilisearch Docker Image #3882 ● そして根本対応も検討されていて期待 ○ Define languagues in settings #702 ● なんとなく昔の文字コード判定で使われた美乳を 思い出しました

Slide 51

Slide 51 text

工夫したポイント

Slide 52

Slide 52 text

構成の工夫 ● 単体で動作するため何らかの方法で冗長構成をとる 必要がある ● 結局はHTTPなのでVarnishとkeepalivedを利用して 冗長化 ● VIPを複数にしているのは 1台に寄るのでpriorityを逆に して複数台で分散できるようにしている

Slide 53

Slide 53 text

doc登録の工夫 ● doc登録は割とi/o負荷が高い ○ これはRAMが少ないからかもしれない ○ とはいえ登録中も快適に検索可能 ● そのため先ほどDB改善で利用したsqlite3を利用 ○ 更新されたイラストやユーザ IDを登録 ○ 指定idのデータを抽出して doc登録 ○ 全台登録できたらsqlite3から該当IDを削除 ● meilisearchのvupなどで一時的にflushを止めることもで きるので便利 ● なお、全更新を行っても普通に検索ができるので 割と気軽にできます ○ 全更新も全idをsqlite3に入れるだけ 全更新

Slide 54

Slide 54 text

どのようにクエリを構築したか

Slide 55

Slide 55 text

データ構造(イラスト) ● イラスト検索では以下が指定可能 ○ タイトル・本文の部分一致 ○ タグの部分/完全一致 ○ 投稿時間 ○ 抜かれた数 ○ いいね数 ○ 期間指定 ○ イラストタイプ ● また内部機能で ○ 最後に抜かれた時間 ○ 最後にブックマークされた時間 ○ ブックマーク数 ● パラメータは結構多い

Slide 56

Slide 56 text

大変だったタグ検索 ● タグ検索には4種類存在 ○ 部分一致(AND) / 完全一致(AND) / 部分一致(OR) / 完全一致(OR) ● タグが[AABB]の場合 ○ AAで部分一致=Hit ○ AAで完全一致=Miss (AABBでHitする) ● 問題はこれのAND/ORをどう表現するかで苦労しました ○ meilisearchのqueryはANDでORが存在しません(現時点では) ■ matchingStrategyはlast/allしかなくallはANDとして使えるがOR相当がない ○ filter機能はIN句を使うことでORを表現できますが、検索文字列は完全一致となります ■ 実現するためのコードはあるのですがパフォーマンスに影響があるらしく未マージ ■ Experimental feature:CONTAINS and Prefix / Suffix filter operators #544 ● タグが[AABB] [CCDD]の場合クエリ ○ 部分一致(AND/OR): q=”AABB” “CCDD” (ORは実現できないので ANDに変更) ○ 完全一致(AND): filter[tag_id =AABBのID, tag_id=CCDDのID] ○ 完全一致(OR): filter[tag_id IN [AABBのID, CCDDのID]] フィルタを使うことでOR検索もできるが制限がある

Slide 57

Slide 57 text

クエリ関連で工夫した点 ● ranking-ruleの設定 ○ デフォルトのランキングルールは以下の通り ■ words > typo > proximity > attribute > sort > exactness ● https://www.meilisearch.com/docs/learn/core_concepts/relevancy ○ 今回は投稿順などの様々な sortを使うことから変更 ■ sort > words > typo > proximity > attribute > exactness ● filterable/sortable-attributeの変更 ○ いいね件数などでsortしたり、イラストタイプで filterしたりするのでそれを追加 ○ これらの更新はindexの再構築が走るのである程度考えて設定したほうがいいです ■ 多少時間がかかる ● 開発中に頻繁に項目を変更したのですが、 docがどのバージョンなのかを把握しづらく バージョン管理するようにしました( FMTVER) ○ 全更新かけるときに FMTVERでfilterして・・・といったこともできて便利 ● データに見られたら困るものを入れないのが第一ですが、 attributesToSearchOnで検索対象は絞ったほうが良いでしょう meilisearchおすすめです クラウド版もそんな高くないので使ってみるとよいと思います

Slide 58

Slide 58 text

まとめと今後 ● ピークでも安定したサービスを実現でき、コスト削減もできた ● 当たり前の話なんですが、餅は餅屋で適切なミドルウェア選定が重要 ○ 規模が変わればミドルウェアも変わる ● 積んでる改善項目は多数あるので今後も実施予定(どれも重いのが多い) がんばります