Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Redis: Caching and Data Structures

By Kokil Thapa | Last reviewed: September 2026

Your database slows down long before your traffic justifies a bigger server. Redis: Caching and Data Structures solves that gap by keeping hot data in memory and giving you native types beyond a plain key-value blob. On production Laravel and PHP stacks, Redis 8.10 sits beside MySQL or PostgreSQL as the first line of defence against repeated queries, session bloat, and queue backlogs. This guide walks through setup, structure choice, and the failure modes I see on real deployments.

What is Redis used for in web application caching?

Redis is an in-memory data store that speaks a simple protocol over TCP. Most teams install it for cache, but the same server also powers sessions, job queues, and real-time counters. That dual role is why I reach for Redis before Memcached on custom Laravel applications and WooCommerce stacks.

Cache entries are usually strings: serialised PHP arrays, JSON API responses, or HTML fragments. Redis evicts keys when memory fills, using policies you configure in redis.conf. The default maxmemory-policy allkeys-lru drops least-recently-used keys—a sensible starting point for read-heavy sites.

Redis Caching Layer ArchitectureWeb AppLaravel / PHPRedis 8.10Cache + QueuesSessions + Pub/SubMySQL 9.7Source of truthGETSQLCache-Aside Flow1. Read Redis → 2. Miss hits DB → 3. Write TTL keyTypical TTL: 60s–3600s by data volatility
Redis: Caching and Data Structures sit between your PHP application and relational database as a fast read layer.

A typical win looks like this: a product category page runs eight Eloquent queries per request. Cache the rendered fragment for five minutes and MySQL load drops sharply. Pair that with speed optimisation work on indexes and you often delay a costly vertical scale.

Core caching commands every developer should know

These commands cover ninety percent of day-to-day cache work:

  • SET key value EX 300 — store a string with a 300-second TTL
  • GET key — fetch a value; returns nil on miss
  • DEL key — invalidate one entry
  • EXISTS key — cheap existence check before a heavy rebuild
  • INCR counter — atomic integer bump for rate limits
  • TTL key — debug remaining lifetime during incidents

Official command reference lives in the Redis command documentation. Keep a REPL handy during outages—redis-cli beats guessing from application logs.

How do you configure Redis caching in Laravel 13?

Laravel ships first-class Redis support through the predis/predis package or the phpredis PECL extension. On production Ubuntu servers I prefer phpredis for lower latency. Laravel 13 requires PHP 8.3 or higher; PHP 8.5 is the current anchor version and runs Redis clients without issue.

Install Redis on Ubuntu 24.04:

sudo apt update
sudo apt install redis-server php8.5-redis
sudo systemctl enable redis-server
redis-cli ping
# Expected: PONG

Point Laravel at Redis in .env:

CACHE_STORE=redis
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_CACHE_DB=1

Separate logical databases—or better, key prefixes—for cache, sessions, and queues. On the booking platform behind Adventure Third Pole Trek, queue workers and page cache share one Redis instance but use different DB indexes to simplify flush operations.

Cache-aside with the Laravel Cache facade

The cache-aside pattern keeps your database authoritative. Application code reads Redis first; on miss it loads from SQL and writes back with a TTL.

use Illuminate\Support\Facades\Cache;

$categories = Cache::remember('shop:categories:v3', 600, function () {
    return Category::query()
        ->withCount('products')
        ->orderBy('name')
        ->get();
});

For config, routes, and views, Laravel has dedicated drivers. See Laravel caching strategies for config, route, view, and data for the full matrix. Run php artisan config:cache and php artisan route:cache on deploy—those files belong in your CI/CD pipeline, not only on manual SSH sessions.

Tagged cache invalidation

When one admin action invalidates dozens of related keys, tags beat manual DEL lists. Redis supports tags through Laravel's tag implementation:

Cache::tags(['products', 'category:12'])->put(
    'product:list:cat:12',
    $products,
    3600
);

// After an update:
Cache::tags(['products', 'category:12'])->flush();

Memcached cannot do this natively. If your catalogue changes often, that alone may justify Redis. Compare trade-offs in Laravel cache tags with Redis vs Memcached.

