Railsキャッシュストアのベンチマーク比較: Redis vs 他のデータストア(翻訳)
最近、Redisの代替が次々に登場していて、うちいくつかは大幅なパフォーマンス向上を謳っています。私たちは、Redisから代替に切り替えたときのパフォーマンスがどの程度向上するかを調べるために、このベンチマークを実施しました。
私たちは、Redisの代替として利用可能な以下の新しいデータストア候補をいくつか調査しました。
また、DBベースのアプローチを優先することでインメモリストレージに挑戦するRails独自のSolid Cacheについても調べました。この比較では、Andrew Atkinsonの提案に従って、PostgreSQL向けに調整したSolid Cacheも調査に含めています。
また、大幅なパフォーマンス向上を実現すると主張するStephen Margheim氏に触発されて、Solid CacheとSQLiteの組み合わせも追加しました。
最後に、LitecacheとSQLite3の組み合わせも追加して、Solid Cacheと直接比較しました。
Solid CacheはRailsに多くの利点をもたらしますが、このベンチマークでは純粋なパフォーマンスのみに着目しています。
🔗 ベンチマークの詳細
ベンチマークにはこのスクリプトを使いました。このスクリプトは、Rails 8アプリケーションを実行するDigitalOceanドロップレット(セットアップ: 4GB RAM、25GB SSD Ubuntu 24.10 x64)で10万回の読み取りおよび書き込み操作を実行しました。
「シングルスレッドベンチマーク」で作成されたスレッドは1つだけです。
「マルチスレッドベンチマーク」で作成されたスレッドは5つです。
収集したデータは、同じテストを5回実行して平均を計算した結果に基づいています。
以下は、スクリプトのシンプルなバージョンです。
require "benchmark"
# Run it in the SolidCache with PG tuned setup
# ActiveRecord::Base
# .connection
# .execute("SELECT pg_prewarm('solid_cache_entries')")
# Run the benchmarking for 100k reads and writes
n = 100_000
def cache_fetch(i)
Rails.cache.fetch(["key", i], expires_in: 5.minutes) do
"Hello World!"
end
end
# Clear the existing cache
Rails.cache.clear
puts "\nSingle thread:"
Benchmark.bm(6) do |x|
x.report("write") {
n.times { |i| cache_fetch(i) }
}
x.report("read") {
n.times { |i| cache_fetch(i) }
}
end
# Spawn "x" threads and run "n" operations combined
def spawn_threads(x = 5, n)
threads = []
x.times do
threads << Thread.new do
(n/x).times { |i| cache_fetch(i) }
end
end
threads.each(&:join)
end
# Clear cache again before running another benchmark
Rails.cache.clear
puts "\nMultiple threads:"
Benchmark.bm(6) do |x|
x.report("write") {
spawn_threads(5, n)
}
x.report("read") {
spawn_threads(5, n)
}
end
🔗 シングルスレッドのパフォーマンス
比較のために、Redisをベンチマークとして利用しました。
MemcachedのパフォーマンスはRedisに近くなっています。ベンチマーク結果ではRedisがわずかに優位ですが、この差は実際のアプリケーションではそれほど重要ではないと思われます。
ValkeyとDiceDBも同程度のパフォーマンスを発揮しています。読み取りと書き込みの操作はどちらもValkeyがわずかに優位に立っていますが、Redisは依然としてValkeyやDiceDBよりも1.5倍高速です。
DragonflyDBはベンチマークで大幅にパフォーマンスが低かったため、グラフから除外してあります。ただしRails 7でのパフォーマンスはValkeyと同等なので、Rails 8では最適化が必要な可能性もあります。
PostgreSQLで動作するSolid Cacheは、読み取り操作ではRedisの約2倍遅く、書き込みではすべてのオプションの中で最も低速です。ただし、調整済みバージョンではパフォーマンスが大幅に向上し、標準のPostgreSQLセットアップよりも1.5倍高速になります。この組み合わせでパフォーマンスのメリットを最大限に引き出したい場合は、キャッシュデータベースの最適化を検討しましょう。
SQLite3で動作するSolid Cacheでは大幅な改善が見られ、読み取り速度はRedisに匹敵し、書き込み速度は調整済みPostgreSQLセットアップの速度を上回っています。
最後に、SQLite3で動作するLiteCacheは、読み取り操作がRedisの約4倍、書き込みが2.5倍高速という優秀なパフォーマンスを実現し、RailsおよびRubyアプリケーションで利用できる最速のオプションとなります。
🔗 マルチスレッドのパフォーマンス
このテストでは、Redis、Memcache、Valkey、DiceDBのパフォーマンスはシングルスレッドベンチマークとほぼ同じで、書き込み操作がわずかに改善されました。Redisはこの中ではトップパフォーマーとしての地位を維持し、引き続き他のパフォーマンスを上回っています。
残念ながら、PostgreSQLで動作するSolid Cacheは最も低速でした。ただし、調整すれば書き込みパフォーマンスが大幅に改善されたので、より良い結果を得るためにはデータベースの最適化が重要である点が浮き彫りになりました。
驚くべきことに、SQLite3で動作するSolid Cacheは、Redisの代替製品に匹敵するパフォーマンスを実現するとともに、調整済みPostgreSQLで動作するSolid Cacheセットアップを2倍上回り、実質的に速度が2倍になりました。
さらに、SQLite3で動作するLitecacheは、書き込み操作でシングル スレッドのパフォーマンスを上回り、読み取り操作と書き込み操作の両方でRedisの4倍という驚異的な速度を実現しました。
🔗 まとめ
ValkeyやDragonflyなどのデータストアはRedisよりも大幅に優れたパフォーマンスを謳っていますが、Rails API経由で利用すると強みを十分に活用できず、Redisと同等のパフォーマンスレベルになります。
PostgreSQLで動作するSolid Cacheは、依存関係が少なくメンテナンスが容易などのメリットがありますが、このテストではメモリ消費量が多く、パフォーマンスも最下位でした。ただし、Solid CacheをPostgreSQLと組み合わせて使う必要がある場合は、Andrew Atkinsonの推奨に従ってチューニングすることが重要です。これにより書き込みパフォーマンスが大幅に向上します。
一方、SQLite3で動作するSolid Cacheは嬉しい驚きでした。特にマルチスレッドテストではRedisと同等のパフォーマンスを発揮しています。これは、DBベースのキャッシュソリューションとしては驚くほど高速です。
最後に、LitecacheとSQLite3の組み合わせは最速のオプションで、4倍の大幅なパフォーマンス向上を誇りますが、いくつかの理由から推奨されません。
- Rails 8はまだサポートされていません(このテストではforkしたブランチを使いました)。
-
Litecacheを使うのに必要なlitestack gem には、LiteJob、LiteCable などのさまざまな追加アドオンもバンドルされています。Litecacheのためだけにこのパッケージスイートをすべて追加する意味はありません。
-
このパッケージはまだ新しい初期段階であるため、productionアプリケーションでの利用はおすすめしません。
関連記事
- 編注: valkeyはRedisのforkで、バージョンによっては違いがほとんどないものもあるとのことです。 ↩
概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。