The most surprising thing about Redis BGSAVE performance is that the cost isn’t in the saving itself, but in the forking of the main Redis process.

Let’s see it in action. Imagine a Redis instance with 100GB of data.

# On the server running Redis
$ ps aux | grep redis
redis     1234  0.0  0.1 123456 7890 ?        Ssl    Jan01   0:05 redis-server 127.0.0.1:6379

When BGSAVE is triggered (manually via BGSAVE command, or automatically by save configurations like save 3600 1), Redis doesn’t start writing data to disk immediately. Instead, it calls fork(). This system call creates a copy of the parent process. On Linux, this is typically a copy-on-write (COW) mechanism. The child process (the one doing the saving) initially shares the same memory pages as the parent. Only when either process modifies a page does the OS create a separate copy of that page for the modifying process.

# After BGSAVE is initiated
$ ps aux | grep redis
redis     1234  0.0  0.1 123456 7890 ?        Ssl    Jan01   0:05 redis-server 127.0.0.1:6379
redis     5678  0.0  0.0 123456 1234 ?        R      10:30   0:00 redis-server 127.0.0.1:6379 *save*

Notice the *save* marker and the R state for the child process, indicating it’s running. The performance impact comes here. The fork() call itself can be a bottleneck on systems with a lot of memory. The OS has to create a new process descriptor, copy page tables, and set up shared memory. If your Redis instance has hundreds of gigabytes of RAM, this fork operation can take seconds, during which the main Redis process is blocked from processing new commands. This blocking is what people perceive as "Redis freezing" during BGSAVE.

The actual writing of data to disk happens asynchronously by the child process. The main process unblocks as soon as the fork() completes. The real performance cost for BGSAVE is directly proportional to the amount of memory your Redis instance is using at the time of the fork.

To understand the levers you control, consider these:

  1. maxmemory and eviction policies: While not directly controlling BGSAVE cost, setting maxmemory and an appropriate eviction policy (volatile-lru, allkeys-lru, etc.) can keep the total memory footprint smaller, thus reducing the cost of fork. A smaller dataset means fewer memory pages to copy/map during fork.

    • Config:
      maxmemory 50gb
      maxmemory-policy allkeys-lru
      
    • Why it works: Limits the total memory Redis can use, preventing the dataset from growing indefinitely and making fork faster.
  2. save directive tuning: The save directive triggers automatic BGSAVE. By tuning these, you can reduce the frequency of BGSAVE operations.

    • Config:
      # Default:
      # save 900 1
      # save 300 10
      # save 60 10000
      
      # Less frequent saves:
      save 3600 1
      save 1800 10
      save 600 100
      
    • Why it works: Fewer BGSAVE calls mean fewer fork operations and less potential for blocking. You’re trading RDB persistence frequency for reduced operational cost.
  3. appendonly (AOF): Enabling AOF persistence with appendfsync always or appendfsync everysec provides an alternative to RDB. If you can tolerate slightly less durable data (with everysec), AOF can often be more performant during writes and BGSAVE indirectly. Redis can perform BGSAVE for AOF rewriting (BGREWRITEAOF) which also uses fork. Tuning BGREWRITEAOF frequency is similar to tuning BGSAVE frequency.

    • Config:
      appendonly yes
      appendfsync everysec
      
    • Why it works: appendfsync everysec offers a good balance between durability and performance. If AOF is enabled, BGSAVE is only needed to compact the AOF file via BGREWRITEAOF, which also uses fork.
  4. System-level tuning (vm.overcommit_memory): This is a critical OS parameter. If vm.overcommit_memory is set to 0 (the default), the kernel checks if there’s enough memory available before allowing fork. If not, fork can fail. Setting vm.overcommit_memory = 1 tells the kernel to always allow memory overcommit, which can make fork more reliable in memory-constrained environments but requires careful monitoring of actual memory usage.

    • Config (as root):
      # Check current value
      sysctl vm.overcommit_memory
      # Temporarily set (until reboot)
      sysctl -w vm.overcommit_memory=1
      # Persist across reboots (edit /etc/sysctl.conf)
      # vm.overcommit_memory = 1
      
    • Why it works: Prevents fork from failing due to perceived lack of memory by the kernel, allowing the BGSAVE child process to be created more reliably.
  5. System-level tuning (vm.dirty_ratio / vm.dirty_background_ratio): These kernel parameters control how much dirty data (data modified but not yet written to disk) can accumulate before the system starts flushing it. High amounts of dirty data can increase the time and memory overhead of fork.

    • Config (as root):
      # Example: Increase background dirty data ratio
      sysctl -w vm.dirty_background_ratio=10 # Default might be 5
      
    • Why it works: Allows more dirty pages to exist before the kernel actively starts writing them back, potentially reducing contention during the fork operation itself.
  6. Hardware: Faster CPUs and, more importantly, faster RAM and I/O subsystems (SSDs vs. HDDs) will reduce the time taken by the fork operation and the subsequent data writing.

    • Check: lscpu, free -h, lsblk
    • Why it works: Direct hardware improvement for the underlying OS operations that Redis relies on.
  7. Running BGSAVE manually during low-traffic periods: If you need to trigger a save and want to minimize impact, do it when your application’s traffic is low. This means fewer client commands are being processed by the main Redis process while it’s blocked during the fork.

    • Command:
      redis-cli BGSAVE
      
    • Why it works: Reduces the perceived latency for your application because the main thread is blocked for a shorter duration relative to the total command processing time.

The next error you’ll hit after tuning BGSAVE performance is likely related to network connectivity or client-side timeouts if your application isn’t robust enough to handle brief periods of Redis unresponsiveness.

Want structured learning?

Take the full Redis course →