Which Redis data structures fit common web application patterns?

Strings work for simple cache blobs, but Redis earns its keep when the data model maps to a native type. Picking the wrong structure creates serialisation overhead or awkward key sprawl. The diagram below maps types to typical use cases.

Redis Data Structures OverviewStringJSON cache, countersHashUser profile fieldsListActivity feedsSetUnique tags, online IDsSorted SetLeaderboards, ranksStreamEvent logsAll types support TTL and atomic ops at memory speed
Redis data structures beyond plain cache strings—each type maps to a distinct application pattern.

Strings and hashes

Strings store serialised objects. Hashes store field maps under one key—ideal when you update single attributes without rewriting a whole JSON blob.

# Hash example: cart summary per user
HSET cart:8842 item_count 3
HSET cart:8842 subtotal_npr 4500
HSET cart:8842 updated_at 1699999999
HGETALL cart:8842

On eCommerce projects like Quick And Easy Nepalese Grocery, hashes hold lightweight session cart metadata while the authoritative cart lives in MySQL. If the Redis key expires, the next page load rebuilds from SQL—acceptable for non-checkout views.

Lists, sets, and sorted sets

Lists give FIFO queues at the data-structure level—though Laravel queues use Redis lists internally via a higher-level API. Sets enforce uniqueness: tag IDs, online user IDs, or "already notified" tokens. Sorted sets (ZSET) pair a member with a score; perfect for leaderboards and time-ordered rankings.

# Top contributors this week (score = points)
ZADD leaderboard:2026w36 1200 "user:101"
ZADD leaderboard:2026w36 980 "user:442"
ZREVRANGE leaderboard:2026w36 0 9 WITHSCORES

Need deeper coverage? Read Redis data structures beyond cache for streams and HyperLogLog use cases.

Streams and pub/sub for real-time features

Streams persist event logs with consumer groups—useful for audit trails and async fan-out. Pub/sub pushes ephemeral messages to subscribers with no persistence. Laravel broadcasting and websocket scaling often combine Redis pub/sub with websocket workers. Do not confuse pub/sub with a durable queue; dropped messages are expected if no subscriber is connected.

How do you prevent cache stampede and stale data in Redis?

A cache stampede hits when a hot key expires and hundreds of concurrent requests rebuild it at once. Your database spikes, Redis memory churns, and p99 latency jumps. I have seen this take down category pages on high-traffic WooCommerce stores during flash sales.

Cache Stampede vs Lock PatternWithout lockWith lock100 requests → 100 DB queries1 rebuild, 99 wait on cacheLock-protected rebuild steps1. SET lock:key NX EX 102. Winner rebuilds → SET data EX 6003. Losers retry GET or serve stale4. DEL lock:key when done
Prevent cache stampede in Redis with short-lived locks and optional stale-while-revalidate semantics.

Practical stampede defences

  1. Probabilistic early expiry — refresh hot keys before hard TTL under load
  2. Mutex via SET NX — one worker rebuilds; others wait or return stale data
  3. Stale-while-revalidate — serve old value while async job refreshes
  4. Request coalescing — Laravel Cache::lock() wraps the rebuild closure
use Illuminate\Support\Facades\Cache;

$value = Cache::remember('report:daily', 900, function () {
    return Cache::lock('lock:report:daily', 10)->block(5, function () {
        return DB::table('orders')->selectRaw('DATE(created_at) as d, COUNT(*) as c')
            ->groupBy('d')->get();
    });
});

Stale data is a product decision, not only a technical one. A price list cached for an hour may be fine for a blog sidebar but wrong for checkout. Document TTL rules per domain object. Query-level patterns are covered in database query caching strategies.

Invalidation strategy checklist

Cache bugs are often stale-data bugs. Tie invalidation to write paths:

  • Flush tagged groups when admin updates a catalogue
  • Version keys (products:v4) instead of deleting unknown dependents
  • Publish invalidation events on model save via Eloquent observers
  • Never cache user-specific auth or payment state globally

Validate JSON API payloads during development with the JSON formatter tool before you serialise them into Redis strings—bad encoding wastes hours in production debugging.

When should you choose Redis over Memcached for caching?

