Railsでのお手軽なキャッシュ戦略
キャッシュの主な動機は、
- パフォーマンスの向上
- より多くのトラフィックを捌けるように
- サーバー・リソースの節約
が主だとおもう。 今回、Webメディアの運用が決まったあたりで、サーバーサイドのキャッシュを真面目に考えてみたので記録を簡単に残しておく。
キャッシュで気をつけるべき点
キャッシュで気をつけるべき点はもうキャッシュコントロールの一言に尽きる。 これを気をつけていい感じに取ってパフォーマンスを向上させるのが今回のお仕事。
主にRailsでのキャッシュ戦略について
あまり調査 -> 実装 -> リリースまでの時間がなかったことから、お手軽に対応できそうな方法を中心に検討してみることにしました。
今回、CDNでのキャッシュ戦略は取っていないが、有効な手段だと思う。 後日検証を行って追加する予定である。順を追って簡単に説明する。
フラグメントキャッシュ
キャッシュコントロール ◎
キャッシュ効果 ◯
Railsで一番ポピュラーに取られている(と思われる)キャッシュ手法である。
ただ、どうしてもWebアプリケーション(というかUnicornやPuma)で処理を行う以上、 前段のNginxで処理を完了させるキャッシュの方式よりも効果は少ない。 とはいえDBへのアクセスは減らせるのと、キャッシュコントロールの難易度が他に比べるとかなり楽にできるので、 ログイン処理のような動的コンテンツがあるようなWebページでも導入が容易だと思う。
その他のやりやすさ
他にもサーバーサイド側でデバイス判定して、 HTMLの出し分けだったりでどうしても動的な処理がサーバーサイド側で発生する場合でもそこまで心配せずに導入することが可能。 また、HTMLの一部分だけをキャッシュするという手法も取れるので他の手法に比べて考える事、 キャッシュを取ることによってできなくなることはほとんど無いように感じる。
ページキャッシュ
キャッシュコントロール ▲
Rails(Ruby)側でのファイル操作が可能なので、記事を更新したときに更新した記事だけの、ページキャッシュを削除したりということがひとまず可能。 後述するNginxのキャッシュよりもキャッシュのコントロールが幾分ラク。 ただどうしても静的ファイルとして出力するので、色々柔軟性には欠ける。
キャッシュ効果 ◎
静的ファイルを直接nginxから呼び出せば効果はばつぐんだ。
その他のやりやすさ
サーバーサイドでデバイス判定して、ページ構成を変更しているようなページを処理するときには注意が必要。 それぞれで異なるページキャッシュを作らないと行けないし、PC用のページキャッシュは必ずPCに、SP用のページキャッシュを必ずSPに返すために工夫が必要だし、 ドキュメントだけを眺めてもそのやり方は書いていないので、自前で実装と言うか工夫が必要になる。 後述するnginxのキャッシュの方がオフィシャルにデバイスによって取得するキャッシュキーを変更できる機能を持っている分、nginxのキャッシュの方が良いのではないか?と思っている。
nginxのキャッシュ
キャッシュ効果 ◎
当然ながらキャッシュの効果は絶大。 Railsのページキャッシュとのスピード面の計測はしていないけど、まぁ変わらないのではないかと思っている。
キャッシュコントロール ×
ただキャッシュのコントロールについては良くないと思っており、 どこかを更新した際には、nginxのすべてのキャッシュファイルを削除するしか無いと思っている。(どのファイルがどの記事のキャッシュかを判別する情報がない) ただ、NginxでキャッシュをとりつつRailsでSessionのブラウザCookieベースで管理する場合、 色々工夫が必要で大変なので、CookieベースのSession管理を取りやめRedisやDBでSessionをStoreする方が良いかもしれない。
結局どの戦略を取ったか
動的に便利にやりたい部分があったというのと、実装までに期間が余り取れなかったという理由もあって、ひとまずフラグメントキャッシュで様子を見てみようということにした。 動作として特に問題が発生していないのと、結構潤沢にサーバーを使える環境というのもあったので、この方法で対応した。
そして、1000万以上PVくらいのWebメディアを半年くらい運用していたのだけどとりたてて問題は発生しなかった。 とりあえずDBアクセスをなくすだけでも効果は絶大なんだな、ということを実感しました。
予算等の見直しや要件に変更が入った場合は2や3への変更を視野に入れる必要もあったのかな、と思っている。
まとめ
- キャッシュは色々便利だけど、最初はなるべくキャッシュは利用しない方向でできるだけ頑張る
- 最初は簡単なのでフラグメントキャッシュから始めるといいです。
- nginxのページキャッシュやRailsのページキャッシュの導入はその後検討でいいと思う。
PAY.JPアンチパターンという内容でLTした
PAY.JPのプラットフォーム向けサービスである、 「PAY.JP Platform」のリリース記念ミートアップで、「PAY.JP アンチパターン」という内容でLTをさせていただきました。
内容
ざっくり見てもらえればわかるのですが、 PAY.JPを使っていく際のアンチパターンを3つほどまとめたものになります。 基本的には定額課金周りのアンチパターンが多めな感じです。
雑感
- はじめてのLTだったので、それなりに緊張したが、なんとかやりおおした。
- 発表のしかたがあまり良くない気もしたが、LTの内容は自分のが一番テックな内容だと思っていて、その点はよかった。
- 今後もLT参加したい。
- 今後PAY.JPのアンチパターンのスライドの完全版を作りたい。
- いくつかのパターンを追加するのと、図解や補足説明を入れるようにしたい。
- ちなみにPAY.JP Platformはうまく使えばかなり便利なのと、競合他社製品に比べると安さ・シンプルさがウリなので、ぜひつかって見てほしい。
また、LTにお誘いいただいた、 PAYの皆様、ありがとうございました。そしてお疲れ様でした。
いちばんやさしい新しいSEOの教本を読んだ
個人的興味や、知識の補完のためにSEO入門するに当たり、みんながおすすめしているように見えたので、この本を買って読んで見ることにした。
いちばんやさしい新しいSEOの教本 人気講師が教える検索に強いサイトの作り方 (「いちばんやさしい教本」シリーズ)
- 作者: 安川洋,江沢真紀,村山佑介
- 出版社/メーカー: インプレス
- 発売日: 2014/02/17
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る
前提として、SEO対策についての知識は簡単にWebで調べれば出るものくらいは知っている感じ。
内容について
目次は公開情報なので、下記に記載してみる。
◆Chapter 1 SEOの目的と考え方を身に付けよう
◆Chapter 2 Webサイトの目的と訪問者の目的を考えよう
◆Chapter 3 有効な検索キーワードを調査しよう
◆Chapter 4 業種別に最適なサイト構成を考えよう
◆Chapter 5 適切な内部対策でSEOの効果を高めよう
◆Chapter 6 質の高い外部対策でWebサイトの価値を高めよう
◆Chapter 7 ソーシャルメディアからWebサイトに集客しよう
◆Chapter 8 技術的な問題を解決して優れたWebサイトを目指そう
◆Chapter 9 SEOの効果を分析してさらなる改善を進めよう
・付録 1 業種・ジャンル別キーワード一覧表
・付録 2 サイト全体のテンプレート一覧管理表
・用語集
Chapter1はともかく、 Chapter2,3,4はサイトを作る前の準備段階の話に3Chapter分の枠を割いていて、 本書を読み進めるごとに実感するのは、SEO対策はとにかく調査と事前準備・設計が大事だということだった。
当たり前の事しか書いていない
少し話は変わるのだけど、 ちょっと前に、メディアサイトのリニューアルにエンジニアとして携わった事があった。
そのときにSEOの面倒を見てくれた方がいて、SEO対策をエンジニアの立場から色々やったりしたのですが、 一般的に言われている「ちゃんとmeta titleつけよう」「H1タグはページ内に1つだけにしようね」とか、「記事のリンク先変わったらちゃんと301リダイレクトしよう」そういう「当たり前の事の徹底」の泥臭い事だったように記憶している。
しかし、そういう当たり前な、簡単な施策をやっておくだけだったのだけど、 リニューアルから数ヶ月後で検索結果に対してもしっかりと効果が現れているそうだ。
本書でも、ごくごく当たり前の事を当たり前にやっていく。 そんな当たり前なことだからこそ、本書紹介されているページ設計やテクニックは今に通ずるものがほとんどだと思っており、 数年しか効果がないような雑なものはないのだろう。
どうしてこの設計にするべきなのかなどの、施策に対しての理由についても結構しっかりめに説明されており、 前述のメディアリニューアル案件で「そういえばこんなページ構成だったな…」「回遊性を上げる理由はそこにあったのかー」とか思い出して、かなり納得感を持って読み進めることができた。
タイトルに「新しい」と入れたのは失敗
流石に最初に出版されたのは5年前で、新しくないのは当たり前。 日進月歩進化していっているWeb界隈や、SEO関連で近年あったような大型アップデート*1については当然触れられてはいない。 いい意味でタイトルに「新しい」と入れたのは失敗だと思っており、SEO入門の古典的な本だと思ってる。
Webエンジニア目線から見て
本書はWebエンジニア目線ではなく、Web担当者やPM向けの印象を受ける。*2
なので、技術的にどうすればいいかみたいなところの章にページは余り割かれておらず*3、 キーワード設計、サイトのページ設計などの事前作業や設計についての大事さがやっぱりメインな感じ。
エンジニアからみたSEO観が変わった
- SEO対策は当たり前の連続だけど、とくに最初のページ・キーワード設計周りなどがめっちゃ大変。できれば専業でお願いしたほうが良い。
- エンジニアリングだけでSEOを良くすることはできない。必ずそこにはSEO上の設計が必要。
- SEO対策の仕事は、サイトを作ったときに見てもらえないという、「検索上のもったいない」をなくす仕事。
おすすめしたい人
- とにかくSEOの基礎知識を身に着けたい人。
- 個人的観測だと、基礎はほとんど、全体の60 - 70%はカバーできるのでは。
- Webに載せるまでもない情報が貴重なので、この本でなくてもいいので、1冊SEO入門書を読んでおいたほうが学習効率は高いと思う。
- メディア案件でSEO意識して!なんて言われてみたものの、SEOよくわからない。
- ディレクターや担当者からの301リダイレクトの依頼を面倒で断っちゃうめんどくさいエンジニア
とにかくSEOに入門するに当たり、この本はおすすめ。 ぜひ読んでみてください。
最後に
- SEO対策をプロジェクトのメインの仕事にするのは正直やりたくないなと思いました。
- キーワード設計したり、ホント泥臭い仕事。
- 興味を持って読んでみてすごく理解したし納得感あったが、自分でやりたいなと思えなかった。
- この記事はSEOを意識しないでパパっと書きました。
追記
第二版が出ているらしいです。 ちょっとこれもさらっと読んでみるかなー
いちばんやさしい新しいSEOの教本 第2版 人気講師が教える検索に強いサイトの作り方[MFI対応] (「いちばんやさしい教本」シリーズ)
- 作者: 安川洋,江沢真紀,村山佑介
- 出版社/メーカー: インプレス
- 発売日: 2018/07/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
SendGridで1つのメールを最大1000件のメールアドレスにまとめて送信する方法
これはよく見るとドキュメントに載っているのだけど、 結構見落としがちだったので、記載しておく。
ドキュメントの内容
v3 Mail Send API概要 - ドキュメント | SendGrid
SendGridのMailSendAPIを使えばOKな感じです。
Rubyのコード
これで終了だとあまりにもつまらないので、 SendGrid公式でRubyのGem(SDK)が提供されているので、それを使った際のサンプルを載せておく。 Rubyのコードにするとざっくりこんな感じだろう。
def mail_send sendgrid = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY']) sendgrid.client.mail._("send").post(request_body: params) end def params param = { "custom_args" => { "test_mode" => true }, "from" => { "email" => 'example@example.com', "name" => 'サイト名' }, "content" => [ { "type" => 'text/html', "value" => 'テスト' } ], "mail_settings" => { "sandbox_mode" => { "enable" => false } } } param["personalizations"] = User.all.find_each do |user| { "to" => [ { "email" => user.email, "name" => user.name } ], "substitutions" => { "[USER_NAME]" => user.name, "[USER_EMAIL]" => user.email, }, "subject" => 'メールタイトル', "custom_args" => { "site_user_id" => user.id.to_s, } } end param end mail_send
この方法で運用すれば1000つの宛先へのメール送信をまとめることができる。 配信メールアドレスが30000件なら、30回のリクエストに分割してメール配信リクエストをSidekiqやDelayedJob等でWorkerの数を調整して処理してやれば送信リクエストはサクッと完了するだろう。 ただ、流石に30,000件とかのユーザーへの一斉送信はMarketing Campaigns API Overview - ドキュメント | SendGrid を使うべきでは…? と思ってしまっています。*1
テンプレートに変数をアサイン
他にもMailAPIでは、テンプレートを変数を定義しておき、送信時に変数を展開することができる。(コードのsubstitutions
あたりを参照してください。)
それぞれのユーザーでメールアドレスだけでなく、ユーザー名や宛名なんかも異なるだろうし、メールの文章中にそういった動的な文言も含めることができる。
まさにSendGridさまさまである。
ransackで雑にRDBMSを使った簡易的なキーワードのOR検索を実装する
一般的なRDBMSを使ったよくあるLIKEでの曖昧検索実装では、検索ボックスに「寿司 和食」と入力してもただの単語での曖昧検索だと、
当たり前なんだけど、キーワードのOR検索を実現することができない。
select * from contents where LIKE body '%寿司 和食%'
となってしまって、空白を含めた1単語として認識されてしまい、思うような結果が得られないと思う。
実際に組み立てるクエリは…?
多分イマイチなんだろうけど、簡易的にOR検索を実現するにはこんな感じになると思う。
"SELECT "contents".* FROM "contents" WHERE (("title" LIKE '%寿司%' OR "title" LIKE '%和食%') OR ("body" LIKE '%寿司%' OR "body" LIKE '%和食%'))"
これで擬似的にキーワードのOR検索を実装することができた。精度もまぁ無いよりかはマシくらいなもんだろう。 これでOKな事例というのは、検索対象が小規模な場合で、設定できるキーワードに上限を一応設けたほうが良いかもしれない。 以上の注意点を踏まえた上で、Railsで簡単に上記SQLを作る方法に進んでみよう。
実際にRansackで雑に実装
もちろん複雑なことはやっていないので、自分でさっきのSQL文を組み立てて投げるのでも良いのだけど、 面倒だし、検索機能を実装する時によく使われているRansackで実装してみようと思う。
以下のようにかけばOK。
Content.search(title_or_body_cont_any: ['寿司', '和食']).result
それでは上記で実行されるSQLを見てみよう。
irb(main):007:0> Content.search(title_or_body_cont_any: ['寿司', '和食']).result.to_sql => "SELECT \"contents\".* FROM \"contents\" WHERE ((\"contents\".\"title\" LIKE '%寿司%' OR \"contents\".\"title\" LIKE '%和食%') OR (\"contents\".\"body\" LIKE '%寿司%' OR \"contents\".\"body\" LIKE '%和食%'))"
おお、大丈夫そうだ。 あとは、JOINでスペースを区切り文字にして、splitでキーワードの配列作ればOKな感じ。
# controller def index if params[:keyword] @contents = Content.search(title_or_body_cont_any: params[:keyword].split(' ')).result else @contents = Content.all end end
これで完成。 意外とシュッとできた。やっぱり小規模用途だとransackは便利だった。