Back to Blog
5 min read

Redis Persistence Options in Azure: RDB and AOF Explained

Redis persistence ensures your in-memory data survives restarts and failures. Azure Cache for Redis supports both RDB (point-in-time snapshots) and AOF (Append Only File) persistence, available on Premium tier.

Understanding Persistence Options

  • RDB (Redis Database): Point-in-time snapshots at configured intervals
  • AOF (Append Only File): Logs every write operation for replay
  • RDB + AOF: Combined for both fast recovery and minimal data loss

Configuring RDB Persistence

# Create Redis with RDB persistence
az redis create \
    --resource-group myResourceGroup \
    --name myredispersistent \
    --location eastus \
    --sku Premium \
    --vm-size P1 \
    --enable-non-ssl-port false \
    --minimum-tls-version 1.2

# Configure RDB backup
az redis update \
    --resource-group myResourceGroup \
    --name myredispersistent \
    --set redisConfiguration.rdb-backup-enabled=true \
    --set redisConfiguration.rdb-backup-frequency=60 \
    --set redisConfiguration.rdb-storage-connection-string="<storage-connection-string>"

Using ARM Template:

{
    "type": "Microsoft.Cache/Redis",
    "apiVersion": "2020-12-01",
    "name": "myredispersistent",
    "location": "eastus",
    "properties": {
        "sku": {
            "name": "Premium",
            "family": "P",
            "capacity": 1
        },
        "redisConfiguration": {
            "rdb-backup-enabled": "true",
            "rdb-backup-frequency": "60",
            "rdb-storage-connection-string": "[parameters('storageConnectionString')]"
        },
        "enableNonSslPort": false,
        "minimumTlsVersion": "1.2"
    }
}

Configuring AOF Persistence

# Enable AOF persistence
az redis update \
    --resource-group myResourceGroup \
    --name myredispersistent \
    --set redisConfiguration.aof-backup-enabled=true \
    --set redisConfiguration.aof-storage-connection-string-0="<primary-storage-connection>" \
    --set redisConfiguration.aof-storage-connection-string-1="<secondary-storage-connection>"

Understanding the Tradeoffs

# Python - Demonstrating persistence behavior
import redis
import time
from datetime import datetime

class PersistenceDemo:
    def __init__(self, host, port=6380, password=None):
        self.redis = redis.StrictRedis(
            host=host,
            port=port,
            password=password,
            ssl=True,
            decode_responses=True
        )

    def demonstrate_rdb_behavior(self):
        """
        RDB Characteristics:
        - Snapshots taken at intervals (e.g., every 60 minutes)
        - Minimal performance impact during backup
        - Potential data loss = data written since last snapshot
        - Faster restart times (single file to load)
        """

        # Write data
        for i in range(1000):
            self.redis.set(f"rdb_test:{i}", f"value_{i}")

        # If server crashes before next snapshot, this data could be lost
        print(f"Wrote 1000 keys at {datetime.now()}")
        print("If RDB backup interval is 60 min, up to 60 min of data could be lost")

    def demonstrate_aof_behavior(self):
        """
        AOF Characteristics:
        - Every write operation logged
        - Higher durability (can configure fsync frequency)
        - Larger file size than RDB
        - Slower restart (replays all operations)
        """

        # AOF fsync policies:
        # - always: fsync after every write (safest, slowest)
        # - everysec: fsync every second (good balance)
        # - no: let OS decide (fastest, least safe)

        # Each write is appended to AOF
        start = time.time()
        for i in range(1000):
            self.redis.set(f"aof_test:{i}", f"value_{i}")

        elapsed = time.time() - start
        print(f"Wrote 1000 keys in {elapsed:.2f}s")
        print("With AOF, maximum data loss is last second of writes (everysec policy)")

    def get_persistence_info(self):
        """Get current persistence configuration"""
        info = self.redis.info('persistence')

        return {
            'rdb_last_save_time': info.get('rdb_last_save_time'),
            'rdb_last_bgsave_status': info.get('rdb_last_bgsave_status'),
            'rdb_current_bgsave_time_sec': info.get('rdb_current_bgsave_time_sec'),
            'aof_enabled': info.get('aof_enabled'),
            'aof_current_size': info.get('aof_current_size'),
            'aof_last_rewrite_time_sec': info.get('aof_last_rewrite_time_sec'),
            'loading': info.get('loading')
        }

Managing Backups and Recovery

// C# - Managing Redis backups
using Azure.ResourceManager.Redis;

public class RedisBackupManager
{
    private readonly RedisResource _redis;
    private readonly string _storageAccountConnectionString;

