Recently, we have seen the rise of Redis alternatives. Some of them claimed substantial performance gains. We did this benchmarking to see how much performance gain one would get by switching from Redis to one of the alternatives.
We explored several new contenders like Valkey, DragonflyDB, and DiceDB, which serve as drop-in Redis replacements. We also looked at Rails' own SolidCache, which challenges in-memory storage by favoring database-based approach. For this comparison, we included a tuned SolidCache for PostgreSQL, as suggested by Andrew Atkinson. We also included SolidCache with sqlite3, inspired by Stephen Margheim, who claims to offer significant performance gains. Finally, we added litecache with sqlite3 to compare it directly against SolidCache.
Although SolidCache brings various advantages to Rails, this benchmarking focused solely on pure performance.
We used this script for benchmarking. It performed 100k read and write operations on a DigitalOcean droplet with a 4GB RAM, 25GB SSD Ubuntu 24.10 x64 setup running a Rails 8 application.
In the case of "Single thread benchmarking" only one thread was created. "Multi thread benchmarking" had five threads.
The data gathered is based on running the same tests five times, with the averages calculated.
A simpler version of the script looks like this.
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
For our comparison, we used Redis as a benchmark.
Memcached performs similarly to Redis, with Redis having a slight edge in the benchmark results. However, this difference is unlikely to be significant in a real-world application.
Valkey and DiceDB deliver similar performance, with Valkey having a slight edge in both read and write operations. However, Redis still remains 1.5x faster than both.
DragonflyDB (not shown in the graph) performed significantly slower in the benchmarks, which is why it was excluded. However, its performance in Rails 7 was comparable to Valkey, suggesting it may require optimizations for Rails 8.
SolidCache on PostgreSQL is approximately twice as slow as Redis for read operations and the slowest for writes among all options. However, the tuned version improves performance significantly, making it 1.5x faster than the standard PostgreSQL setup. Consider optimizing your cache database if you want to get maximum performance benefits.
SolidCache with sqlite3 has shown significant improvement, with read speeds now comparable to Redis and write speeds surpassing those of a tuned PostgreSQL setup.
Finally, litecache with sqlite3 delivers exceptional performance, being approximately 4x faster in read operations and 2.5x faster in writes compared to Redis, making it the fastest option available for Rails and Ruby applications.
In this test, Redis, Memcache, Valkey, and DiceDB performanced similarly to the single-thread benchmarks, with slight improvements in write operations. Redis maintained its position as the top performer, continuing to outpace the others.
Unfortunately, SolidCache with PostgreSQL was the slowest option. However, when tuned, it significantly improved write performance, highlighting the importance of optimizing the database for better results.
Surprisingly, SolidCache with SQLite3 delivered performance on par with Redis alternatives while outperforming a tuned PostgreSQL setup by a factor of two, effectively doubling its speed.
Additionally, LiteCache with SQLite3 outperformed single-thread performance in write operations, making it an impressive 4x faster than Redis in both read and write operations.
It’s clear that while data stores like Valkey, Dragonfly, and others claim significantly better performance than Redis, using them through Rails APIs doesn’t fully leverage those advantages, leading to performance levels similar to Redis.
SolidCache with PostgreSQL offers benefits such as fewer dependencies and easier maintenance, but it was the slowest performer in this test, with high memory consumption. However, if you must use SolidCache with PostgreSQL, tuning it as recommended by Andrew Atkinson is crucial, as it can notably enhance write performance.
On the other hand, SolidCache with SQLite3 was a pleasant surprise, particularly in the multi-threaded test, where it performed on par with Redis. For a database-backed caching solution, this is impressively fast.
Lastly, while LiteCache with SQLite3 is the fastest option, boasting a significant 4x performance gain, it’s not recommended for several reasons. First, it doesn’t yet support Rails 8 (we used a forked branch for this test). Second, the litestack gem required for this comes bundled with a variety of additional addons, such as LiteJob, LiteCable, and others. Adding this entire suite of packages just for LiteCache doesn’t make sense. Finally, since the package is still new and in its early stages, we would not recommend using it for production applications.
If this blog was helpful, check out our full blog archive.