Skip to content
query-caching-strategies logo

Query Caching Strategies

query-caching-strategies

Implement query caching strategies to improve performance. Use when setting up caching layers, configuring Redis, or optimizing database query response times.

SKILL.md

Full skill instructions

Query Caching Strategies

Overview

Implement multi-level caching strategies using Redis, Memcached, and database-level caching. Covers cache invalidation, TTL strategies, and cache warming patterns.

When to Use

  • Query result caching
  • High-read workload optimization
  • Reducing database load
  • Improving response time
  • Cache layer selection
  • Cache invalidation patterns
  • Distributed cache setup

Application-Level Caching

Redis Caching with PostgreSQL

Setup Redis Cache Layer:

// Node.js example with Redis
const redis = require('redis');
const client = redis.createClient({
  host: 'localhost',
  port: 6379,
  db: 0
});

// Get user with caching
async function getUser(userId) {
  const cacheKey = `user:${userId}`;

  // Check cache
  const cached = await client.get(cacheKey);
  if (cached) return JSON.parse(cached);

  // Query database
  const user = await db.query(
    'SELECT * FROM users WHERE id = $1',
    [userId]
  );

  // Cache result (TTL: 1 hour)
  await client.setex(cacheKey, 3600, JSON.stringify(user));
  return user;
}

// Cache warming on startup
async function warmCache() {
  const hotUsers = await db.query(
    'SELECT * FROM users WHERE active = true ORDER BY last_login DESC LIMIT 100'
  );

  for (const user of hotUsers) {
    await client.setex(
      `user:${user.id}`,
      3600,
      JSON.stringify(user)
    );
  }
}

Query Result Caching Pattern:

// Generalized cache pattern
async function queryCached(
  key,
  queryFn,
  ttl = 3600  // Default 1 hour
) {
  // Check cache
  const cached = await client.get(key);
  if (cached) return JSON.parse(cached);

  // Execute query
  const result = await queryFn();

  // Cache result
  await client.setex(key, ttl, JSON.stringify(result));
  return result;
}

// Usage
const posts = await queryCached(
  'user:123:posts',
  async () => db.query(
    'SELECT * FROM posts WHERE user_id = $1 ORDER BY created_at DESC',
    [123]
  ),
  1800  // 30 minutes TTL
);

Memcached Caching

PostgreSQL with Memcached:

// Node.js with Memcached
const Memcached = require('memcached');
const memcached = new Memcached(['localhost:11211']);

async function getProductWithCache(productId) {
  const cacheKey = `product:${productId}`;

  try {
    // Try cache first
    const cached = await memcached.get(cacheKey);
    if (cached) return cached;
  } catch (err) {
    // Memcached down, continue to database
  }

  // Query database
  const product = await db.query(
    'SELECT * FROM products WHERE id = $1',
    [productId]
  );

  // Set cache (TTL: 3600 seconds)
  try {
    await memcached.set(cacheKey, product, 3600);
  } catch (err) {
    // Fail silently, serve from database
  }

  return product;
}

Database-Level Caching

PostgreSQL Query Cache

Materialized Views for Caching:

-- Create materialized view for expensive query
CREATE MATERIALIZED VIEW user_statistics AS
SELECT
  u.id,
  u.email,
  COUNT(o.id) as total_orders,
  SUM(o.total) as total_spent,
  AVG(o.total) as avg_order_value,
  MAX(o.created_at) as last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.email;

-- Index materialized view for fast access
CREATE INDEX idx_user_stats_email ON user_statistics(email);

-- Refresh strategy (scheduled)
REFRESH MATERIALIZED VIEW CONCURRENTLY user_statistics;

-- Query view instead of base tables
SELECT * FROM user_statistics WHERE email = '[email protected]';

Partial Indexes for Query Optimization:

-- Index only active users (reduce index size)
CREATE INDEX idx_active_users ON users(created_at DESC)
WHERE active = true AND deleted_at IS NULL;

-- Index recently created records
CREATE INDEX idx_recent_orders ON orders(user_id, total DESC)
WHERE created_at > NOW() - INTERVAL '30 days';

MySQL Query Cache

MySQL Query Cache Configuration:

-- Check query cache status
SHOW VARIABLES LIKE 'query_cache%';

