Edited at

memoistとRailsキャッシュのどちらがパフォーマンスが良いか調べてみた

More than 3 years have passed since last update.

memoistとRailsキャッシュのどちらがパフォーマンスが良いか調べてみた


はじめに

Railsでマスター系のDBで何度も同じ内容を叩いたり、N+1問題対策でincludesをつけていても、結局

デカイSQLを発行してしまい効率的ではない場合もある

そこでメモリに保存して再利用する方法memoistを使っては見たものの、普通のRailsについてる機能のLow-Level Cachingを使ってもそんなにパフォーマンスが変わらないんじゃないか?と言う疑問をがふつふつと湧いたので計測して、運用しているサービスでどちらを採用するか決めた

※Railsのキャッシュはmemcacheを使用


検証環境


  • OS: MacOSX 10.10

  • Rails version: 4.2

  • memoist version: 0.12.0


サンプルコード

class SampleCode < ActiveRecord::Base

#キャッシュされるSampleCodeモデルのコード群
@@cached_codes = [AAA, BBB, CCC, DDD, EEE].map!(&:freeze).freeze

# クラスメソッドのメモ化
class << self
extend Memoist
def plan_ids
SampleCode(code: @@cached_codes).pluck(:id)
end
memoize :plan_ids
end

# クラスメソッドで、Low-Level Cachingを利用
def self.plan_ids_cache
Rails.cache.fetch("plan_ids_cache_key", expires_in: 1.day) do
SampleCode(code: @@cached_codes).pluck(:id)
end
end
end


計測

Rails consoleを起動して下記にある検証コードをコピペして実行する

> bin/rails console 

5つの整数を格納した配列を取得するメソッドが1000回

memoistを使う場合とLow-Level Cachingを使う場合、最後に普通にActiveRecordでデータを取ってくる場合だ

(ベンチマークの試行回数の書き方が間違ってたので修正してあります)

# 開実行する

require "benchmark"

#試行回数
num = 1000

plan_code = [AAA, BBB, CCC, DDD, EEE].map!(&:freeze).freeze

report = Benchmark.bm do |r|
r.report "memo" do
num.times { SampleCode.plan_ids }
end
r.report "cache" do
num.times { CampaignCode.plan_ids_cache }
end
# ActiveRecordで素でとった場合
r.report "ar original" do
num.times { SampleCode.where(code: plan_code).pluck(:id) }
end
end

# ローカル環境実行結果(memcachedもローカルサーバで動いている)
=>
user system total real
memo 0.0200 0.0100 0.0300 (0.0332)
cache 0.3299 0.2200 0.5499 (1.1794)
ar original 1.6600 0.2500 1.9100 (4.5657)

=>

# Staging環境実行結果(サーバはHeroku, memcachedはアドオン動いている)

user system total real
memo 0.0099 0.0 0.0099 (0.1472)
cache 0.5000 0.1499 0.6499 (3.6802)
ar original 1.1500 0.1699 1.3200 (3.3467)


考察

memoistを使った場合圧倒的に早いLow-Level Cachingを用いた場合キャッシュもActiveRecordを直接取ってくる場合よりよりもローカル環境では5倍以上倍早く、stagingでは2倍以上早い。

Staging環境でのテストでもmemcachedが別サーバにあるでのオーバヘッドがあるにも関わらず予想外に好成績だった(むしろHerokuが予想外に早くてびっくりした)

memoistの場合はサーバのメモリに保存するがLow-Level Cachingの場合はmemcacheにキャッシュした値を委譲できるので、少メモリと言う観点ではLow-Level Cachingが優位性がある


結論

使いドコロによってmemoistとLow-Level Cachingを使い分けることにした


  • memoist はバッチ処理など一気に大量のデータを処理する場合に用いる

  • Low-Level Cachingはユーザがよく参照する画面で必要な情報をキャッシュする


refs

Ruby でベンチマークを取る方法