
September 08, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Most teams install Redis to shave milliseconds off database reads, then treat every key like a disposable blob with a TTL. That works until you need ordered leaderboards, idempotent job queues, live presence, or abuse-resistant rate limits. Redis data structures beyond cache—Lists, Sets, Sorted Sets, Hashes, Streams, Bitmaps, and HyperLogLog—let you model real application state in memory with atomic operations. On production Laravel queues with Redis and legal-tech portals I've maintained, the same Redis instance often handles cache, sessions, and operational data. This guide maps each structure to a concrete web-app problem and shows when Redis should stay secondary to MySQL 9.7 or PostgreSQL 18.
What Redis data structures exist beyond a simple key-value cache?
Redis is an in-memory data structure server, not a dumb key-value store with expiration. Every key has a type, and commands operate on that type atomically. Understanding the type system is the first step toward using Redis data structures beyond cache correctly.
The core types ship in every Redis 8.10 deployment. Strings hold scalars, counters, and small JSON blobs. Lists give FIFO/LIFO queues. Sets store unique members with O(1) membership checks. Sorted Sets (ZSET) add a score for ranking. Hashes map fields inside one key—ideal for objects. Streams append event logs with consumer groups. Bitmaps and HyperLogLog compress boolean flags and cardinality estimates.
A common mistake is storing everything as a JSON string. You lose atomic increments, range queries, and memory-efficient encodings. Redis chooses compact internal representations (ziplists, listpacks) when values stay small. Large values force raw allocations and spike RAM usage.
For authoritative type documentation, see the Redis data types reference. Pair that with your framework's Redis client—Laravel 13 uses predis/predis or phpredis via the redis connection in config/database.php.
How do you use Redis Lists and Streams for queues and event processing?
Job queues are the most familiar non-cache Redis pattern. Laravel's default Redis queue driver pushes JSON payloads onto a List keyed like queues:default. Workers call BLPOP for blocking dequeue semantics. That is simple and fast for millions of jobs per day on a single legal-tech portal or eCommerce site.
Lists for FIFO work queues
Raw List operations look like this:
LPUSH queues:emails '{"id":9912,"to":"client@example.com"}'
BRPOP queues:emails 30
LLEN queues:emails Lists work well when one consumer group owns the queue and you do not need replay. Laravel wraps this with retries, backoff, and failed-job tables in MySQL. I've seen teams run email notifications, PDF generation, and webhook delivery entirely through Redis Lists backed by Horizon or supervisor workers.
Streams for durable event logs
Redis Streams add append-only logs with consumer groups—closer to Kafka lite than a simple List. Each entry gets an auto ID like 1735123456789-0. Consumer groups track per-consumer offsets and support acknowledgment.
XADD booking-events * action "confirmed" booking_id "8841"
XGROUP CREATE booking-events processors $ MKSTREAM
XREADGROUP GROUP processors worker1 COUNT 10 STREAMS booking-events >
XACK booking-events processors 1735123456789-0 On a production Laravel application with booking workflows, Streams suit audit trails and fan-out events. Multiple services can read the same stream without stealing jobs from each other. Persist Streams when data loss would hurt—configure RDB snapshots or AOF as described in our Redis persistence and clustering guide.
Choose Lists when Laravel's queue abstraction is enough. Choose Streams when you need replay, multiple independent readers, or an append-only domain event log. Do not mirror every MySQL row into a Stream—that duplicates source-of-truth problems.
When should you use Redis Sorted Sets instead of SQL for rankings and scheduling?
Sorted Sets store a unique member with a numeric score. Redis keeps members ordered by score in O(log N) time. That makes them ideal for leaderboards, priority queues, and time-window rate limiting where score equals a Unix timestamp.
Leaderboards and top-N queries
ZADD trek:leaderboard 9840 "team_alpha" 8720 "team_beta"
ZREVRANGE trek:leaderboard 0 9 WITHSCORES
ZINCRBY trek:leaderboard 50 "team_alpha" On a booking platform like Adventure Third Pole Trek, a Sorted Set can hold live availability slots scored by datetime. Range queries fetch slots in the next hour without scanning SQL indexes under peak load. The relational database still owns bookings; Redis holds the hot read model.
Delayed and scheduled tasks
Score-as-timestamp patterns power delayed jobs. Laravel's Redis queue driver uses Sorted Sets internally for scheduled work. A poller fetches entries with score less than time() and pushes them to the ready List.
ZADD delayed:jobs 1735200000 '{"job":"SendReminder"}'
ZRANGEBYSCORE delayed:jobs 0 1735199999 LIMIT 0 100 Sorted Sets beat raw SQL when update frequency is high and you only need ordered slices. They lose when you need complex joins, ACID transactions across tables, or reports spanning months. Keep financial totals in MySQL or PostgreSQL.
| Criteria | Redis Sorted Set | MySQL / PostgreSQL |
|---|---|---|
| Top-N by score | Native, sub-ms | Index + ORDER BY, slower at scale |
| Complex filters | Limited | Full SQL power |
| Durability | Depends on persistence config | ACID by default |
| Memory cost | All data in RAM | Disk-backed, cheaper per GB |
| Concurrent increments | Atomic ZINCRBY | Requires row locks or careful SQL |
How do Hashes, Sets, and Bitmaps model session state and feature flags?
Hashes store field-value pairs under one key. They fit user session fragments, shopping-cart lines, or configuration blobs without serializing the entire object on every field change.
HSET session:abc123 user_id 441 role "admin" last_seen 1735123400
HGET session:abc123 user_id
HINCRBY cart:user441 item_992 1 Sets store unique strings with union/intersection/difference in one round trip. Tag systems, "users online in room X", and permission caches use Sets well. On WooCommerce stacks, object cache plugins may use Sets indirectly through WordPress object cache with Redis—know the underlying types when debugging memory.
Bitmaps for compact daily active tracking
Bitmaps treat a string as a bit array. Set bit offsets for user IDs to track daily active users in kilobytes instead of megabytes.
SETBIT dau:2026-09-08 441 1
GETBIT dau:2026-09-08 441
BITCOUNT dau:2026-09-08 Bitmaps assume numeric user IDs or a stable mapping. UUID strings do not work as offsets without a lookup table. HyperLogLog goes further for approximate unique counts—add elements with PFADD and read cardinality with PFCOUNT using roughly 12 KB per key regardless of billions of entries.
How do you implement rate limiting and pub/sub with Redis primitives?
Rate limiting belongs in Redis when you need sub-millisecond checks at the edge of every API request. The sliding-window pattern uses a Sorted Set keyed by client identifier. Each request adds a member scored by timestamp, trims old entries, and counts the window.
ZADD rl:192.168.1.50 1735123456 "req-991"
ZREMRANGEBYSCORE rl:192.168.1.50 0 1735123156
ZCARD rl:192.168.1.50 Wrap that in a Lua script or Redis 8.10 function for atomicity. Laravel 13's RateLimiter facade can use Redis as the backend. For deeper patterns, see our guide on API rate limiting and abuse prevention. Test edge cases with the regex tester when building path-based limit keys.
Pub/Sub and real-time presence
Pub/Sub channels push messages to subscribers with fire-and-forget semantics—no persistence if no listener is connected. That fits live dashboard updates and WebSocket broadcasting in Laravel. Do not use Pub/Sub as a job queue; messages vanish when workers are offline.
PUBLISH room:442 "user_joined:991"
SUBSCRIBE room:442 For presence ("who is online"), combine Sets with TTL keys or Hash fields updated on heartbeat. Symfony 8.1 apps using Mercure or SSE can use Redis pub/sub as the fan-out bus behind a single publisher process.
What operational rules keep Redis safe when it holds more than cache?
Once Redis stores queues and rankings, eviction policy matters. Never run production workload Redis with maxmemory-policy allkeys-lru unless every key is truly disposable. Prefer noeviction or volatile-lru on keys with TTL only. Monitor memory with INFO memory and set maxmemory below 80% of available RAM.
- Separate logical databases or instances: cache on DB 0, queues on a dedicated instance.
- Enable AOF with
appendfsync everysecwhen Streams or Lists must survive restarts. - Name keys with namespaces:
app:env:entity:idavoids collisions across projects. - Set TTL on ephemeral Sorted Sets and rate-limit keys to prevent silent memory growth.
- Run
redis-cli --bigkeysandMEMORY USAGE keyduring incidents.
On shared EC2 boxes where I run Deployer 7 releases for sister legal-tech sites, Redis 8.10 sits beside PHP-FPM 8.4. Wrong ownership or a missing persistence directory has caused more outages than slow queries. Our Linux system administration practice treats Redis backups like database dumps—nightly RDB copy off-box.
Compare Redis against alternatives before committing. Memcached still wins pure cache throughput for some read-heavy Magento 2.4.x storefronts—see Redis vs Memcached vs Dragonfly and Magento 2 Redis and Varnish. Symfony apps may layer APCu for local cache and Redis for shared state via the Symfony cache component.
Laravel integration stays straightforward. Inject the Redis connection and call native commands through Redis::command() or the connection()->client() handle:
use Illuminate\Support\Facades\Redis;
Redis::zadd('leaderboard', 1200, 'user_441');
$top = Redis::zrevrange('leaderboard', 0, 9, ['WITHSCORES' => true]); The official Laravel 13 Redis documentation covers connections, clustering, and Horizon. For cache-specific patterns—not structures—read Redis caching patterns for web apps and Laravel cache tags with Redis.
When scoping enterprise application development or API development projects, document which structures hold authoritative versus derived data. Client portals with document uploads—like those in our Mijar Law Associates portfolio—should keep file metadata in SQL and use Redis only for notification queues and presence.
Validate JSON payloads stored in Lists or Streams with the JSON formatter during debugging. For notification fan-out beyond Redis, Laravel supports multi-channel delivery documented in Laravel notifications beyond email. Ongoing tuning falls under support and maintenance and testing and optimization engagements.
Key Takeaways
- Match the Redis type to the access pattern—Lists for FIFO jobs, Streams for replayable events, Sorted Sets for rankings and sliding-window limits.
- Never store everything as JSON strings; you lose atomic ops and compact encodings that Redis 8.10 applies automatically.
- Split cache, queue, and session Redis instances so LRU eviction cannot drop in-flight jobs.
- Use HyperLogLog and Bitmaps for analytics counters where approximate or daily granularity is acceptable.
- Keep financial records and audit trails in MySQL or PostgreSQL; Redis holds hot, derived, or ephemeral state.
- Enable persistence (AOF or RDB) whenever Redis data structures beyond cache would be expensive to rebuild from SQL.
People Also Ask
Is Redis only good for caching?
No. Redis supports Lists, Sets, Sorted Sets, Hashes, Streams, Bitmaps, and HyperLogLog with atomic commands. Production PHP apps use it for job queues, pub/sub, rate limiting, leaderboards, and session storage alongside—or instead of—pure TTL cache keys.
What is the difference between Redis Lists and Streams?
Lists are simple FIFO queues ideal for single-consumer workloads like Laravel's Redis queue driver. Streams are append-only logs with consumer groups, message IDs, and acknowledgment—better when multiple services must read the same events or you need replay after failure.
When should HyperLogLog replace a Set for counting?
Use HyperLogLog when you need approximate unique counts—page views, daily visitors, campaign reach—and can accept roughly 0.81% error. Use Sets when membership must be exact, such as permission checks or deduplicated coupon redemptions.
Can Redis replace MySQL for application data?
Redis should not replace relational databases as the system of record. It excels at speed-layer patterns: cache, queues, rankings, and real-time counters. Orders, user accounts, and compliance data belong in MySQL 9.7 or PostgreSQL 18 with Redis mirroring hot subsets.
Build faster systems with the right Redis primitives
Redis data structures beyond cache turn a memory server into a practical speed layer for queues, rankings, presence, and abuse control—if you pick types deliberately and isolate eviction policies. Start with one non-cache use case, measure memory and persistence requirements, and keep SQL as source of truth. Need help designing Redis into a Laravel 13 or Symfony 8.1 stack? Contact us or explore custom software development to scope your next production deployment.
Frequently Asked Questions
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.