Both are fast in-memory stores. Memcached 1.6.x remains excellent for simple GET/SET cache pools. Redis adds data structures, persistence options, pub/sub, and Lua scripting. The table below summarises what I tell teams choosing infrastructure in 2026.

CriteriaRedis 8.10Memcached 1.6.x
Primary use caseCache + queues + sessions + structuresPure object cache
Data typesStrings, hashes, lists, sets, ZSET, streamsOpaque blobs only
PersistenceRDB snapshots, AOF logNone (by design)
Tag-based invalidationSupported via app patternsNot native
Memory efficiencyHigher per-key overheadLean for small objects
Multi-threadingIO threads; mostly single-threaded command coreMulti-threaded on 1.6 line
Typical PHP stack fitLaravel cache, queue, session, HorizonWordPress object cache only

Verdict: choose Memcached when you need a stateless, horizontally sharded blob cache and nothing else. Choose Redis when one service should handle cache, Laravel queues, sessions, rate limiting, and pub/sub. Full comparison: Redis vs Memcached vs Dragonfly.

WordPress sites benefit from Redis object cache too—see WordPress object cache with Redis setup. Magento 2.4.x shops often pair Redis with Varnish as described in Magento 2 Redis and Varnish for speed.

How do you run Redis safely in production?

Redis is fast because it keeps data in RAM. That speed comes with operational obligations: memory caps, persistence choice, network isolation, and monitoring. On Ubuntu servers I manage via Linux system administration, Redis runs on a private interface—not exposed to the public internet.

Production Redis TopologyApp ServersPHP-FPM + WorkersRedis Primarymaxmemory + AOFrequirepass enabledRedis ReplicaRead failoverOperational checklistBind 127.0.0.1 · UFW deny 6379 · maxmemory 70% RAMMonitor: used_memory, evicted_keys, connected_clientsPersistence: RDB + AOF for queue/session data
Production Redis: Caching and Data Structures require memory limits, auth, and persistence planning beyond dev installs.

Memory, persistence, and security

Set maxmemory to roughly seventy percent of available RAM on a dedicated Redis box. Leave headroom for OS page cache and RDB fork spikes. Enable requirepass or ACL users; bind to 127.0.0.1 unless you run a managed cluster.

Persistence trade-offs matter when Redis holds more than disposable page cache. Queues and session data need RDB snapshots, AOF, or both. Read Redis persistence RDB vs AOF compared and Redis persistence and clustering before you assume cache-only defaults.

Monitor these metrics continuously:

  • used_memory_rss vs maxmemory
  • evicted_keys — rising evictions mean TTL or capacity tuning needed
  • instantaneous_ops_per_sec — baseline for anomaly detection
  • blocked_clients — signals slow commands or BLPOP waits

After deploy, reload PHP-FPM so opcache picks up code changes—same routine I use on legal-tech portals like Court Marriage In Nepal and sister sites on shared Deployer pipelines. Redis restarts do not replace application deploy hygiene.

High-traffic and multi-layer caching

Redis is one layer. Full stacks combine CDN edge cache, HTTP reverse proxies, application cache, and ORM query cache. See caching strategies for high-traffic sites and improving web performance with caching strategies. API responses may also use ETag headers as in API caching with ETag and Last-Modified.

Symfony projects integrate through the cache component—Symfony cache with Redis and APCu covers adapter configuration for PHP 8.4+ and Symfony 8.1 deployments.

Key Takeaways

  • Use Redis 8.10 for cache plus queues, sessions, and native data structures—not only GET/SET strings.
  • Configure Laravel with phpredis, separate logical DBs, and tagged invalidation for catalogue-heavy apps.
  • Match structure to pattern: hashes for field maps, sets for uniqueness, sorted sets for rankings, streams for durable events.
  • Stop cache stampedes with locks, stale-while-revalidate, and TTL rules tied to business freshness needs.
  • Cap memory, enable auth, choose persistence when data survives beyond a page view, and monitor evictions.
  • Combine Redis with CDN and HTTP caching layers—one store rarely fixes every latency problem alone.

People Also Ask

Is Redis only a cache?