-- Enable query cache
SET GLOBAL query_cache_type = 1;
SET GLOBAL query_cache_size = 268435456;  -- 256MB

-- Monitor query cache
SHOW STATUS LIKE 'Qcache%';

-- View cached queries
SELECT * FROM performance_schema.table_io_waits_summary_by_table_io_type;

-- Invalidate specific queries
FLUSH QUERY CACHE;
FLUSH TABLES;

Cache Invalidation Strategies

Event-Based Invalidation

PostgreSQL with Triggers:

-- Create function to invalidate cache on write
CREATE OR REPLACE FUNCTION invalidate_user_cache()
RETURNS TRIGGER AS $$
BEGIN
  -- In production, this would publish to Redis/​Memcached
  -- PERFORM redis_publish('cache_invalidation', json_build_object(
  --   'event', 'user_updated',
  --   'user_id', NEW.id
  -- ));
  RAISE LOG 'Invalidating cache for user %', NEW.id;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Attach to users table
CREATE TRIGGER invalidate_cache_on_user_update
AFTER UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION invalidate_user_cache();

-- When users are updated, trigger fires and invalidates cache
UPDATE users SET email = '[email protected]' WHERE id = 123;

Application-Level Invalidation:

// Invalidate cache on data modification
async function updateUser(userId, userData) {
  // Update database
  const updatedUser = await db.query(
    'UPDATE users SET name = $1, email = $2 WHERE id = $3 RETURNING *',
    [userData.name, userData.email, userId]
  );

  // Invalidate related caches
  const cacheKeys = [
    `user:${userId}`,
    `user:${userId}:profile`,
    `user:${userId}:orders`,
    'active_users_list'
  ];

  for (const key of cacheKeys) {
    await client.del(key);
  }

  return updatedUser;
}

Time-Based Invalidation

TTL-Based Cache Expiration:

// Variable TTL based on data type
const CACHE_TTLS = {
  user_profile: 3600,        // 1 hour
  product_list: 1800,        // 30 minutes
  order_summary: 300,        // 5 minutes (frequently changes)
  category_list: 86400,      // 1 day (rarely changes)
  user_settings: 7200        // 2 hours
};

async function getCachedData(key, type, queryFn) {
  const cached = await client.get(key);
  if (cached) return JSON.parse(cached);

  const result = await queryFn();
  const ttl = CACHE_TTLS[type] || 3600;

  await client.setex(key, ttl, JSON.stringify(result));
  return result;
}

LRU Cache Eviction

Redis LRU Policy:

# redis.conf
maxmemory 1gb
maxmemory-policy allkeys-lru  # Evict least recently used key

# Or other policies:
# volatile-lru: evict any key with TTL (LRU)
# allkeys-lfu: evict least frequently used key
# volatile-ttl: evict key with shortest TTL

Cache Warming

Pre-load Hot Data:

// Warm cache on application startup
async function warmApplicationCache() {
  // Warm popular users
  const popularUsers = await db.query(
    'SELECT * FROM users ORDER BY last_login DESC LIMIT 50'
  );

  for (const user of popularUsers) {
    await client.setex(
      `user:${user.id}`,
      3600,
      JSON.stringify(user)
    );
  }

  // Warm top products
  const topProducts = await db.query(
    'SELECT * FROM products ORDER BY sales DESC LIMIT 100'
  );

  for (const product of topProducts) {
    await client.setex(
      `product:${product.id}`,
      1800,
      JSON.stringify(product)
    );
  }

  console.log('Cache warming complete');
}

// Run on server startup
app.listen(3000, warmApplicationCache);

Distributed Caching

Redis Cluster Setup:

# Multi-node Redis for distributed caching
redis-server --port 6379 --cluster-enabled yes
redis-server --port 6380 --cluster-enabled yes
redis-server --port 6381 --cluster-enabled yes

# Create cluster
redis-cli --cluster create localhost:6379 localhost:6380 localhost:6381

Cross-Datacenter Cache:

// Replicate cache across regions
async function setCacheMultiRegion(key, value, ttl) {
  const regions = ['us-east', 'eu-west', 'ap-south'];

  await Promise.all(
    regions.map(region =>
      redisClients[region].setex(key, ttl, JSON.stringify(value))
    )
  );
}

