便利なherokuと active recordの 速度改善tips
Post on 30-Jul-2015
734 Views
Preview:
TRANSCRIPT
便利なHerokuとActiveRecordの速度改善Tips
2015-06-06(土) 第9回中国地方DB勉強会 IN 米子株式会社リゾーム システム開発部 尾古 豊明
自己紹介
名前:尾古 豊明
所属:株式会社リゾーム(岡山)
TwitterID:@patorash
鳥取県大山町(旧中山町)出身。
米子東高校卒業後、愛媛大学に進学。松山で就職。
NWしてたけど25歳でPHPプログラマに強制転向。
独自フレームワーク、CakePHP、WordPressなどを経てAndroidやって、1年個人事業主やって、チーム開発がしたくなりリゾームに入社。RubyとRailsを覚える。
DBには詳しくありません!
実家は牧場です
和牛の飼育からお肉の通信販売
焼肉レストランを経営しています
春先から放牧することも
会社の話
とは?
ショッピングセンター(SC)・専門店向けのシステム開発やコンサルティング業務をしている会社です。
Windows系・Java・Rubyなどで自社製品の
開発を行っています。
● 顧客分析システム● デベロッパーマネジメントシステム● BOND GATE(コミュニケーションウェア)● SC GATE(SC・ショップDB)
SC GATEとは?
SCと、そのテナント(ショップ)と運営元の企業を横断的に検索・比較できるデータベースシステムです。
● SCの出店計画を立てるため● 専門店の出店計画● ライバルの動向チェック
などなど。
SC GATEを支える技術
● プログラミング言語 :Ruby 2.2.2● フレームワーク :Rails 4.2.1● プラットフォーム :Heroku● データベース :Postgresql● ソースコード管理 :github● CSSフレームワーク :Bootstrap● AltJS :CoffeeScript● CSS :Sass● テンプレートエンジン :Slim● JSライブラリ :jQuery,Knockout
Ruby on Railsとは?
● 世の中のWebフレームワークで最も大きな影響を与えたであろうMVCフレークワーク
● Railsのようなフレームワークが色んなプログラミング言語で作られた○ PHP・・ CakePHP○ Java・・ SpringMVC、Play Framework○ Python・ Django
● 中でもModelを担当するActiveRecordがものすごく優秀で使いやすい
今日話すこと
● Herokuについて● ActiveRecordについて● PostGISについて
Herokuとは?
● PaaSサービス● 課金でスケーラブルな環境が手に入る● 様々なプログラミング言語をサポート● 様々なデプロイ方法(git, github,DropBox)● 制限が色々…
○ 30秒ルール○ ファイルの保存はできない等
● アドオンを追加することで、拡張可能○ データベース(Postgresql, MySQL)○ SSL○ NewRelicなど監視ツール系、ログ収集系○ メール配信系○ memcachedなどその他色々…
Heroku App
Heroku git
Slug(塊)
Dyno Manager
DynoDyno Dyno
コンパイル
アドオン● データベース● メール配信システム● キャッシュシステム● 状態監視サービス● スケジューラー● SSL● エラー監視サービス● ログサービス
git
Rails
git push heroku tagname:master開発者
● 固有のサーバを持たなくて済む。● Dynoの性能変更でスケールアップ、数を変更でスケールアウト● スポットでDynoの増減をすればいい(イベント時のみ増やすとか)
コード管理はgithubgit push origin develop
Herokuのデータベース
● Heroku Postgresqlが提供されている● MySQLはサードパーティのアドオン● HerokuでRailsアプリを作る場合は
特に制限・こだわりがなければPostgresqlがいいと思う(情報が多いから)
● 定期的にバックアップを取ってくれる● お試しの無料プラン、趣味の$9プラン、
本番環境向けのstandard, premiumなど色々ある。
● 商用だとstandard0以上オススメ($50〜)○ 最大コネクション数が100になるから
ActiveRecordとは?
● 超強力なO/R Mapperです。○ データベースから取得したデータが自動的にクラスのオ
ブジェクトに変換される仕組みのこと
利点● SQLを書かなくてもいい。
● マイグレーションができる。
● データベース周辺を抽象化してくれる。
● オブジェクトなので扱いやすい
欠点
● 遅い(と、よく言われる)
● メモリを大量に使う
利点:SQLを書かなくてもいい
SELECT * FROM articles WHERE id = 1;SELECT * FROM comments WHERE article_id = 1;のようなSQLを書かなくてもよい。結構複雑なところまで網羅できるが、拡張gemを使うとさらに便利に。
article = Article.find 1article.comments.each do |comment| p comment.titleend
ActiveRecordの表現力の高さ
user = User.find 1articles = Article.where(status: ‘open’, user_id: user).order(created_at: :desc).limit(10)
user = User.find 1articles = Article.open.written_by(user).recent(10)
ActiveRecordの流儀に則ってscopeを定義すると、データの絞り込みが流れるように書ける
利点:マイグレーションができる
DB定義の変更はリスクを伴うし、だめだった場合に戻すのが大変だった。しかし、マイグレーションが簡単になったことで気軽にDB定義の変更ができるようになった。
class AddCommentsCountToArticle < ActiveRecord::Migration def up add_column :articles, :comments_count, :integer, default: 0 end
def down remove_column :articles, :comments_count endend
利点:マイグレーションができる
カジュアルにテーブル定義を書き換えられる。
仕様変更しやすい。ダメでもすぐに戻せる。
テーブル定義の変更履歴がマイグレーションファイルとして残るため、
変更の反映漏れが起こる確率はかなり低くなる。
$ bin/rake db:migrate
● テーブル定義を変更する
● テーブル定義を前の状態に戻す
$ bin/rake db:rollback
欠点:遅い(と言われる)
● 適当にコードを書くと、DBアクセス回数が膨大になる(N+1問題)
● 大量のデータを読み込むとメモリを大量に消費する(Rubyでオブジェクトが大量に作られるため)
● Rubyでループを回すと遅い
N+1問題とは?
article = Article.find 1article.comments.each do |comment| p comment.user.nameend
SELECT * FROM articles WHERE id = 1;SELECT * FROM comments WHERE article_id = 1;SELECT * FROM users WHERE user_id = 1;コメントしたユーザー名の取得N回
コメント全件取得1回のDBアクセスが発生する。
どうすれば速くなるか?
● 1度のDBアクセスでデータを取得する(DBアクセス回数を減らす)
● 使うカラムのデータだけ取得する(取得するデータ総量を減らす、 オブジェクトを生成しない等)
● 大量のデータを一度に取得しない(生成するオブジェクトの数を減らす)
● Rubyでのループの回数を減らす(一度に取得する上限は20件まで等)
● そもそもDBにアクセスしない
どうすれば気付くか、減らせるか
● パフォーマンス監視ツールを使う● 検知するためのgemを使う● counter_cacheを使う● 便利なメソッドを使う● ページング用のgemを使う(kaminari)● フラグメントキャッシュを使う
どうすれば気付くか、減らせるか
● パフォーマンス監視ツールを使う● 検知するためのgemを使う● counter_cacheを使う● 便利なメソッドを使う● ページング用のgemを使う(kaminari)● フラグメントキャッシュを使う
New Relic:パフォーマンス監視
NewRelicはローカル環境も見れるhttp://localhost:3000/newrelic/
レスポンスタイムが一目でわかる。遅い場合は赤文字で教えてくれる。そこを速くすればいい。
どんなクエリが発行されて、どれだけ時間がかかっているかが一覧でわかる。遅い場所があれば、そのクエリを改善してみる。結果をキャッシュしてもよいのならキャッシュを試みる。そして、計測する。
bullet:N+1問題検出用gem
N+1問題とは?
article = Article.find 1article.comments.each do |comment| p comment.user.nameend
SELECT * FROM articles WHERE id = 1;SELECT * FROM comments WHERE article_id = 1;SELECT * FROM users WHERE user_id = 1;コメントしたユーザー名の取得N回
コメント全件取得1回のDBアクセスが発生する。
bulletがN+1問題を検出して通知。<= Comment.includes(:user)をすればいいと言ってくれてる。
counter_cacheを使う
関連先のデータ数をキャッシュしておく
例:記事のコメント数がいくらあるか?
SELECT * FROM articles WHERE id = 1SELECT COUNT(*) FROM comments WHERE article_id = 1;2回SQLが実行される
article = Article.find 1article.comments.count
counter_cacheを使う
例:記事のコメント数がいくらあるか?
comments_countカラムを定義して、
更新時にカウント数をキャッシュしておく。
SELECT * FROM articles WHERE id = 1
1回のSQLで済む
article = Article.find 1article.comments_count
counter_cacheの注意点
例えば、論理削除・無効フラグなどを使ってデータを制御している場合は、デフォルトだとその設定は無視される。
絞り込み条件も与えること。
has_many :comments
has_many :comments, -> { where(deleted: false) }
便利なメソッドを使う
Railsは便利だがパフォーマンスが悪い、と
長年言われ続けてきたためパフォーマンス改善のために様々なメソッドが準備されている。
● pluck● find_each● eager_load, joins, preload● to_sql● explain
pluck
モデルオブジェクトを生成せずに指定したカラムの
データを配列で取得する。
titles = Article.pluck(:title)titles.each do |title| puts titleend
articles = Article.allarticles.each do |article| puts article.titleend
find_each
大量のデータを小分けに取得してループする。
一度に大量のオブジェクトが作られないため、
メモリに優しい反面、DBへのアクセス回数は増える
Article.all.each do |article| puts article.titleend
Article.find_each do |article| puts article.titleend
eager_load, joins, preload
関連データをキャッシュしてDBアクセス数を減らす。
代わりにメモリを消費するので大きなテーブル同士は
やめたほうがいい。検索条件に使うだけならjoinsを使う。
article = Article.find 1article.comments.each do |comment| puts comment.contentend
article = Article.eager_load(:comments).find 1article.comments.each do |comment| puts comment.contentend
to_sql
ActiveRecordで組み立てて発行したSQL文を取得する。
サブクエリとして使うパターンもある。
私自身はあまり使わないが、思った通りのデータが取得できないときに、確認のためにデバッグツール上で呼ぶことがある。
query = Article.where(id: 1).to_sql
explain
実行計画を見ることができる。
私自身は全く使ったことがない。
Article.where(id: 1).explain
PostGISについて
PostGISはGISを扱うための拡張機能
GeographicInformationSystem・・・ 地理情報システム
緯度・経度・標高など
平たくいうと、Google Mapsなど地図アプリで独自
のデータを扱う際などに便利!
PostGISのなにが嬉しいのか?
位置情報からレコードを
検索できる
例えば・・・
● 指定した緯度・経度から
半径10km以内のレコードを取得
● 距離でソートなど
ActiveRecordでPostGIS
● activerecord_postgis_adapterを利用することで使える
● 2系だとRails4.2系がサポートされていなかった。スライド書き始めた時は最新が3.0.0.β5だったが、最近3.0.0がリリース
GISを扱う時にハマッたポイント
測地系:世界測地系・日本測地系
● 数百メートル近くずれる● activerecord_postgis_adapterでは、
世界測地系しかサポートしていない● システム的には、日本測地系のデータしか使わ
ない● 毎回、測地系変換をJS側で実行している
HerokuでPostGISは使えるか?
使える
● PostGISの有効化● Rails起動時にAdapter差し替え処理を追加
○ Herokuのドキュメントに書いてある
※ただし、正式サポートではない
以上!!
top related