No. Redis started as a cache but now serves as a primary data store for ephemeral and semi-durable workloads. Teams use it for session storage, job queues, rate limiting, pub/sub, leaderboards, and distributed locks. Cache remains the most common entry point, but the data structures are what keep it in the stack long term.

What TTL should I set for Redis cache keys?

Match TTL to how fast the underlying data changes. Static config can live for hours or days. Product listings might use five to thirty minutes. Personalised or cart data often needs seconds to minutes, or no cache at all. When in doubt, shorter TTL plus tag invalidation beats long TTL plus manual flush scripts.

Does Redis replace MySQL or PostgreSQL?

Not for authoritative business data. Redis excels at speed and atomic in-memory operations but is sized for RAM, not terabytes of relational history. Keep orders, users, and payments in MySQL 9.7 or PostgreSQL 18. Use Redis as an acceleration and coordination layer on top.

Can Redis run on the same server as PHP?

Yes for small and mid-traffic sites—a common setup on client VPS hosts at roughly Rs 2,500–5,000/month (~USD 19–37). Watch RAM contention: if Redis and MySQL compete for memory, latency spikes on both. Split Redis to its own instance once evictions or OOM kills appear in logs.

Ship faster applications with the right Redis layer

Redis: Caching and Data Structures reward teams who treat cache as architecture, not an afterthought. Pick the native type, set honest TTLs, protect hot keys from stampedes, and lock down production access. The result is lower database load, snappier pages, and headroom before your next hardware bill. If you want help auditing cache strategy on a Laravel, WordPress, or custom PHP codebase, review our testing and optimisation service or contact us for a production review.

Frequently Asked Questions

Redis is an in-memory data store for hot reads, sessions, queues, and native structures like rate limits and leaderboards beside your SQL database.

Match TTL to freshness: hours for static config, five to thirty minutes for listings, seconds to minutes for cart data—or skip cache for checkout values.

No—keep orders, users, and payments in MySQL 9.7 or PostgreSQL 18; Redis accelerates reads and ephemeral workloads in RAM, not full relational history.

Laravel 13 ships first-class Redis support through predis/predis or the phpredis PECL extension; I prefer phpredis on production Ubuntu for lower latency. Install redis-server and php8.5-redis on Ubuntu 24.04, confirm with redis-cli ping, then set CACHE_STORE=redis, REDIS_CLIENT=phpredis, REDIS_HOST=127.0.0.1, and REDIS_CACHE_DB=1 in .env. Laravel 13 requires PHP 8.3 or higher. Separate logical databases or key prefixes for cache, sessions, and queues—on Adventure Third Pole Trek's booking platform, queue workers and page cache share one instance but different DB indexes to simplify flush operations.

These six commands cover ninety percent of day-to-day cache work on production stacks. SET key value EX 300 stores a string with a 300-second TTL. GET fetches a value and returns nil on miss. DEL invalidates one entry. EXISTS is a cheap existence check before a heavy rebuild. INCR atomically bumps an integer for rate limits. TTL shows remaining lifetime during incidents. Keep redis-cli handy during outages—it beats guessing from application logs when a hot key misbehaves.

Strings store serialised PHP arrays, JSON API responses, or HTML fragments—fine for simple cache blobs. Hashes hold field maps under one key, ideal when you update single cart attributes without rewriting a whole JSON blob—I've used this on Quick And Easy Nepalese Grocery for lightweight cart metadata. Lists give FIFO queues at the structure level; sets enforce uniqueness for tag IDs or online users; sorted sets pair members with scores for leaderboards. Streams persist event logs with consumer groups; pub/sub pushes ephemeral messages with no persistence—do not treat pub/sub as a durable queue.

Cache-aside keeps your database authoritative. Application code reads Redis first; on miss it loads from SQL and writes back with a TTL. In Laravel, Cache::remember wraps this cleanly—pass a versioned key like shop:categories:v3, a TTL in seconds, and a closure that runs the Eloquent query only on miss. Pair this pattern with honest TTL rules per domain object. A category page that runs eight queries per request becomes one cache hit for five minutes, and MySQL load drops sharply without changing your schema.

