Back to Blog
3 min read

Azure Cache for Redis: Caching Patterns

Azure Cache for Redis provides in-memory caching for high-performance applications. Sub-millisecond latency for frequently accessed data.

Creating Redis Cache

az redis create \
    --name myrediscache \
    --resource-group myRG \
    --location eastus \
    --sku Standard \
    --vm-size C1

Basic Operations (.NET)

using StackExchange.Redis;

// Connection
var redis = ConnectionMultiplexer.Connect("myrediscache.redis.cache.windows.net:6380,password=xxx,ssl=True");
var db = redis.GetDatabase();

// Set value
await db.StringSetAsync("user:123", JsonSerializer.Serialize(user));

// Get value
var json = await db.StringGetAsync("user:123");
var user = JsonSerializer.Deserialize<User>(json);

// Set with expiration
await db.StringSetAsync("session:abc", "data", TimeSpan.FromMinutes(30));

// Delete
await db.KeyDeleteAsync("user:123");

Cache-Aside Pattern

public async Task<User> GetUserAsync(string userId)
{
    var cacheKey = $"user:{userId}";

    // Try cache first
    var cached = await _redis.StringGetAsync(cacheKey);
    if (cached.HasValue)
    {
        return JsonSerializer.Deserialize<User>(cached);
    }

    // Cache miss - get from database
    var user = await _dbContext.Users.FindAsync(userId);

    if (user != null)
    {
        // Store in cache
        await _redis.StringSetAsync(
            cacheKey,
            JsonSerializer.Serialize(user),
            TimeSpan.FromMinutes(15)
        );
    }

    return user;
}

public async Task UpdateUserAsync(User user)
{
    // Update database
    _dbContext.Users.Update(user);
    await _dbContext.SaveChangesAsync();

    // Invalidate cache
    await _redis.KeyDeleteAsync($"user:{user.Id}");
}

Write-Through Pattern

public async Task SaveOrderAsync(Order order)
{
    // Write to cache AND database atomically
    var tasks = new List<Task>
    {
        _redis.StringSetAsync($"order:{order.Id}", JsonSerializer.Serialize(order)),
        _orderRepository.SaveAsync(order)
    };

    await Task.WhenAll(tasks);
}

Distributed Locking

public async Task<bool> TryAcquireLockAsync(string resource, TimeSpan expiry)
{
    var lockKey = $"lock:{resource}";
    var lockValue = Guid.NewGuid().ToString();

    // SET NX (only if not exists)
    var acquired = await _redis.StringSetAsync(
        lockKey,
        lockValue,
        expiry,
        When.NotExists
    );

    return acquired;
}

public async Task ReleaseLockAsync(string resource, string lockValue)
{
    var lockKey = $"lock:{resource}";

    // Only delete if we own the lock (Lua script for atomicity)
    var script = @"
        if redis.call('get', KEYS[1]) == ARGV[1] then
            return redis.call('del', KEYS[1])
        else
            return 0
        end";

    await _redis.ScriptEvaluateAsync(script,
        new RedisKey[] { lockKey },
        new RedisValue[] { lockValue }
    );
}

Pub/Sub Messaging

// Publisher
var pub = redis.GetSubscriber();
await pub.PublishAsync("notifications", JsonSerializer.Serialize(notification));

// Subscriber
var sub = redis.GetSubscriber();
await sub.SubscribeAsync("notifications", (channel, message) =>
{
    var notification = JsonSerializer.Deserialize<Notification>(message);
    ProcessNotification(notification);
});

Hash Data Structures

// Store object as hash
await db.HashSetAsync("user:123", new HashEntry[]
{
    new HashEntry("name", "John"),
    new HashEntry("email", "john@example.com"),
    new HashEntry("loginCount", 42)
});

// Get single field
var name = await db.HashGetAsync("user:123", "name");

// Increment field
await db.HashIncrementAsync("user:123", "loginCount");

// Get all fields
var entries = await db.HashGetAllAsync("user:123");

Sorted Sets for Leaderboards

// Add score
await db.SortedSetAddAsync("leaderboard", "player1", 1500);
await db.SortedSetAddAsync("leaderboard", "player2", 2200);

// Get top 10
var topPlayers = await db.SortedSetRangeByRankWithScoresAsync(
    "leaderboard",
    0, 9,
    Order.Descending
);

// Get player rank
var rank = await db.SortedSetRankAsync("leaderboard", "player1", Order.Descending);

Redis caching transforms application performance at scale.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.