    public async Task TriggerManualBackupAsync()
    {
        // Export current data to storage
        var exportData = new ExportRDBParameters
        {
            Prefix = $"backup-{DateTime.UtcNow:yyyyMMdd-HHmmss}",
            Container = _storageAccountConnectionString
        };

        await _redis.ExportDataAsync(WaitUntil.Completed, exportData);
    }

    public async Task ImportBackupAsync(string backupPath)
    {
        // Import data from storage backup
        var importData = new ImportRDBParameters
        {
            Files = new[] { backupPath }
        };

        await _redis.ImportDataAsync(WaitUntil.Completed, importData);
    }

    public async Task ScheduleBackupRetention()
    {
        // Implement backup rotation
        var backups = await ListBackupsAsync();

        var cutoffDate = DateTime.UtcNow.AddDays(-30);
        var oldBackups = backups.Where(b => b.CreatedAt < cutoffDate);

        foreach (var backup in oldBackups)
        {
            await DeleteBackupAsync(backup.Name);
        }
    }
}

Monitoring Persistence Performance

// Node.js - Monitor persistence metrics
const Redis = require('ioredis');

class PersistenceMonitor {
    constructor(config) {
        this.redis = new Redis({
            host: config.host,
            port: 6380,
            password: config.password,
            tls: { servername: config.host }
        });
    }

    async getPersistenceMetrics() {
        const info = await this.redis.info('persistence');
        const lines = info.split('\r\n');
        const metrics = {};

        lines.forEach(line => {
            const [key, value] = line.split(':');
            if (key && value) {
                metrics[key] = value;
            }
        });

        return {
            // RDB metrics
            rdb: {
                lastSaveTime: new Date(parseInt(metrics.rdb_last_save_time) * 1000),
                changesSinceLastSave: parseInt(metrics.rdb_changes_since_last_save),
                lastBgsaveStatus: metrics.rdb_last_bgsave_status,
                lastBgsaveDuration: parseInt(metrics.rdb_last_bgsave_time_sec),
                inProgress: parseInt(metrics.rdb_current_bgsave_time_sec) !== -1
            },
            // AOF metrics
            aof: {
                enabled: metrics.aof_enabled === '1',
                rewriteInProgress: metrics.aof_rewrite_in_progress === '1',
                currentSize: parseInt(metrics.aof_current_size),
                baseSize: parseInt(metrics.aof_base_size),
                pendingRewrites: parseInt(metrics.aof_pending_rewrite),
                lastRewriteDuration: parseInt(metrics.aof_last_rewrite_time_sec)
            },
            // Loading status (during recovery)
            loading: {
                inProgress: metrics.loading === '1',
                loadedBytes: parseInt(metrics.loading_loaded_bytes) || 0,
                totalBytes: parseInt(metrics.loading_total_bytes) || 0,
                progress: metrics.loading_total_bytes
                    ? (parseInt(metrics.loading_loaded_bytes) / parseInt(metrics.loading_total_bytes) * 100).toFixed(2)
                    : 0
            }
        };
    }

    async monitorBackgroundSave(callback) {
        const checkInterval = setInterval(async () => {
            const metrics = await this.getPersistenceMetrics();

            callback(metrics);

            if (metrics.rdb.inProgress) {
                console.log('Background save in progress...');
            }
        }, 1000);

        return () => clearInterval(checkInterval);
    }
}

// Usage
const monitor = new PersistenceMonitor(config);

monitor.monitorBackgroundSave((metrics) => {
    if (metrics.rdb.changesSinceLastSave > 10000) {
        console.log('Warning: Many changes since last save');
    }
});

Persistence Best Practices

# Configuration recommendations based on use case

# E-commerce session store
session-store:
  persistence: RDB
  rdb-backup-frequency: 60  # hourly
  rationale: "Sessions can be recreated; RDB provides good balance"

# Financial transaction cache
financial-cache:
  persistence: AOF
  aof-fsync: everysec
  rationale: "Minimize data loss for monetary data"

# Gaming leaderboard
leaderboard:
  persistence: RDB + AOF
  rdb-backup-frequency: 360  # every 6 hours
  rationale: "Fast recovery (RDB) + minimal loss (AOF)"

# Temporary computation cache
temp-cache:
  persistence: none
  rationale: "Data is ephemeral and can be recomputed"

Best Practices

  1. Choose based on requirements: RDB for speed, AOF for durability
  2. Use separate storage accounts: For primary and secondary AOF
  3. Monitor save times: Long saves indicate performance issues
  4. Test recovery procedures: Regularly verify backup integrity
  5. Size storage appropriately: AOF files grow over time

Redis persistence transforms an in-memory cache into a durable data store, providing the reliability needed for critical applications while maintaining Redis’s performance characteristics.

Michael John Peña

Michael John Peña

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