When one admin action invalidates dozens of related keys, tags beat manual DEL lists. Laravel's Redis tag implementation lets you group keys: Cache::tags(['products', 'category:12'])->put on write, then Cache::tags(['products', 'category:12'])->flush after an update. Memcached 1.6.x cannot do this natively. If your catalogue changes often, that alone may justify Redis over Memcached on Laravel apps. Tie invalidation to write paths—flush tagged groups on admin updates, use version keys like products:v4 instead of hunting unknown dependents, and never cache user-specific auth or payment state globally.

A cache stampede hits when a hot key expires and hundreds of concurrent requests rebuild it at once—I've seen this spike p99 latency on high-traffic WooCommerce category pages during flash sales. Practical defences include probabilistic early expiry, mutex via SET NX so one worker rebuilds while others wait, stale-while-revalidate to serve old data while an async job refreshes, and request coalescing with Laravel Cache::lock wrapping the rebuild closure. Stale data is also a product decision: an hour-long price cache may suit a blog sidebar but is wrong for checkout.

Memcached 1.6.x remains excellent for pure GET/SET blob caches—it is lean for small objects and multi-threaded. Redis 8.10 adds native data structures, persistence, pub/sub, Lua scripting, and tag-based invalidation via application patterns. Choose Memcached when you need a stateless, horizontally sharded object cache and nothing else. Choose Redis when one service should handle cache, Laravel queues, sessions, rate limiting, and pub/sub together. On custom Laravel applications I reach for Redis first; WordPress object cache and Magento 2.4.x shops also commonly pair Redis with other layers like Varnish.

Redis is fast because it lives in RAM—that speed demands operational discipline. Set maxmemory to roughly seventy percent of available RAM on a dedicated box and use allkeys-lru as a sensible starting eviction policy. Enable requirepass or ACL users and bind to 127.0.0.1 unless you run a managed cluster. Choose RDB snapshots, AOF, or both when Redis holds more than disposable page cache—queues and session data are not throwaway. Monitor used_memory_rss, evicted_keys, instantaneous_ops_per_sec, and blocked_clients continuously. I run Redis on a private interface, never exposed to the public internet.

Yes—for small and mid-traffic sites, running Redis beside PHP on a client VPS at roughly Rs 2,500–5,000/month (~USD 19–37) is a common and workable setup. Watch RAM contention carefully: when Redis and MySQL compete for the same memory, latency spikes on both services. Set maxmemory caps so Redis cannot consume the entire box. Split Redis to its own instance once evicted_keys climb in logs or OOM kills appear. On larger Laravel deployments behind Adventure Third Pole Trek-scale booking traffic, isolating Redis early avoids painful emergency migrations.

Redis streams persist event logs and support consumer groups—useful for audit trails and async fan-out where messages must survive subscriber downtime. Pub/sub pushes ephemeral messages to subscribers instantly but stores nothing; dropped messages are expected if no subscriber is connected at publish time. Laravel broadcasting and websocket scaling often combine Redis pub/sub with websocket workers for real-time features. Do not confuse pub/sub with a durable queue. If your workload needs guaranteed delivery and replay, streams or Laravel's Redis-backed queue lists are the correct tool.

On a dedicated Redis server, set maxmemory to roughly seventy percent of available RAM and leave headroom for OS page cache and RDB fork spikes. The default maxmemory-policy allkeys-lru drops least-recently-used keys when memory fills—a sensible starting point for read-heavy sites. Rising evicted_keys in monitoring usually means TTL tuning or capacity work is overdue, not that eviction is inherently bad. Cache-only workloads can tolerate more aggressive eviction; queues, sessions, and semi-durable data need persistence planning and tighter capacity margins so keys are not dropped unexpectedly.

No—cache is the most common entry point, but Redis 8.10 routinely serves as session storage, job queues, rate limiting, pub/sub, leaderboards, and distributed locks on production Laravel and PHP stacks. That dual role is why I reach for Redis before Memcached on custom applications where one in-memory service should handle multiple concerns. The native data structures—hashes, sets, sorted sets, streams—are what keep Redis in the stack long after the first GET/SET optimisation ships. Treat it as architecture, not a bolt-on afterthought.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: