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:
-
maxmemoryand eviction policies: While not directly controllingBGSAVEcost, settingmaxmemoryand an appropriate eviction policy (volatile-lru,allkeys-lru, etc.) can keep the total memory footprint smaller, thus reducing the cost offork. A smaller dataset means fewer memory pages to copy/map duringfork.- 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
forkfaster.
- Config:
-
savedirective tuning: Thesavedirective triggers automaticBGSAVE. By tuning these, you can reduce the frequency ofBGSAVEoperations.- 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
BGSAVEcalls mean fewerforkoperations and less potential for blocking. You’re trading RDB persistence frequency for reduced operational cost.
- Config:
-
appendonly(AOF): Enabling AOF persistence withappendfsync alwaysorappendfsync everysecprovides an alternative to RDB. If you can tolerate slightly less durable data (witheverysec), AOF can often be more performant during writes andBGSAVEindirectly. Redis can performBGSAVEfor AOF rewriting (BGREWRITEAOF) which also usesfork. TuningBGREWRITEAOFfrequency is similar to tuningBGSAVEfrequency.- Config:
appendonly yes appendfsync everysec - Why it works:
appendfsync everysecoffers a good balance between durability and performance. If AOF is enabled,BGSAVEis only needed to compact the AOF file viaBGREWRITEAOF, which also usesfork.
- Config:
-
System-level tuning (
vm.overcommit_memory): This is a critical OS parameter. Ifvm.overcommit_memoryis set to 0 (the default), the kernel checks if there’s enough memory available before allowingfork. If not,forkcan fail. Settingvm.overcommit_memory = 1tells the kernel to always allow memory overcommit, which can makeforkmore 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
forkfrom failing due to perceived lack of memory by the kernel, allowing theBGSAVEchild process to be created more reliably.
- Config (as root):
-
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 offork.- 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
forkoperation itself.
- Config (as root):
-
Hardware: Faster CPUs and, more importantly, faster RAM and I/O subsystems (SSDs vs. HDDs) will reduce the time taken by the
forkoperation 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.
- Check:
-
Running
BGSAVEmanually 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 thefork.- 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.
- Command:
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.