Redis vs Database Caching
Caching Layers in Magento 2
Application Layer
├── Full Page Cache (Redis/Varnish)
├── Block Cache (Redis)
├── Configuration Cache (Redis)
└── Query Cache (Database)
└── Redis (istributed cache)
Redis Caching
// Redis configuration in env.php
'redis' => [
'host' => '127.0.0.1',
'port' => '6379',
'database' => '0',
'password' => '',
'timeout' => '2.5',
'persistent' => true,
],
// Cache implementation
$cache = $this->cache->load('catalog_product_' . $productId);
if ($cache) {
return unserialize($cache);
}
// Fallback to database
$product = $this->productRepository->getById($productId);
$this->cache->save(serialize($product), 'catalog_product_' . $productId, [], 3600);
Database Query Caching
-- MySQL query cache (deprecated in 8.0)
SELECT SQL_CACHE * FROM catalog_product_entity WHERE entity_id = 123;
-- Using temporary tables for complex queries
CREATE TEMPORARY TABLE tmp_product_data AS
SELECT p.*, pe.*
FROM catalog_product_entity p
JOIN catalog_product_entity_varchar pe ON p.entity_id = pe.entity_id
WHERE pe.attribute_id = 71; -- name attribute
Performance Comparison
Response Time Comparison
| Operation | Redis | MySQL |
|---|---|---|
| Simple GET | 0.1-0.5ms | 1-10ms |
| Complex query | N/A | 10-500ms |
| Serialization | 0.1ms | N/A |
| Network overhead | 0.2ms | 0.5ms |
Throughput
// Redis: ~100,000 operations/sec
// MySQL: ~1,000-10,000 queries/sec
// Redis cluster: ~500,000 operations/sec
// Benchmark example
$iterations = 10000;
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$redis->get('product_' . $i);
}
$redisTime = microtime(true) - $start;
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$db->query('SELECT * FROM catalog_product_entity WHERE entity_id = ' . $i);
}
$dbTime = microtime(true) - $start;
Memory Considerations
// Redis memory usage
$redis->set('product_123', serialize($productData));
$memoryUsed = $redis->info()['used_memory'];
// Database: disk-based, no memory limit for reads
// Redis: RAM-based, limited by server memory
// Recommendation: Use Redis for hot data, DB for cold storage
When to Use Each
Use Redis When:
- Data is frequently accessed (hot data)
- Response time is critical (< 5ms needed)
- Data is small and fits in memory
- Multiple reads per write
- Session data, configuration, cart data
Use Database When:
- Data is large and rarely accessed (cold data)
- Complex queries with joins/aggregations
- Data requires ACID transactions
- Full-text search needed
- Reporting and analytics queries
Hybrid Approach (Recommended)
// Read-through cache pattern
public function getProduct($id)
{
$cacheKey = 'product_' . $id;
$cached = $this->redis->get($cacheKey);
if ($cached !== false) {
return unserialize($cached);
}
$product = $this->db->fetchRow(
'SELECT * FROM catalog_product_entity WHERE entity_id = ?',
[$id]
);
$this->redis->setex($cacheKey, 3600, serialize($product));
return $product;
}
// Cache-aside with TTL
public function getCategoryProducts($categoryId)
{
$cacheKey = 'category_' . $categoryId . '_products';
$cached = $this->redis->get($cacheKey);
if ($cached !== false) {
return unserialize($cached);
}
$products = $this->db->fetchAll(
'SELECT * FROM catalog_category_product WHERE category_id = ?',
[$categoryId]
);
$this->redis->setex($cacheKey, 1800, serialize($products));
return $products;
}
Consistency Trade-offs
Cache Invalidation Strategies
// 1. Time-based expiration (TTL)
$this->redis->setex($key, 3600, $data);
// 2. Event-based invalidation
public function onProductSave($observer)
{
$productId = $observer->getEvent()->getProduct()->getId();
$this->redis->del('product_' . $productId);
}
// 3. Write-through cache
public function saveProduct($product)
{
$this->db->save($product);
$this->redis->setex('product_' . $product->getId(), 3600, serialize($product));
}
// 4. Versioned cache keys
$version = $this->config->get('catalog_version');
$cacheKey = 'product_' . $id . '_v' . $version;
Stale Data Risks
Scenario: Product price updated
├── Redis cached: $29.99 (5 min old)
├── Database: $34.99 (new price)
└── Risk: Customer sees old price
Solutions:
1. Short TTL for price data (60 seconds)
2. Price change event triggers cache invalidation
3. Checkout always reads from database
4. Price versioning with cache invalidation
Distribution Challenges
// Multi-server cache consistency
$redis1 = new Redis(); // Server 1
$redis2 = new Redis(); // Server 2
// Problem: Cache invalidation on one server
// doesn't affect other servers
// Solution: Redis Pub/Sub for invalidation
$redis1->publish('cache_invalidation', $key);
$redis2->subscribe(['cache_invalidation'], function($redis, $channel, $key) {
$redis->del($key);
});
Practice Problems
Design a caching strategy for a product catalog with 1M products, 10K updates/hour, and 100K reads/second.
Solution
// Strategy:
// 1. Hot products (top 10%): Redis with 5-min TTL
// 2. Warm products: Redis with 60-min TTL
// 3. Cold products: Database only
// 4. Invalidation: Event-based on save
// 5. Warm-up: Pre-load on deploy Implement a cache invalidation system that works across multiple Redis instances.
Solution
// Implementation:
// 1. Version counter in database
// 2. Cache key includes version: product_123_v5
// 3. On update: increment version, publish invalidation
// 4. On receive: delete local cache
// 5. Fallback: TTL-based expiration Quiz
1. Redis is best suited for which type of data?
2. What is the typical response time difference between Redis and MySQL?
3. Which cache invalidation strategy is most reliable for price changes?
4. What is the main limitation of Redis caching?
Flashcards
Question
Redis typical response time?
Click to reveal answer
Answer
0.1-0.5ms for simple GET operations
Question
MySQL typical query time?
Click to reveal answer
Answer
1-10ms for simple queries, up to 500ms for complex
Question
Best cache invalidation for prices?
Click to reveal answer
Answer
Event-based invalidation on price save event
Question
Redis main limitation?
Click to reveal answer
Answer
Limited by available RAM memory
Question
Recommended pattern for product cache?
Click to reveal answer
Answer
Read-through cache with TTL and event-based invalidation
Revision Notes
Key Takeaways
- 1. Redis: 0.1-0.5ms response, ~100K ops/sec, RAM-limited
- 2. MySQL: 1-10ms response, ~1-10K queries/sec, disk-based
- 3. Use Redis for hot data, MySQL for cold/complex queries
- 4. Event-based invalidation is most reliable
- 5. Hybrid approach: Redis for reads, DB as source of truth
Interview Tips
- • Compare response times and throughput metrics
- • Explain cache invalidation strategies
- • Discuss consistency vs performance trade-offs
- • Know when to use each caching layer
Cheat Sheet
Redis vs DB
- Redis: 0.1-0.5ms, 100K ops/sec, RAM
- MySQL: 1-10ms, 1-10K qps, Disk
- Hot data → Redis, Cold → MySQL
- Invalidation: Event-based > TTL
- Checkout: Always read from DB