Azure Cache for Redis
Azure Cache for Redis is a fully managed, in-memory data store based on Redis. It’s used for caching, session management, leaderboards, rate limiting, and real-time messaging — the same use cases as AWS ElastiCache Redis.
Why Caching?
Section titled “Why Caching?”Without cache: Client ──► App Server ──► Database (50-100 ms)
With cache: Client ──► App Server ──► Redis (1-2 ms) ──hit──► return cached data │ └──miss──► Database ──► store in Redis ──► returnCaching reduces database load, lowers latency, and improves scalability.
| Tier | Description | Size | SLA | Key Features |
|---|---|---|---|---|
| Basic | Single node, no SLA | 250 MB – 53 GB | None | Development/testing only |
| Standard | Primary + replica | 250 MB – 53 GB | 99.9% | Replication, failover |
| Premium | Clustered, VNet, persistence | 6 GB – 1.2 TB | 99.9% | Clustering, geo-replication, data persistence |
| Enterprise | Redis Enterprise (RediSearch, RedisJSON, etc.) | Up to 10+ TB | 99.999% | Redis modules, active-active geo-replication |
| Enterprise Flash | Redis Enterprise on NVMe + RAM | Very large datasets | 99.999% | Cost-effective for large data |
Choosing a Tier
Section titled “Choosing a Tier”| Scenario | Tier |
|---|---|
| Local development, testing | Basic |
| Production with HA | Standard |
| Large datasets, clustering, VNet | Premium |
| Advanced data structures (JSON, search, time series) | Enterprise |
Creating a Cache
Section titled “Creating a Cache”# Create a Standard tier cache (6 GB, primary + replica)az redis create \ --resource-group myapp-rg \ --name myapp-cache \ --location eastus \ --sku Standard \ --vm-size C1 \ --enable-non-ssl-port false \ --minimum-tls-version 1.2
# Get connection detailsaz redis show \ --resource-group myapp-rg \ --name myapp-cache \ --query "{host: hostName, port: sslPort}" -o table
# Get access keyaz redis list-keys \ --resource-group myapp-rg \ --name myapp-cache \ --query "primaryKey" -o tsvConnecting to Redis
Section titled “Connecting to Redis”Python
Section titled “Python”import redis
r = redis.Redis( host="myapp-cache.redis.cache.windows.net", port=6380, password="<access-key>", ssl=True, decode_responses=True)
# Basic operationsr.set("user:123:name", "Alice", ex=3600) # set with 1 hour TTLname = r.get("user:123:name") # "Alice"With Managed Identity (Entra ID Authentication)
Section titled “With Managed Identity (Entra ID Authentication)”from azure.identity import DefaultAzureCredentialimport redis
credential = DefaultAzureCredential()token = credential.get_token("https://redis.azure.com/.default").token
r = redis.Redis( host="myapp-cache.redis.cache.windows.net", port=6380, username="<object-id-of-managed-identity>", password=token, ssl=True, decode_responses=True)Node.js
Section titled “Node.js”const Redis = require("ioredis");
const redis = new Redis({ host: "myapp-cache.redis.cache.windows.net", port: 6380, password: "<access-key>", tls: { servername: "myapp-cache.redis.cache.windows.net" },});
await redis.set("session:abc", JSON.stringify({ userId: 123 }), "EX", 1800);const session = JSON.parse(await redis.get("session:abc"));Caching Patterns
Section titled “Caching Patterns”Cache-Aside (Lazy Loading)
Section titled “Cache-Aside (Lazy Loading)”The most common pattern — application checks cache first, falls back to database:
def get_product(product_id): # 1. Check cache cached = r.get(f"product:{product_id}") if cached: return json.loads(cached)
# 2. Cache miss — fetch from database product = db.query("SELECT * FROM products WHERE id = %s", product_id)
# 3. Store in cache with TTL r.set(f"product:{product_id}", json.dumps(product), ex=600) # 10 min TTL
return productPros: Only caches data that is actually requested. Cons: First request is always slow (cache miss); stale data until TTL expires.
Write-Through
Section titled “Write-Through”Write to cache and database at the same time:
def update_product(product_id, data): # Write to database db.execute("UPDATE products SET ... WHERE id = %s", product_id) # Write to cache r.set(f"product:{product_id}", json.dumps(data), ex=600)Pros: Cache is always up to date. Cons: Write latency increases; unused data may fill cache.
Write-Behind (Write-Back)
Section titled “Write-Behind (Write-Back)”Write to cache immediately, flush to database asynchronously:
def update_product(product_id, data): # Write to cache r.set(f"product:{product_id}", json.dumps(data)) # Queue a database write r.lpush("db:write-queue", json.dumps({"table": "products", "id": product_id, "data": data}))Pros: Fastest writes. Cons: Risk of data loss if cache crashes before flush; complexity.
Cache Invalidation
Section titled “Cache Invalidation”| Strategy | When to Use |
|---|---|
| TTL (Time-to-Live) | Simple, good enough for most cases |
| Explicit delete | Delete cache key when data changes |
| Pub/Sub | Notify other services to invalidate their local caches |
| Event-driven | Use Event Grid or Service Bus to trigger cache invalidation |
# Explicit invalidation on updatedef update_product(product_id, data): db.execute("UPDATE products SET ...") r.delete(f"product:{product_id}") # next read will re-cacheCommon Use Cases
Section titled “Common Use Cases”Session Storage
Section titled “Session Storage”import uuid, json
def create_session(user_id): session_id = str(uuid.uuid4()) r.set(f"session:{session_id}", json.dumps({ "user_id": user_id, "created": "2026-02-17T10:00:00Z" }), ex=1800) # 30-minute session return session_id
def get_session(session_id): data = r.get(f"session:{session_id}") if data: r.expire(f"session:{session_id}", 1800) # sliding expiration return json.loads(data) return NoneLeaderboard
Section titled “Leaderboard”# Add scorer.zadd("leaderboard:daily", {"player-alice": 1500, "player-bob": 1200})
# Get top 10top_10 = r.zrevrange("leaderboard:daily", 0, 9, withscores=True)# [("player-alice", 1500), ("player-bob", 1200), ...]
# Get player rankrank = r.zrevrank("leaderboard:daily", "player-alice") # 0 (first place)Rate Limiting
Section titled “Rate Limiting”def is_rate_limited(user_id, limit=100, window=60): key = f"ratelimit:{user_id}" current = r.incr(key) if current == 1: r.expire(key, window) return current > limitPub/Sub (Real-Time Messaging)
Section titled “Pub/Sub (Real-Time Messaging)”# Publisherr.publish("notifications", json.dumps({ "user_id": 123, "message": "Your order has shipped"}))
# Subscriber (in a separate process)pubsub = r.pubsub()pubsub.subscribe("notifications")for message in pubsub.listen(): if message["type"] == "message": handle_notification(json.loads(message["data"]))Distributed Locking
Section titled “Distributed Locking”import time
def acquire_lock(lock_name, timeout=10): """Acquire a distributed lock using Redis SET NX.""" lock_key = f"lock:{lock_name}" acquired = r.set(lock_key, "locked", nx=True, ex=timeout) return acquired
def release_lock(lock_name): r.delete(f"lock:{lock_name}")
# Usageif acquire_lock("process-order-123"): try: process_order(123) finally: release_lock("process-order-123")Clustering (Premium Tier)
Section titled “Clustering (Premium Tier)”Premium tier supports Redis Cluster for horizontal scaling:
# Create a Premium cache with 3 shardsaz redis create \ --resource-group myapp-rg \ --name myapp-cache-premium \ --location eastus \ --sku Premium \ --vm-size P1 \ --shard-count 3Data is automatically distributed across shards by hash slot. More shards = more throughput and memory.
Geo-Replication
Section titled “Geo-Replication”Link two Premium caches for cross-region replication:
# Link primary (East US) to secondary (West Europe) for geo-replicationaz redis server-link create \ --name myapp-cache-primary \ --resource-group myapp-rg \ --server-to-link /subscriptions/.../myapp-cache-secondary \ --replication-role SecondaryData Persistence (Premium Tier)
Section titled “Data Persistence (Premium Tier)”| Type | Description | RPO |
|---|---|---|
| RDB snapshots | Point-in-time snapshots to Azure Storage | Minutes (snapshot interval) |
| AOF (Append Only File) | Log every write operation | ~1 second |
# Enable RDB persistence (snapshot every 15 minutes)az redis update \ --resource-group myapp-rg \ --name myapp-cache-premium \ --set "redisConfiguration.rdb-backup-enabled=true" \ --set "redisConfiguration.rdb-backup-frequency=15" \ --set "redisConfiguration.rdb-storage-connection-string=<connection-string>"VNet Integration (Premium Tier)
Section titled “VNet Integration (Premium Tier)”Inject the cache into your VNet for private access:
az redis create \ --resource-group myapp-rg \ --name myapp-cache-private \ --location eastus \ --sku Premium \ --vm-size P1 \ --subnet-id /subscriptions/.../subnets/redis-subnetWith VNet integration, the cache is only accessible from within the VNet — no public endpoint.
Monitoring
Section titled “Monitoring”| Metric | What to Watch |
|---|---|
| Cache hit ratio | Should be >90%; low ratio means inefficient caching |
| Used memory | Approaching max → eviction starts |
| Connected clients | Unexpected spikes may indicate connection leaks |
| Server load | >80% sustained → scale up or out |
| Evicted keys | Keys removed due to memory pressure |
| Latency | P99 should be <5 ms for same-region access |
# View cache metricsaz monitor metrics list \ --resource /subscriptions/.../Microsoft.Cache/redis/myapp-cache \ --metric "cacheHits,cacheMisses,usedmemory,serverLoad" \ --interval PT5MAzure Cache for Redis vs AWS ElastiCache
Section titled “Azure Cache for Redis vs AWS ElastiCache”| Feature | Azure Cache for Redis | AWS ElastiCache Redis |
|---|---|---|
| Managed | Fully managed | Fully managed |
| Clustering | Premium tier | Yes (cluster mode) |
| Geo-replication | Premium (active-passive), Enterprise (active-active) | Global Datastore (active-passive) |
| Modules | Enterprise tier (RediSearch, RedisJSON, RedisTimeSeries) | Not supported |
| Entra ID auth | Yes | IAM auth |
| VNet integration | Premium tier (VNet injection) | VPC subnets |
| Persistence | RDB + AOF (Premium) | RDB + AOF |
Key Takeaways
Section titled “Key Takeaways”- Azure Cache for Redis is a fully managed in-memory data store for sub-millisecond access.
- Cache-aside is the most common pattern — check cache first, fall back to database on miss.
- Common use cases: session storage, leaderboards (sorted sets), rate limiting (INCR + EXPIRE), pub/sub, distributed locks.
- Standard tier provides HA with replication; Premium adds clustering, VNet, persistence, and geo-replication.
- Enterprise tier unlocks Redis modules (RediSearch, RedisJSON) and active-active geo-replication.
- Monitor cache hit ratio (>90%) and server load (<80%) to ensure healthy performance.