// Read from nearest cache
async function getCacheNearest(key, region) {
  const value = await redisClients[region].get(key);
  if (value) return JSON.parse(value);

  // Fallback to other regions
  for (const fallbackRegion of ['us-east', 'eu-west', 'ap-south']) {
    const fallbackValue = await redisClients[fallbackRegion].get(key);
    if (fallbackValue) return JSON.parse(fallbackValue);
  }

  return null;
}

Cache Monitoring

Redis Cache Statistics:

async function getCacheStats() {
  const info = await client.info('stats');
  return {
    hits: info.keyspace_hits,
    misses: info.keyspace_misses,
    hitRate: info.keyspace_hits / (info.keyspace_hits + info.keyspace_misses)
  };
}

// Monitor hit ratio
setInterval(async () => {
  const stats = await getCacheStats();
  console.log(`Cache hit rate: ${(stats.hitRate * 100).toFixed(2)}%`);
}, 60000);

Cache Strategy Selection

StrategyBest ForDrawbacks
Application-levelFlexible, fine-grained controlMore code, consistency challenges
Database-levelTransparent, automaticLess flexibility
Distributed cacheHigh throughput, scaleExtra complexity, network latency
Materialized viewsComplex queries, aggregationsManual refresh needed

Best Practices

✅ DO implement cache warming ✅ DO monitor cache hit rates ✅ DO use appropriate TTLs ✅ DO implement cache invalidation ✅ DO plan for cache failures ✅ DO test cache scenarios

❌ DON'T cache sensitive data ❌ DON'T cache without invalidation strategy ❌ DON'T ignore cache inconsistency risks ❌ DON'T use same TTL for all data

Resources

More Sales & marketing skills

revops logo
Sales & marketing

revops

Design and optimize systems connecting marketing, sales, and customer success into a unified revenue engine.

53.3K 104.9K
View
lead-magnets logo
Sales & marketing

lead-magnets

Strategic planning and optimization for email capture offers and gated content.

53.3K 103.4K
View
directory-submissions logo
Sales & marketing

directory-submissions

Directory submissions: the foundation layer of distribution that passes backlinks and discovery surface area into your marketing pages.

53.3K 76.3K
View
gsap-timeline logo
Sales & marketing

gsap-timeline

Sequence and choreograph multi-step animations with precise timing control and nested timelines.

15.9K 58.4K
View
launch-strategy logo
Sales & marketing

launch-strategy

Structured framework for planning product launches, feature announcements, and go-to-market strategies.

53.3K 56.8K
View
gws-drive-upload logo
Sales & marketing

gws-drive-upload

Upload files to Google Drive with automatic MIME type detection and metadata.

31.2K 47.2K
View
ci-cd-and-automation logo
Sales & marketing

ci-cd-and-automation

Automates CI/CD pipeline setup. Use when setting up or modifying build and deployment pipelines. Use when you need to automate quality gates, configure test runners in CI, or establish deployment strategies.

101.4K 43.2K
View
persona-team-lead logo
Sales & marketing

persona-team-lead

Team leadership persona with standup coordination, meeting prep, task delegation, and team communication.

31.2K 29.3K
View
recipe-find-large-files logo
Sales & marketing

recipe-find-large-files

Identify large Google Drive files consuming storage quota.

31.2K 29.2K
View

Sales & marketing AI tools

KreadoAI logo
Sales & marketing

KreadoAI

Revolutionize Video Creation with AI-Generated Avatars and Voices

Paid
View
Followr logo
Sales & marketing

Followr

Social media on autopilot

Paid
View
Logodiffusion logo
Sales & marketing

Logodiffusion

Design custom logos instantly with AI.

Paid
View
I
Sales & marketing

Ideogram

Text-heavy marketing and design images

Freemium
View
Prompt Silo logo
Sales & marketing

Prompt Silo

Boost Your SEO with Content and SEO Silos

Free
View
Marketing Blocks logo
Sales & marketing

Marketing Blocks

Revolutionize Your Marketing with AI-Powered MarketingBlocks!

Paid
View
Scrip AI logo
Sales & marketing

Scrip AI

10X faster & free AI content creation for 100+ use cases

Free
View
T
Sales & marketing

Tribescaler

Tribescaler | Make Your Tweets Go Viral

Paid
View